summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp41
-rw-r--r--Android.bp3
-rw-r--r--android-sdk-flags/OWNERS1
-rw-r--r--android-sdk-flags/flags.aconfig1
-rw-r--r--apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java4
-rw-r--r--apex/jobscheduler/framework/aconfig/job.aconfig16
-rw-r--r--apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java22
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl3
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java56
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl19
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java100
-rw-r--r--apex/jobscheduler/service/aconfig/device_idle.aconfig10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java32
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java231
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java190
-rw-r--r--api/Android.bp64
-rw-r--r--api/ApiDocs.bp2
-rw-r--r--api/api.go5
-rw-r--r--cmds/bootanimation/BootAnimation.cpp2
-rw-r--r--cmds/idmap2/libidmap2/ResourceContainer.cpp16
-rw-r--r--config/preloaded-classes-denylist11
-rw-r--r--core/api/current.txt435
-rw-r--r--core/api/lint-baseline.txt30
-rw-r--r--core/api/system-current.txt107
-rw-r--r--core/api/system-lint-baseline.txt46
-rw-r--r--core/api/test-current.txt15
-rw-r--r--core/api/test-lint-baseline.txt6
-rw-r--r--core/java/Android.bp38
-rw-r--r--core/java/android/adaptiveauth/OWNERS2
-rw-r--r--core/java/android/app/Activity.java80
-rw-r--r--core/java/android/app/ActivityManager.java24
-rw-r--r--core/java/android/app/ActivityTaskManager.java5
-rw-r--r--core/java/android/app/ActivityThread.java57
-rw-r--r--core/java/android/app/AppCompatCallbacks.java1
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java12
-rw-r--r--core/java/android/app/AppOpsManager.java511
-rw-r--r--core/java/android/app/ContextImpl.java1
-rw-r--r--core/java/android/app/DisabledWallpaperManager.java12
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java1
-rw-r--r--core/java/android/app/INotificationManager.aidl5
-rw-r--r--core/java/android/app/IWallpaperManager.aidl10
-rw-r--r--core/java/android/app/Notification.java180
-rw-r--r--core/java/android/app/NotificationManager.java14
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java535
-rw-r--r--core/java/android/app/ResourcesManager.java120
-rw-r--r--core/java/android/app/StatusBarManager.java24
-rw-r--r--core/java/android/app/TaskInfo.java32
-rw-r--r--core/java/android/app/WallpaperManager.java99
-rw-r--r--core/java/android/app/ZenBypassingApp.java98
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java6
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java158
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl5
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig54
-rw-r--r--core/java/android/app/appfunctions/AppFunctionException.aidl21
-rw-r--r--core/java/android/app/appfunctions/AppFunctionException.java261
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java38
-rw-r--r--core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java4
-rw-r--r--core/java/android/app/appfunctions/AppFunctionService.java44
-rw-r--r--core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java2
-rw-r--r--core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java4
-rw-r--r--core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java280
-rw-r--r--core/java/android/app/appfunctions/GenericDocumentWrapper.java11
-rw-r--r--core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl4
-rw-r--r--core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java42
-rw-r--r--core/java/android/app/assist/AssistContent.java15
-rw-r--r--core/java/android/app/compat/ChangeIdStateCache.java13
-rw-r--r--core/java/android/app/compat/ChangeIdStateQuery.java1
-rw-r--r--core/java/android/app/compat/CompatChanges.java1
-rw-r--r--core/java/android/app/compat/PackageOverride.java1
-rw-r--r--core/java/android/app/jank/AppJankStats.java253
-rw-r--r--core/java/android/app/jank/FrameOverrunHistogram.java107
-rw-r--r--core/java/android/app/jank/JankDataProcessor.java8
-rw-r--r--core/java/android/app/jank/JankTracker.java227
-rw-r--r--core/java/android/app/multitasking.aconfig8
-rw-r--r--core/java/android/app/notification.aconfig38
-rw-r--r--core/java/android/app/performance.aconfig17
-rw-r--r--core/java/android/app/supervision/flags.aconfig10
-rw-r--r--core/java/android/app/wallpaper.aconfig18
-rw-r--r--core/java/android/app/wallpaper/WallpaperDescription.java47
-rw-r--r--core/java/android/appwidget/flags.aconfig2
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java132
-rw-r--r--core/java/android/companion/virtual/flags.aconfig7
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig7
-rw-r--r--core/java/android/content/ClipData.java1
-rw-r--r--core/java/android/content/Context.java6
-rw-r--r--core/java/android/content/Intent.java294
-rw-r--r--core/java/android/content/pm/IOnAppsChangedListener.aidl2
-rw-r--r--core/java/android/content/pm/LauncherApps.java46
-rw-r--r--core/java/android/content/pm/LauncherUserInfo.java47
-rw-r--r--core/java/android/content/pm/PackageInstaller.java38
-rw-r--r--core/java/android/content/pm/ServiceInfo.java2
-rw-r--r--core/java/android/content/pm/flags.aconfig17
-rw-r--r--core/java/android/content/pm/multiuser.aconfig18
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java66
-rw-r--r--core/java/android/content/res/ApkAssets.java33
-rw-r--r--core/java/android/content/res/ResourcesImpl.java22
-rw-r--r--core/java/android/content/res/TEST_MAPPING3
-rw-r--r--core/java/android/content/res/flags.aconfig22
-rw-r--r--core/java/android/credentials/flags.aconfig10
-rw-r--r--core/java/android/database/BulkCursorNative.java2
-rw-r--r--core/java/android/database/CursorWindow.java2
-rw-r--r--core/java/android/database/CursorWindow_ravenwood.java (renamed from ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java)20
-rw-r--r--core/java/android/database/sqlite/flags.aconfig3
-rw-r--r--core/java/android/hardware/DataSpace.java35
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java162
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java2
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java101
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java95
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java127
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java155
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java143
-rw-r--r--core/java/android/hardware/devicestate/DeviceState.java20
-rw-r--r--core/java/android/hardware/devicestate/feature/flags.aconfig9
-rw-r--r--core/java/android/hardware/display/BrightnessInfo.java15
-rw-r--r--core/java/android/hardware/display/DisplayManager.java83
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java191
-rw-r--r--core/java/android/hardware/display/DisplayTopology.aidl19
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java (renamed from services/core/java/com/android/server/display/DisplayTopology.java)229
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl10
-rw-r--r--core/java/android/hardware/display/IVirtualDisplayCallback.aidl5
-rw-r--r--core/java/android/hardware/display/VirtualDisplay.java22
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java63
-rw-r--r--core/java/android/hardware/input/AidlInputGestureData.aidl21
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl10
-rw-r--r--core/java/android/hardware/input/InputGestureData.java133
-rw-r--r--core/java/android/hardware/input/InputManager.java56
-rw-r--r--core/java/android/hardware/input/InputSettings.java56
-rw-r--r--core/java/android/hardware/input/KeyGestureEvent.java16
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig35
-rw-r--r--core/java/android/hardware/lights/Light.java7
-rw-r--r--core/java/android/hardware/location/ContextHubInfo.java1
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java38
-rw-r--r--core/java/android/hardware/location/HubInfo.aidl20
-rw-r--r--core/java/android/hardware/location/HubInfo.java153
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl5
-rw-r--r--core/java/android/hardware/location/OWNERS2
-rw-r--r--core/java/android/hardware/location/VendorHubInfo.aidl20
-rw-r--r--core/java/android/hardware/location/VendorHubInfo.java95
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java27
-rw-r--r--core/java/android/hardware/usb/OWNERS6
-rw-r--r--core/java/android/hardware/usb/UsbManager.java269
-rw-r--r--core/java/android/hardware/usb/flags/usb_framework_flags.aconfig8
-rw-r--r--core/java/android/net/vcn/flags.aconfig7
-rw-r--r--core/java/android/os/AggregateBatteryConsumer.java14
-rw-r--r--core/java/android/os/BatteryConsumer.java30
-rw-r--r--core/java/android/os/BatteryUsageStats.java4
-rw-r--r--core/java/android/os/Build.java61
-rw-r--r--core/java/android/os/Bundle.java2
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java87
-rw-r--r--core/java/android/os/ConcurrentMessageQueue/MessageQueue.java2
-rw-r--r--core/java/android/os/IThermalHeadroomListener.aidl31
-rw-r--r--core/java/android/os/IThermalService.aidl17
-rw-r--r--core/java/android/os/IVibratorManagerService.aidl9
-rw-r--r--core/java/android/os/IpcDataCache.java6
-rw-r--r--core/java/android/os/LegacyMessageQueue/MessageQueue.java2
-rw-r--r--core/java/android/os/LockedMessageQueue/MessageQueue.java2
-rw-r--r--core/java/android/os/MessageQueue_ravenwood.java (renamed from ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java)13
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java8
-rw-r--r--core/java/android/os/PowerComponents.java12
-rw-r--r--core/java/android/os/PowerManager.java238
-rw-r--r--core/java/android/os/Process.java124
-rw-r--r--core/java/android/os/RemoteCallbackList.java20
-rw-r--r--core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java2
-rw-r--r--core/java/android/os/StrictMode.java136
-rw-r--r--core/java/android/os/SystemVibrator.java70
-rw-r--r--core/java/android/os/SystemVibratorManager.java103
-rw-r--r--core/java/android/os/UidBatteryConsumer.java17
-rw-r--r--core/java/android/os/Vibrator.java69
-rw-r--r--core/java/android/os/VibratorManager.java37
-rw-r--r--core/java/android/os/WorkSource.java8
-rw-r--r--core/java/android/os/flags.aconfig9
-rw-r--r--core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl37
-rw-r--r--core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl33
-rw-r--r--core/java/android/os/instrumentation/MethodDescriptor.aidl37
-rw-r--r--core/java/android/os/instrumentation/TargetProcess.aidl29
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl1
-rw-r--r--core/java/android/os/vibrator/IVibrationSession.aidl55
-rw-r--r--core/java/android/os/vibrator/IVibrationSessionCallback.aidl43
-rw-r--r--core/java/android/os/vibrator/VendorVibrationSession.java236
-rw-r--r--core/java/android/permission/flags.aconfig97
-rw-r--r--core/java/android/print/OWNERS1
-rw-r--r--core/java/android/printservice/OWNERS1
-rw-r--r--core/java/android/provider/Settings.java76
-rw-r--r--core/java/android/provider/flags.aconfig8
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionManager.java180
-rw-r--r--core/java/android/security/flags.aconfig9
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig1
-rw-r--r--core/java/android/service/autofill/FillRequest.java12
-rw-r--r--core/java/android/service/dreams/flags.aconfig10
-rw-r--r--core/java/android/service/games/GameSession.java2
-rw-r--r--core/java/android/service/notification/flags.aconfig9
-rw-r--r--core/java/android/service/quickaccesswallet/flags.aconfig9
-rw-r--r--core/java/android/service/settings/preferences/GetValueRequest.aidl4
-rw-r--r--core/java/android/service/settings/preferences/GetValueRequest.java139
-rw-r--r--core/java/android/service/settings/preferences/GetValueResult.aidl4
-rw-r--r--core/java/android/service/settings/preferences/GetValueResult.java213
-rw-r--r--core/java/android/service/settings/preferences/IGetValueCallback.aidl9
-rw-r--r--core/java/android/service/settings/preferences/IMetadataCallback.aidl9
-rw-r--r--core/java/android/service/settings/preferences/ISetValueCallback.aidl9
-rw-r--r--core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl18
-rw-r--r--core/java/android/service/settings/preferences/MetadataRequest.aidl4
-rw-r--r--core/java/android/service/settings/preferences/MetadataRequest.java75
-rw-r--r--core/java/android/service/settings/preferences/MetadataResult.aidl4
-rw-r--r--core/java/android/service/settings/preferences/MetadataResult.java164
-rw-r--r--core/java/android/service/settings/preferences/SetValueRequest.aidl4
-rw-r--r--core/java/android/service/settings/preferences/SetValueRequest.java158
-rw-r--r--core/java/android/service/settings/preferences/SetValueResult.aidl4
-rw-r--r--core/java/android/service/settings/preferences/SetValueResult.java179
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java436
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceService.java201
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java248
-rw-r--r--core/java/android/service/settings/preferences/SettingsPreferenceValue.java220
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java6
-rw-r--r--core/java/android/service/wallpaper/IWallpaperService.aidl2
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java3
-rw-r--r--core/java/android/telephony/PhoneStateListener.java6
-rw-r--r--core/java/android/telephony/TelephonyCallback.java34
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java19
-rw-r--r--core/java/android/text/flags/flags.aconfig11
-rw-r--r--core/java/android/util/Log.java6
-rw-r--r--core/java/android/view/Display.java12
-rw-r--r--core/java/android/view/DisplayInfo.java4
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java4
-rw-r--r--core/java/android/view/HapticScrollFeedbackProvider.java28
-rw-r--r--core/java/android/view/ImeBackAnimationController.java11
-rw-r--r--core/java/android/view/ImeFocusController.java12
-rw-r--r--core/java/android/view/InsetsController.java16
-rw-r--r--core/java/android/view/InsetsSourceControl.java3
-rw-r--r--core/java/android/view/KeyEvent.java89
-rw-r--r--core/java/android/view/RoundScrollbarRenderer.java4
-rw-r--r--core/java/android/view/SurfaceControl.java5
-rw-r--r--core/java/android/view/View.java171
-rw-r--r--core/java/android/view/ViewRootImpl.java23
-rw-r--r--core/java/android/view/WindowManager.java25
-rw-r--r--core/java/android/view/WindowMetrics.java4
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java9
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java95
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig14
-rw-r--r--core/java/android/view/autofill/AutofillFeatureFlags.java84
-rw-r--r--core/java/android/view/autofill/AutofillManager.java17
-rw-r--r--core/java/android/view/flags/scroll_feedback_flags.aconfig7
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java6
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java13
-rw-r--r--core/java/android/view/inputmethod/InputMethodSubtype.java125
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig8
-rw-r--r--core/java/android/webkit/WebChromeClient.java19
-rw-r--r--core/java/android/widget/TextView.java38
-rw-r--r--core/java/android/window/BackProgressAnimator.java30
-rw-r--r--core/java/android/window/DesktopModeFlags.java4
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java28
-rw-r--r--core/java/android/window/OnBackInvokedCallbackInfo.java18
-rw-r--r--core/java/android/window/SystemOnBackInvokedCallbacks.java168
-rw-r--r--core/java/android/window/SystemOverrideOnBackInvokedCallback.java61
-rw-r--r--core/java/android/window/TransitionFilter.java13
-rw-r--r--core/java/android/window/TransitionInfo.java9
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java27
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig20
-rw-r--r--core/java/android/window/flags/responsible_apis.aconfig15
-rw-r--r--core/java/android/window/flags/window_surfaces.aconfig8
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig78
-rw-r--r--core/java/com/android/internal/accessibility/common/ShortcutConstants.java10
-rw-r--r--core/java/com/android/internal/accessibility/util/ShortcutUtils.java3
-rw-r--r--core/java/com/android/internal/app/AppLocaleCollector.java6
-rw-r--r--core/java/com/android/internal/app/LocaleCollectorBase.java36
-rw-r--r--core/java/com/android/internal/app/LocalePickerWithRegion.java18
-rw-r--r--core/java/com/android/internal/app/SystemLocaleCollector.java8
-rw-r--r--core/java/com/android/internal/compat/AndroidBuildClassifier.java1
-rw-r--r--core/java/com/android/internal/compat/ChangeReporter.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeInfo.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverrideConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java1
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java1
-rw-r--r--core/java/com/android/internal/compat/OverrideAllowedState.java1
-rw-r--r--core/java/com/android/internal/display/BrightnessSynchronizer.java4
-rw-r--r--core/java/com/android/internal/jank/DisplayResolutionTracker.java5
-rw-r--r--core/java/com/android/internal/os/KernelSingleUidTimeReader.java21
-rw-r--r--core/java/com/android/internal/os/LongArrayMultiStateCounter.java235
-rw-r--r--core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java (renamed from ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java)30
-rw-r--r--core/java/com/android/internal/os/LongMultiStateCounter.java2
-rw-r--r--core/java/com/android/internal/os/LongMultiStateCounter_ravenwood.java (renamed from ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java)4
-rw-r--r--core/java/com/android/internal/pm/pkg/component/AconfigFlags.java23
-rw-r--r--core/java/com/android/internal/policy/DecorView.java39
-rw-r--r--core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java67
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIcon.java3
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl2
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl3
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressDrawable.java49
-rw-r--r--core/java/com/android/internal/widget/NotificationRowIconView.java60
-rw-r--r--core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java228
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operation.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/PaintContext.java20
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Platform.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java209
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java44
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java121
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/TouchListener.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java26
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java26
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Header.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java43
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java599
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java80
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickHandler.java35
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java35
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java124
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java91
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java109
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java (renamed from core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java)30
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java40
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java91
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java93
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java59
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java91
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java28
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java46
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java28
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java53
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java51
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java36
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java302
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java62
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java119
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java97
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java105
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java555
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java371
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java128
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java341
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java74
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java53
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java66
-rw-r--r--core/jni/Android.bp6
-rw-r--r--core/jni/android_hardware_Camera.cpp2
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp165
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.h21
-rw-r--r--core/jni/android_view_SurfaceControl.cpp18
-rw-r--r--core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp56
-rw-r--r--core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp261
-rw-r--r--core/jni/com_android_internal_os_LongMultiStateCounter.cpp8
-rw-r--r--core/jni/platform/host/HostRuntime.cpp2
-rw-r--r--core/proto/android/providers/settings/secure.proto1
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/proto/android/service/appwidget.proto13
-rw-r--r--core/res/Android.bp4
-rw-r--r--core/res/AndroidManifest.xml84
-rw-r--r--core/res/res/drawable-watch/ic_lock_bugreport.xml17
-rw-r--r--core/res/res/drawable-watch/ic_lock_power_off.xml13
-rw-r--r--core/res/res/drawable-watch/ic_restart.xml13
-rw-r--r--core/res/res/drawable-watch/ic_settings.xml19
-rw-r--r--core/res/res/drawable/ic_zen_mode_icon_star_badge.xml (renamed from core/res/res/drawable/ic_zen_mode_type_unknown.xml)0
-rw-r--r--core/res/res/layout/input_method_switch_item_new.xml23
-rw-r--r--core/res/res/values-af/strings.xml62
-rw-r--r--core/res/res/values-am/strings.xml62
-rw-r--r--core/res/res/values-ar/strings.xml44
-rw-r--r--core/res/res/values-as/strings.xml44
-rw-r--r--core/res/res/values-az/strings.xml62
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml44
-rw-r--r--core/res/res/values-be/strings.xml44
-rw-r--r--core/res/res/values-bg/strings.xml62
-rw-r--r--core/res/res/values-bn/strings.xml44
-rw-r--r--core/res/res/values-bs/strings.xml74
-rw-r--r--core/res/res/values-ca/strings.xml64
-rw-r--r--core/res/res/values-cs/strings.xml62
-rw-r--r--core/res/res/values-da/strings.xml66
-rw-r--r--core/res/res/values-de-feminine/strings.xml24
-rw-r--r--core/res/res/values-de-masculine/strings.xml24
-rw-r--r--core/res/res/values-de-neuter/strings.xml24
-rw-r--r--core/res/res/values-de/strings.xml62
-rw-r--r--core/res/res/values-el/strings.xml62
-rw-r--r--core/res/res/values-en-rAU/strings.xml46
-rw-r--r--core/res/res/values-en-rCA/strings.xml27
-rw-r--r--core/res/res/values-en-rGB/strings.xml46
-rw-r--r--core/res/res/values-en-rIN/strings.xml46
-rw-r--r--core/res/res/values-es-rUS-feminine/strings.xml24
-rw-r--r--core/res/res/values-es-rUS-masculine/strings.xml24
-rw-r--r--core/res/res/values-es-rUS-neuter/strings.xml24
-rw-r--r--core/res/res/values-es-rUS/strings.xml48
-rw-r--r--core/res/res/values-es/strings.xml62
-rw-r--r--core/res/res/values-et/strings.xml62
-rw-r--r--core/res/res/values-eu/strings.xml64
-rw-r--r--core/res/res/values-fa/strings.xml64
-rw-r--r--core/res/res/values-fi/strings.xml62
-rw-r--r--core/res/res/values-fr-rCA/strings.xml44
-rw-r--r--core/res/res/values-fr/strings.xml62
-rw-r--r--core/res/res/values-gl/strings.xml66
-rw-r--r--core/res/res/values-gu/strings.xml66
-rw-r--r--core/res/res/values-hi/strings.xml48
-rw-r--r--core/res/res/values-hr/strings.xml44
-rw-r--r--core/res/res/values-hu/strings.xml62
-rw-r--r--core/res/res/values-hy/strings.xml66
-rw-r--r--core/res/res/values-in/strings.xml62
-rw-r--r--core/res/res/values-is/strings.xml46
-rw-r--r--core/res/res/values-it/strings.xml55
-rw-r--r--core/res/res/values-iw/strings.xml44
-rw-r--r--core/res/res/values-ja/strings.xml44
-rw-r--r--core/res/res/values-ka/strings.xml44
-rw-r--r--core/res/res/values-kk/strings.xml62
-rw-r--r--core/res/res/values-km/strings.xml44
-rw-r--r--core/res/res/values-kn/strings.xml44
-rw-r--r--core/res/res/values-ko/strings.xml44
-rw-r--r--core/res/res/values-ky/strings.xml62
-rw-r--r--core/res/res/values-lo/strings.xml44
-rw-r--r--core/res/res/values-lt/strings.xml44
-rw-r--r--core/res/res/values-lv/strings.xml62
-rw-r--r--core/res/res/values-mk/strings.xml62
-rw-r--r--core/res/res/values-ml/strings.xml46
-rw-r--r--core/res/res/values-mn/strings.xml62
-rw-r--r--core/res/res/values-mr/strings.xml44
-rw-r--r--core/res/res/values-ms/strings.xml44
-rw-r--r--core/res/res/values-my/strings.xml44
-rw-r--r--core/res/res/values-nb/strings.xml62
-rw-r--r--core/res/res/values-ne/strings.xml48
-rw-r--r--core/res/res/values-nl/strings.xml44
-rw-r--r--core/res/res/values-or/strings.xml66
-rw-r--r--core/res/res/values-pa/strings.xml44
-rw-r--r--core/res/res/values-pl/strings.xml46
-rw-r--r--core/res/res/values-pt-rBR/strings.xml46
-rw-r--r--core/res/res/values-pt-rPT/strings.xml29
-rw-r--r--core/res/res/values-pt/strings.xml46
-rw-r--r--core/res/res/values-ro/strings.xml46
-rw-r--r--core/res/res/values-ru/strings.xml44
-rw-r--r--core/res/res/values-si/strings.xml62
-rw-r--r--core/res/res/values-sk/strings.xml62
-rw-r--r--core/res/res/values-sl/strings.xml44
-rw-r--r--core/res/res/values-sq/strings.xml64
-rw-r--r--core/res/res/values-sr/strings.xml44
-rw-r--r--core/res/res/values-sv/strings.xml44
-rw-r--r--core/res/res/values-sw/strings.xml64
-rw-r--r--core/res/res/values-ta/strings.xml64
-rw-r--r--core/res/res/values-te/strings.xml62
-rw-r--r--core/res/res/values-th/strings.xml44
-rw-r--r--core/res/res/values-tl/strings.xml46
-rw-r--r--core/res/res/values-tr/strings.xml62
-rw-r--r--core/res/res/values-uk/strings.xml64
-rw-r--r--core/res/res/values-ur/strings.xml44
-rw-r--r--core/res/res/values-uz/strings.xml44
-rw-r--r--core/res/res/values-vi/strings.xml62
-rw-r--r--core/res/res/values-zh-rCN/strings.xml44
-rw-r--r--core/res/res/values-zh-rHK/strings.xml62
-rw-r--r--core/res/res/values-zh-rTW/strings.xml72
-rw-r--r--core/res/res/values-zu/strings.xml62
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/attrs_manifest.xml16
-rw-r--r--core/res/res/values/config.xml24
-rw-r--r--core/res/res/values/config_telephony.xml5
-rw-r--r--core/res/res/values/public-staging.xml6
-rw-r--r--core/res/res/values/stoppable_fgs_system_apps.xml27
-rw-r--r--core/res/res/values/strings.xml53
-rw-r--r--core/res/res/values/symbols.xml30
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/AndroidTest.xml2
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java170
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java35
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java3
-rw-r--r--core/tests/coretests/src/android/content/IntentTest.java43
-rw-r--r--core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java52
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java35
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt472
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java81
-rw-r--r--core/tests/coretests/src/android/view/DisplayInfoTest.java17
-rw-r--r--core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java30
-rw-r--r--core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java6
-rw-r--r--core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java43
-rw-r--r--core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java9
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java93
-rw-r--r--core/tests/vibrator/src/android/os/VibratorTest.java20
-rw-r--r--data/etc/platform.xml12
-rw-r--r--data/etc/privapp-permissions-platform.xml6
-rw-r--r--data/keyboards/Generic.kl42
-rw-r--r--data/sounds/Android.bp304
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java2
-rw-r--r--graphics/java/android/graphics/ColorSpace.java2
-rw-r--r--graphics/java/android/graphics/ImageFormat.java18
-rw-r--r--graphics/java/android/graphics/Paint.java24
-rw-r--r--graphics/java/android/graphics/text/PositionedGlyphs.java4
-rw-r--r--keystore/java/android/security/keystore/KeyStoreManager.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java39
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java61
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml1
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt44
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt98
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt27
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_change_aspect_ratio.xml25
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml1
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml14
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml14
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml14
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml8
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.aidl (renamed from libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl)2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java (renamed from libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java)168
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java8
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt20
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt5
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt33
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/OWNERS4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java71
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java128
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt97
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt95
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java124
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java64
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java64
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java269
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java64
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java166
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java166
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt162
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java112
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt5
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt56
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt52
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.kt51
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAppWindowsTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt4
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt82
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java191
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt47
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt173
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt171
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt146
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt102
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt28
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt129
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt126
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java74
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java178
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java201
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java270
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java331
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt)46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java189
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt128
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java211
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java28
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt99
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt8
-rw-r--r--libs/appfunctions/Android.bp8
-rw-r--r--libs/appfunctions/api/current.txt54
-rw-r--r--libs/appfunctions/appfunctions.extension.xml (renamed from libs/appfunctions/appfunctions.sidecar.xml)4
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java212
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java (renamed from libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java)25
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java (renamed from libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java)31
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java (renamed from libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java)6
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java103
-rw-r--r--libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java (renamed from libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java)74
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java359
-rw-r--r--libs/appfunctions/tests/Android.bp2
-rw-r--r--libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt (renamed from libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt)78
-rw-r--r--libs/hwui/FeatureFlags.h4
-rw-r--r--libs/hwui/Properties.cpp9
-rw-r--r--libs/hwui/Properties.h2
-rw-r--r--libs/hwui/aconfig/hwui_flags.aconfig10
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp1
-rw-r--r--libs/hwui/hwui/MinikinUtils.h2
-rw-r--r--libs/hwui/hwui/Paint.h2
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp11
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp71
-rw-r--r--libs/hwui/jni/Graphics.cpp2
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp6
-rw-r--r--media/java/android/media/AudioAttributes.java32
-rw-r--r--media/java/android/media/ImageReader.java12
-rw-r--r--media/java/android/media/ImageUtils.java17
-rw-r--r--media/java/android/media/MediaCas.java9
-rw-r--r--media/java/android/media/MediaCodec.java54
-rw-r--r--media/java/android/media/MediaFormat.java12
-rw-r--r--media/java/android/media/MediaRoute2Info.java31
-rw-r--r--media/java/android/media/MediaRouter2.java22
-rw-r--r--media/java/android/media/RoutingSessionInfo.java52
-rw-r--r--media/java/android/media/audio/common/AidlConversion.java4
-rw-r--r--media/java/android/media/flags/editing.aconfig7
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig17
-rw-r--r--media/java/android/media/quality/AmbientBacklightEvent.aidl19
-rw-r--r--media/java/android/media/quality/AmbientBacklightEvent.java153
-rw-r--r--media/java/android/media/quality/AmbientBacklightMetadata.aidl19
-rw-r--r--media/java/android/media/quality/AmbientBacklightMetadata.java163
-rw-r--r--media/java/android/media/quality/AmbientBacklightSettings.aidl19
-rw-r--r--media/java/android/media/quality/AmbientBacklightSettings.java238
-rw-r--r--media/java/android/media/quality/IAmbientBacklightCallback.aidl24
-rw-r--r--media/java/android/media/quality/IMediaQualityManager.aidl41
-rw-r--r--media/java/android/media/quality/IPictureProfileCallback.aidl8
-rw-r--r--media/java/android/media/quality/ISoundProfileCallback.aidl30
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java43
-rw-r--r--media/java/android/media/quality/MediaQualityManager.java679
-rw-r--r--media/java/android/media/quality/ParamCapability.aidl19
-rw-r--r--media/java/android/media/quality/ParamCapability.java189
-rw-r--r--media/java/android/media/quality/PictureProfile.java218
-rw-r--r--media/java/android/media/quality/PictureProfileHandle.aidl19
-rw-r--r--media/java/android/media/quality/PictureProfileHandle.java75
-rw-r--r--media/java/android/media/quality/SoundProfile.java26
-rw-r--r--media/java/android/media/tv/TvInputService.java22
-rw-r--r--media/java/android/media/tv/TvInputServiceExtensionManager.java820
-rw-r--r--media/java/android/mtp/OWNERS7
-rw-r--r--media/jni/android_media_MediaCodec.cpp15
-rw-r--r--media/tests/MtpTests/OWNERS7
-rw-r--r--media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java22
-rw-r--r--native/android/Android.bp2
-rw-r--r--native/android/dynamic_instrumentation_manager.cpp173
-rw-r--r--native/android/include_platform/android/dynamic_instrumentation_manager.h132
-rw-r--r--native/android/libandroid.map.txt9
-rw-r--r--native/android/tests/thermal/NativeThermalUnitTest.cpp8
-rw-r--r--nfc/api/current.txt9
-rw-r--r--nfc/api/system-current.txt61
-rw-r--r--nfc/java/android/nfc/ComponentNameAndUser.aidl19
-rw-r--r--nfc/java/android/nfc/ComponentNameAndUser.java100
-rw-r--r--nfc/java/android/nfc/Entry.aidl18
-rw-r--r--nfc/java/android/nfc/Entry.java77
-rw-r--r--nfc/java/android/nfc/INfcAdapter.aidl3
-rw-r--r--nfc/java/android/nfc/INfcCardEmulation.aidl5
-rw-r--r--nfc/java/android/nfc/INfcEventListener.aidl11
-rw-r--r--nfc/java/android/nfc/INfcOemExtensionCallback.aidl6
-rw-r--r--nfc/java/android/nfc/NfcAdapter.java19
-rw-r--r--nfc/java/android/nfc/NfcOemExtension.java87
-rw-r--r--nfc/java/android/nfc/NfcRoutingTableEntry.java46
-rw-r--r--nfc/java/android/nfc/OemLogItems.aidl19
-rw-r--r--nfc/java/android/nfc/OemLogItems.java325
-rw-r--r--nfc/java/android/nfc/RoutingTableAidEntry.java46
-rw-r--r--nfc/java/android/nfc/RoutingTableProtocolEntry.java129
-rw-r--r--nfc/java/android/nfc/RoutingTableSystemCodeEntry.java46
-rw-r--r--nfc/java/android/nfc/RoutingTableTechnologyEntry.java101
-rw-r--r--nfc/java/android/nfc/cardemulation/ApduServiceInfo.java8
-rw-r--r--nfc/java/android/nfc/cardemulation/CardEmulation.java109
-rw-r--r--nfc/java/android/nfc/cardemulation/HostApduService.java40
-rw-r--r--packages/CarrierDefaultApp/res/values-bs/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml2
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java3
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java4
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java52
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java4
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java6
-rw-r--r--packages/SettingsLib/Android.bp16
-rw-r--r--packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java15
-rw-r--r--packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java15
-rw-r--r--packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java2
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java3
-rw-r--r--packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt6
-rw-r--r--packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java2
-rw-r--r--packages/SettingsLib/Graph/graph.proto21
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt1
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt44
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt40
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt12
-rw-r--r--packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml2
-rw-r--r--packages/SettingsLib/IntroPreference/Android.bp1
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt27
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt5
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt10
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt9
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt10
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt2
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt68
-rw-r--r--packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt37
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/Android.bp2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml36
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml30
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml32
-rw-r--r--packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml35
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml13
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt2
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt59
-rw-r--r--packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt2
-rw-r--r--packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt2
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig25
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bs/arrays.xml8
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values/strings.xml12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt49
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt24
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java65
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPendingIntentAction.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt30
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt4
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java133
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java57
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt25
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java44
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/Shell/AndroidManifest.xml12
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java2
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/README.md2
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING3
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values-uk/strings.xml2
-rw-r--r--packages/SystemUI/aconfig/accessibility.aconfig7
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig109
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java3
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt63
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt233
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt39
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt22
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt28
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt14
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt47
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt14
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt30
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt23
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt225
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt58
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt187
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt43
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt71
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt84
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt53
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt18
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt3
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt20
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt35
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt59
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt63
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt7
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt152
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt59
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt18
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt200
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt56
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt7
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt14
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt2
-rw-r--r--packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml19
-rw-r--r--packages/SystemUI/customization/res/values-sw600dp/dimens.xml20
-rw-r--r--packages/SystemUI/customization/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt6
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt20
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt126
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt12
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt94
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepositoryImpl.kt85
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt94
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryImpl.kt85
-rw-r--r--packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt4
-rw-r--r--packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt4
-rw-r--r--packages/SystemUI/docs/demo_mode.md1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java152
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt225
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt124
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java548
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt555
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt205
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt75
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt102
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt143
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt145
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt92
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpManagerTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt291
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt)6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt64
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt51
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt224
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt332
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt31
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt222
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt61
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt182
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt142
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt219
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt52
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt368
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt81
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt123
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt143
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt38
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt137
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt76
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreImplTest.kt54
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationControllerTest.kt108
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerTest.kt185
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt66
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java25
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt76
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt91
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt25
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt69
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt648
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt80
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java845
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt867
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt101
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt72
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt143
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt91
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java111
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt126
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt88
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepositoryTestBase.kt163
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepositoryTest.kt31
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt86
-rw-r--r--packages/SystemUI/res-keyguard/values-or/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_notes.xml23
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml32
-rw-r--r--packages/SystemUI/res/layout/screen_record_dialog.xml164
-rw-r--r--packages/SystemUI/res/layout/split_clock_view.xml54
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml86
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_drawer.xml2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml55
-rw-r--r--packages/SystemUI/res/values-af/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-am/strings.xml55
-rw-r--r--packages/SystemUI/res/values-am/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml60
-rw-r--r--packages/SystemUI/res/values-ar/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-as/strings.xml51
-rw-r--r--packages/SystemUI/res/values-as/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-az/strings.xml59
-rw-r--r--packages/SystemUI/res/values-az/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml52
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-be/strings.xml54
-rw-r--r--packages/SystemUI/res/values-be/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml60
-rw-r--r--packages/SystemUI/res/values-bg/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml51
-rw-r--r--packages/SystemUI/res/values-bn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml56
-rw-r--r--packages/SystemUI/res/values-bs/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml67
-rw-r--r--packages/SystemUI/res/values-ca/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml63
-rw-r--r--packages/SystemUI/res/values-cs/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-da/strings.xml55
-rw-r--r--packages/SystemUI/res/values-da/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-de/strings.xml69
-rw-r--r--packages/SystemUI/res/values-de/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-el/strings.xml54
-rw-r--r--packages/SystemUI/res/values-el/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml54
-rw-r--r--packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml29
-rw-r--r--packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml54
-rw-r--r--packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml54
-rw-r--r--packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml52
-rw-r--r--packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-es/strings.xml61
-rw-r--r--packages/SystemUI/res/values-es/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-et/strings.xml65
-rw-r--r--packages/SystemUI/res/values-et/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml59
-rw-r--r--packages/SystemUI/res/values-eu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml52
-rw-r--r--packages/SystemUI/res/values-fa/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml59
-rw-r--r--packages/SystemUI/res/values-fi/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml60
-rw-r--r--packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml65
-rw-r--r--packages/SystemUI/res/values-fr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml65
-rw-r--r--packages/SystemUI/res/values-gl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml48
-rw-r--r--packages/SystemUI/res/values-gu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml63
-rw-r--r--packages/SystemUI/res/values-hi/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml54
-rw-r--r--packages/SystemUI/res/values-hr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml57
-rw-r--r--packages/SystemUI/res/values-hu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml64
-rw-r--r--packages/SystemUI/res/values-hy/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-in/strings.xml61
-rw-r--r--packages/SystemUI/res/values-in/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-is/strings.xml52
-rw-r--r--packages/SystemUI/res/values-is/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-it/strings.xml40
-rw-r--r--packages/SystemUI/res/values-it/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml57
-rw-r--r--packages/SystemUI/res/values-iw/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml50
-rw-r--r--packages/SystemUI/res/values-ja/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml54
-rw-r--r--packages/SystemUI/res/values-ka/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml55
-rw-r--r--packages/SystemUI/res/values-kk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-km/strings.xml58
-rw-r--r--packages/SystemUI/res/values-km/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml50
-rw-r--r--packages/SystemUI/res/values-kn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml53
-rw-r--r--packages/SystemUI/res/values-ko/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml57
-rw-r--r--packages/SystemUI/res/values-ky/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml54
-rw-r--r--packages/SystemUI/res/values-lo/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml58
-rw-r--r--packages/SystemUI/res/values-lt/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml63
-rw-r--r--packages/SystemUI/res/values-lv/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml59
-rw-r--r--packages/SystemUI/res/values-mk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml51
-rw-r--r--packages/SystemUI/res/values-ml/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml51
-rw-r--r--packages/SystemUI/res/values-mn/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml48
-rw-r--r--packages/SystemUI/res/values-mr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml58
-rw-r--r--packages/SystemUI/res/values-ms/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-my/strings.xml63
-rw-r--r--packages/SystemUI/res/values-my/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml57
-rw-r--r--packages/SystemUI/res/values-nb/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml60
-rw-r--r--packages/SystemUI/res/values-ne/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml56
-rw-r--r--packages/SystemUI/res/values-nl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-or/strings.xml65
-rw-r--r--packages/SystemUI/res/values-or/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml51
-rw-r--r--packages/SystemUI/res/values-pa/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml56
-rw-r--r--packages/SystemUI/res/values-pl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml71
-rw-r--r--packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml41
-rw-r--r--packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml71
-rw-r--r--packages/SystemUI/res/values-pt/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml57
-rw-r--r--packages/SystemUI/res/values-ro/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml57
-rw-r--r--packages/SystemUI/res/values-ru/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-si/strings.xml55
-rw-r--r--packages/SystemUI/res/values-si/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml59
-rw-r--r--packages/SystemUI/res/values-sk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml62
-rw-r--r--packages/SystemUI/res/values-sl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml55
-rw-r--r--packages/SystemUI/res/values-sq/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml52
-rw-r--r--packages/SystemUI/res/values-sr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml57
-rw-r--r--packages/SystemUI/res/values-sv/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml55
-rw-r--r--packages/SystemUI/res/values-sw/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ta/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-te/strings.xml52
-rw-r--r--packages/SystemUI/res/values-te/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-th/strings.xml64
-rw-r--r--packages/SystemUI/res/values-th/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml58
-rw-r--r--packages/SystemUI/res/values-tl/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml61
-rw-r--r--packages/SystemUI/res/values-tr/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml65
-rw-r--r--packages/SystemUI/res/values-uk/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml65
-rw-r--r--packages/SystemUI/res/values-ur/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml60
-rw-r--r--packages/SystemUI/res/values-uz/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml53
-rw-r--r--packages/SystemUI/res/values-vi/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml58
-rw-r--r--packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml55
-rw-r--r--packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml55
-rw-r--r--packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml55
-rw-r--r--packages/SystemUI/res/values-zu/tiles_states_strings.xml3
-rw-r--r--packages/SystemUI/res/values/config.xml14
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml41
-rw-r--r--packages/SystemUI/res/values/styles.xml10
-rw-r--r--packages/SystemUI/res/values/tiles_states_strings.xml10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java28
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java42
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java41
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java284
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt (renamed from packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt (renamed from packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt (renamed from packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt)23
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt135
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl18
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt238
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxyExt.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt175
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt160
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt156
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt (renamed from packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt)147
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutInfo.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogDelegate.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt272
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt231
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt355
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/SessionTokenFactory.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt172
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt195
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt146
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt118
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt111
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java192
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/LegacyCaptureParameters.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/TaskReference.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserSettingsRepositoryModule.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt105
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt (renamed from packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt136
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java)187
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt314
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt186
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt110
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepository.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java6
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json)0
-rw-r--r--packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json (renamed from packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json)127
-rw-r--r--packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java1594
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt191
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt238
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt199
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java584
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt131
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java146
-rw-r--r--packages/SystemUI/tests/utils/src/android/view/FakeWindowManager.kt61
-rw-r--r--packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/OnTeardownRule.kt75
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt58
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt81
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeSessionTokenFactory.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/SessionTokenFactoryKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt61
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt41
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotViewControllerStore.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotWindowControllerStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSystemEventChipAnimationControllerStore.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStoreKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt50
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreKosmos.kt42
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/FakePrivacyDotViewController.kt63
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerKosmos.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerKosmos.kt (renamed from services/core/java/com/android/server/integrity/model/IndexingFileConstants.java)22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt)5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/viewmodel/StatusBarUserChipViewModelKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/ExecutionKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSecureSettingsRepositoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt)29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSystemSettingsRepositoryKosmos.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/FakeVolumeDialogRingerFeedbackRepository.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt3
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt2
-rw-r--r--packages/Vcn/framework-b/Android.bp44
-rw-r--r--packages/Vcn/framework-b/api/current.txt1
-rw-r--r--packages/Vcn/framework-b/api/module-lib-current.txt1
-rw-r--r--packages/Vcn/framework-b/api/module-lib-removed.txt1
-rw-r--r--packages/Vcn/framework-b/api/removed.txt1
-rw-r--r--packages/Vcn/framework-b/api/system-current.txt1
-rw-r--r--packages/Vcn/framework-b/api/system-removed.txt1
-rw-r--r--packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java25
-rw-r--r--packages/Vcn/service-b/Android.bp36
-rw-r--r--packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java25
-rw-r--r--packages/overlays/Android.bp1
-rw-r--r--ravenwood/Android.bp12
-rw-r--r--ravenwood/Framework.bp3
-rw-r--r--ravenwood/TEST_MAPPING4
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java20
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java103
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java12
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java161
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java33
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java16
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java90
-rw-r--r--ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java70
-rw-r--r--ravenwood/runtime-helper-src/framework/android/util/Log_host.java4
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java63
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java8
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java13
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java2
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeState.java35
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java5
-rw-r--r--ravenwood/runtime-jni/ravenwood_initializer.cpp50
-rw-r--r--ravenwood/runtime-jni/ravenwood_runtime.cpp24
-rwxr-xr-xravenwood/scripts/run-ravenwood-tests.sh88
-rw-r--r--ravenwood/tests/bivalenttest/Android.bp3
-rw-r--r--ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt56
-rw-r--r--ravenwood/tests/runtime-test/Android.bp3
-rw-r--r--ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java66
-rw-r--r--ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java33
-rw-r--r--ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java54
-rw-r--r--ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java45
-rw-r--r--ravenwood/texts/build.prop-sample-cuttlefish132
-rw-r--r--ravenwood/texts/ravenwood-annotation-allowed-classes.txt9
-rw-r--r--ravenwood/texts/ravenwood-build.prop44
-rw-r--r--ravenwood/texts/ravenwood-services-policies.txt11
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh3
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py1
-rw-r--r--services/Android.bp1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java60
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java138
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java77
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java15
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java64
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java26
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java502
-rw-r--r--services/art-profile2
-rw-r--r--services/art-wear-profile4
-rw-r--r--services/autofill/features.aconfig8
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java3
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java7
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java213
-rw-r--r--services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java225
-rw-r--r--services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java13
-rw-r--r--services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java27
-rw-r--r--services/companion/java/com/android/server/companion/virtual/CameraAccessController.java13
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java45
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java44
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java24
-rw-r--r--services/core/Android.bp10
-rw-r--r--services/core/java/android/os/BatteryStatsInternal.java3
-rw-r--r--services/core/java/com/android/server/BatteryService.java375
-rw-r--r--services/core/java/com/android/server/BootReceiver.java14
-rw-r--r--services/core/java/com/android/server/ConsumerIrService.java6
-rw-r--r--services/core/java/com/android/server/SerialService.java21
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java11
-rw-r--r--services/core/java/com/android/server/SystemConfig.java8
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java62
-rw-r--r--services/core/java/com/android/server/TradeInModeService.java2
-rw-r--r--services/core/java/com/android/server/adaptiveauth/OWNERS1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java81
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java12
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java11
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java16
-rw-r--r--services/core/java/com/android/server/am/BroadcastController.java4
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java147
-rw-r--r--services/core/java/com/android/server/am/BroadcastSkipPolicy.java100
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java2
-rw-r--r--services/core/java/com/android/server/am/FgsTempAllowList.java17
-rw-r--r--services/core/java/com/android/server/am/OWNERS3
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java286
-rw-r--r--services/core/java/com/android/server/am/OomAdjusterModernImpl.java22
-rw-r--r--services/core/java/com/android/server/am/PhantomProcessList.java1
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java1
-rw-r--r--services/core/java/com/android/server/am/ProcessServiceRecord.java10
-rw-r--r--services/core/java/com/android/server/am/ProcessStateController.java25
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java7
-rw-r--r--services/core/java/com/android/server/am/broadcasts_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig35
-rw-r--r--services/core/java/com/android/server/appbinding/AppBindingService.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java27
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java80
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java74
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java44
-rw-r--r--services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java86
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java1
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java69
-rw-r--r--services/core/java/com/android/server/biometrics/PreAuthInfo.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java10
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java1
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java44
-rw-r--r--services/core/java/com/android/server/compat/OverrideValidatorImpl.java1
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java29
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompatNative.java1
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java1
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceRepository.java13
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java87
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java15
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java19
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java29
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java7
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java10
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java17
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java95
-rw-r--r--services/core/java/com/android/server/flags/services.aconfig11
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java10
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java8
-rw-r--r--services/core/java/com/android/server/input/AppLaunchShortcutManager.java336
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java355
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java40
-rw-r--r--services/core/java/com/android/server/input/InputSettingsObserver.java9
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java415
-rw-r--r--services/core/java/com/android/server/input/KeyboardBacklightController.java10
-rw-r--r--services/core/java/com/android/server/input/KeyboardLedController.java62
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java26
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java33
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java1
-rw-r--r--services/core/java/com/android/server/integrity/model/BitInputStream.java72
-rw-r--r--services/core/java/com/android/server/integrity/model/BitOutputStream.java105
-rw-r--r--services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java63
-rw-r--r--services/core/java/com/android/server/integrity/model/ComponentBitSize.java45
-rw-r--r--services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java93
-rw-r--r--services/core/java/com/android/server/integrity/model/RuleMetadata.java41
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java66
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java324
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java69
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java151
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java52
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleSerializer.java39
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java35
-rw-r--r--services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java60
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java66
-rw-r--r--services/core/java/com/android/server/media/AudioManagerRouteController.java11
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java5
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java78
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java4
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java37
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java64
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java191
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java207
-rw-r--r--services/core/java/com/android/server/net/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/notification/GroupHelper.java211
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java94
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java219
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java30
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java2
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java9
-rw-r--r--services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java135
-rw-r--r--services/core/java/com/android/server/pm/BackgroundInstallControlService.java14
-rw-r--r--services/core/java/com/android/server/pm/InstallDependencyHelper.java67
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java57
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java90
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java51
-rw-r--r--services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesImpl.java80
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java120
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java21
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java22
-rw-r--r--services/core/java/com/android/server/policy/ModifierShortcutManager.java25
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java73
-rw-r--r--services/core/java/com/android/server/policy/WindowWakeUpPolicy.java37
-rw-r--r--services/core/java/com/android/server/power/FrameworkStatsLogger.java62
-rw-r--r--services/core/java/com/android/server/power/Notifier.java77
-rw-r--r--services/core/java/com/android/server/power/PowerGroup.java44
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java23
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java373
-rw-r--r--services/core/java/com/android/server/power/feature/PowerManagerFlags.java12
-rw-r--r--services/core/java/com/android/server/power/feature/power_flags.aconfig7
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java52
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java38
-rw-r--r--services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java (renamed from services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java)10
-rw-r--r--services/core/java/com/android/server/security/adaptiveauthentication/OWNERS3
-rw-r--r--services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java24
-rw-r--r--services/core/java/com/android/server/stats/pull/psi/OWNERS9
-rw-r--r--services/core/java/com/android/server/stats/pull/psi/PsiData.java106
-rw-r--r--services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java161
-rw-r--r--services/core/java/com/android/server/utils/LazyJniRegistrar.java47
-rw-r--r--services/core/java/com/android/server/utils/OWNERS1
-rw-r--r--services/core/java/com/android/server/vcn/VcnContext.java4
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java2
-rw-r--r--services/core/java/com/android/server/vibrator/ExternalVibrationSession.java16
-rw-r--r--services/core/java/com/android/server/vibrator/SingleVibrationSession.java14
-rw-r--r--services/core/java/com/android/server/vibrator/VendorVibrationSession.java493
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java17
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSession.java33
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java517
-rw-r--r--services/core/java/com/android/server/vr/VrManagerService.java5
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperCropper.java19
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java31
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java62
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java18
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java28
-rw-r--r--services/core/java/com/android/server/wm/AppCompatCameraPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java16
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java57
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java29
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java79
-rw-r--r--services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java15
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java33
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java4
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java4
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java13
-rw-r--r--services/core/java/com/android/server/wm/OWNERS1
-rw-r--r--services/core/java/com/android/server/wm/RefreshRatePolicy.java42
-rw-r--r--services/core/java/com/android/server/wm/Session.java2
-rw-r--r--services/core/java/com/android/server/wm/SnapshotController.java5
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java62
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java63
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java55
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java6
-rw-r--r--services/core/jni/Android.bp2
-rw-r--r--services/core/jni/OWNERS1
-rw-r--r--services/core/jni/com_android_server_SerialService.cpp83
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp47
-rw-r--r--services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp52
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp131
-rw-r--r--services/core/jni/onload.cpp11
-rw-r--r--services/core/services-jarjar-rules.txt2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java1223
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java16
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java43
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java151
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java41
-rw-r--r--services/java/com/android/server/SystemServer.java17
-rw-r--r--services/proguard.flags2
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java8
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java58
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/Android.bp44
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/AndroidManifest.xml27
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING7
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java24
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java23
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java40
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterface.java (renamed from services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java)23
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java23
-rw-r--r--services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java143
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java12
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java195
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt6
-rw-r--r--services/tests/displayservicetests/AndroidManifest.xml1
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java116
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java29
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt1
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt476
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java45
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt13
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java68
-rw-r--r--services/tests/mockingservicestests/Android.bp29
-rw-r--r--services/tests/mockingservicestests/AndroidTest.xml6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java338
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java66
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/OWNERS2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java439
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java384
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java173
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java435
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java103
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java55
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java192
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java52
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java85
-rw-r--r--services/tests/powerservicetests/Android.bp1
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java338
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java122
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java60
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java24
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java68
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java35
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java66
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java5
-rw-r--r--services/tests/servicestests/Android.bp41
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java168
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java97
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java914
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java344
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java291
-rw-r--r--services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java)19
-rw-r--r--services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java305
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt96
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java436
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java329
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java58
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java285
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java67
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java33
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java18
-rw-r--r--services/tests/vibrator/AndroidManifest.xml3
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java537
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java97
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java95
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java93
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java95
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java145
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java44
-rw-r--r--services/usb/OWNERS8
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java26
-rw-r--r--telecomm/java/android/telecom/Log.java19
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java34
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java10
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java61
-rw-r--r--telephony/java/android/telephony/satellite/EarfcnRange.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/EarfcnRange.java124
-rw-r--r--telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl12
-rw-r--r--telephony/java/android/telephony/satellite/ISatelliteDisallowedReasonsCallback.aidl30
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java122
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java14
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java38
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteInfo.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteInfo.java169
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java317
-rw-r--r--telephony/java/android/telephony/satellite/SatellitePosition.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/SatellitePosition.java114
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl55
-rw-r--r--tests/AppJankTest/AndroidManifest.xml2
-rw-r--r--tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java171
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java26
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt27
-rw-r--r--tests/Input/res/xml/bookmarks.xml60
-rw-r--r--tests/Input/src/com/android/server/input/InputGestureManagerTests.kt71
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt14
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt996
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt38
-rw-r--r--tests/PackageWatchdog/Android.bp8
-rw-r--r--tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java244
-rw-r--r--tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java4
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java8
-rw-r--r--tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java108
-rw-r--r--tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java32
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java32
-rw-r--r--tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt76
-rw-r--r--tests/testables/tests/Android.bp4
-rw-r--r--tests/testables/tests/AndroidManifest.xml4
-rw-r--r--tests/testables/tests/AndroidTest.xml51
-rw-r--r--tests/testables/tests/goldens/recordFilmstrip_withAnimator.pngbin0 -> 40500 bytes
-rw-r--r--tests/testables/tests/goldens/recordFilmstrip_withSpring.pngbin0 -> 32206 bytes
-rw-r--r--tests/testables/tests/goldens/recordTimeSeries_withAnimator.json (renamed from tests/testables/tests/goldens/recordMotion_withAnimator.json)2
-rw-r--r--tests/testables/tests/goldens/recordTimeSeries_withSpring.json (renamed from tests/testables/tests/goldens/recordMotion_withSpring.json)2
-rw-r--r--tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt188
-rw-r--r--tests/utils/testutils/java/android/os/test/TestLooper.java22
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java19
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java1
-rw-r--r--tools/aapt2/Debug.cpp81
-rw-r--r--tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt2
-rw-r--r--wifi/wifi.aconfig9
2414 files changed, 80094 insertions, 30128 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 26fbd270eb46..6e37b7e55eef 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@ aconfig_declarations_group {
java_aconfig_libraries: [
// !!! KEEP THIS LIST ALPHABETICAL !!!
"aconfig_mediacodec_flags_java_lib",
+ "aconfig_settingslib_flags_java_lib",
"aconfig_trade_in_mode_flags_java_lib",
"android-sdk-flags-java",
"android.adaptiveauth.flags-aconfig-java",
@@ -71,6 +72,7 @@ aconfig_declarations_group {
"android.service.dreams.flags-aconfig-java",
"android.service.notification.flags-aconfig-java",
"android.service.appprediction.flags-aconfig-java",
+ "android.service.quickaccesswallet.flags-aconfig-java",
"android.service.voice.flags-aconfig-java",
"android.speech.flags-aconfig-java",
"android.systemserver.flags-aconfig-java",
@@ -348,6 +350,7 @@ java_aconfig_library {
aconfig_declarations {
name: "android.security.flags-aconfig",
package: "android.security",
+ exportable: true,
container: "system",
srcs: ["core/java/android/security/*.aconfig"],
}
@@ -365,6 +368,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.security.flags-aconfig-java-export",
+ aconfig_declarations: "android.security.flags-aconfig",
+ mode: "exported",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
cc_aconfig_library {
name: "android_security_flags_aconfig_c_lib",
aconfig_declarations: "android.security.flags-aconfig",
@@ -1749,3 +1759,34 @@ cc_aconfig_library {
],
min_sdk_version: "apex_inherit",
}
+
+// Settings Lib
+aconfig_declarations {
+ name: "aconfig_settingslib_flags",
+ package: "com.android.settingslib.flags",
+ container: "system",
+ srcs: [
+ "packages/SettingsLib/aconfig/settingslib.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "aconfig_settingslib_flags_java_lib",
+ aconfig_declarations: "aconfig_settingslib_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Quick Access Wallet
+aconfig_declarations {
+ name: "android.service.quickaccesswallet.flags-aconfig",
+ package: "android.service.quickaccesswallet",
+ exportable: true,
+ container: "system",
+ srcs: ["core/java/android/service/quickaccesswallet/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.service.quickaccesswallet.flags-aconfig-java",
+ aconfig_declarations: "android.service.quickaccesswallet.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 26d0d65f329c..48f0928f24d7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -220,7 +220,7 @@ java_library {
"android.hardware.contexthub-V1.0-java",
"android.hardware.contexthub-V1.1-java",
"android.hardware.contexthub-V1.2-java",
- "android.hardware.contexthub-V3-java",
+ "android.hardware.contexthub-V4-java",
"android.hardware.gnss-V1.0-java",
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
@@ -399,6 +399,7 @@ java_defaults {
"com.android.sysprop.foldlockbehavior",
"com.android.sysprop.view",
"framework-internal-utils",
+ "dynamic_instrumentation_manager_aidl-java",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
diff --git a/android-sdk-flags/OWNERS b/android-sdk-flags/OWNERS
new file mode 100644
index 000000000000..01f45dd01e36
--- /dev/null
+++ b/android-sdk-flags/OWNERS
@@ -0,0 +1 @@
+include /SDK_OWNERS
diff --git a/android-sdk-flags/flags.aconfig b/android-sdk-flags/flags.aconfig
index cfe298e187d1..19c7bf674832 100644
--- a/android-sdk-flags/flags.aconfig
+++ b/android-sdk-flags/flags.aconfig
@@ -6,6 +6,7 @@ flag {
namespace: "android_sdk"
description: "Use the new SDK major.minor versioning scheme (e.g. Android 40.1) which replaces the old single-integer scheme (e.g. Android 15)."
bug: "350458259"
+ is_exported: true
# Use is_fixed_read_only because DeviceConfig may not be available when Build.VERSION_CODES is first accessed
is_fixed_read_only: true
diff --git a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
index 8d2d04471f8e..8160ca2eba69 100644
--- a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
@@ -159,9 +159,7 @@ public class LongArrayMultiStateCounterPerfTest {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
long time = 1000;
- LongArrayMultiStateCounter.LongArrayContainer timeInFreq =
- new LongArrayMultiStateCounter.LongArrayContainer(4);
- timeInFreq.setValues(new long[]{100, 200, 300, 400});
+ long[] timeInFreq = {100, 200, 300, 400};
while (state.keepRunning()) {
counter.setState(1, time);
counter.setState(0, time + 1000);
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index debd85096488..47a85498f51b 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -37,3 +37,19 @@ flag {
description: "Ignore the important_while_foreground flag and change the related APIs to be not effective"
bug: "374175032"
}
+
+flag {
+ name: "get_pending_job_reasons_api"
+ is_exported: true
+ namespace: "backstage_power"
+ description: "Introduce a new getPendingJobReasons() API which returns reasons why a job may not have executed. Also deprecate the existing getPendingJobReason() API."
+ bug: "372031023"
+}
+
+flag {
+ name: "get_pending_job_reasons_history_api"
+ is_exported: true
+ namespace: "backstage_power"
+ description: "Introduce a new getPendingJobReasonsHistory() API which returns a limited historical view of getPendingJobReasons()."
+ bug: "372031023"
+}
diff --git a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
index 3cfddc6d8e2b..e9b11f46ddde 100644
--- a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
+++ b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java
@@ -25,11 +25,13 @@ import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
+import android.app.job.PendingJobReasonsInfo;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
import android.util.ArrayMap;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -173,6 +175,26 @@ public class JobSchedulerImpl extends JobScheduler {
}
@Override
+ @NonNull
+ public int[] getPendingJobReasons(int jobId) {
+ try {
+ return mBinder.getPendingJobReasons(mNamespace, jobId);
+ } catch (RemoteException e) {
+ return new int[] { PENDING_JOB_REASON_UNDEFINED };
+ }
+ }
+
+ @Override
+ @NonNull
+ public List<PendingJobReasonsInfo> getPendingJobReasonsHistory(int jobId) {
+ try {
+ return mBinder.getPendingJobReasonsHistory(mNamespace, jobId);
+ } catch (RemoteException e) {
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ @Override
public boolean canRunUserInitiatedJobs() {
try {
return mBinder.canRunUserInitiatedJobs(mContext.getOpPackageName());
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl
index 416a2d8c0002..dc7f3d143e4c 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl
@@ -20,6 +20,7 @@ import android.app.job.IUserVisibleJobObserver;
import android.app.job.JobInfo;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
+import android.app.job.PendingJobReasonsInfo;
import android.content.pm.ParceledListSlice;
import java.util.Map;
@@ -39,6 +40,8 @@ interface IJobScheduler {
ParceledListSlice<JobInfo> getAllPendingJobsInNamespace(String namespace);
JobInfo getPendingJob(String namespace, int jobId);
int getPendingJobReason(String namespace, int jobId);
+ int[] getPendingJobReasons(String namespace, int jobId);
+ List<PendingJobReasonsInfo> getPendingJobReasonsHistory(String namespace, int jobId);
boolean canRunUserInitiatedJobs(String packageName);
boolean hasRunUserInitiatedJobsPermission(String packageName, int userId);
List<JobInfo> getStartedJobs();
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index ad54cd397413..4fbd55a5d528 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -16,6 +16,7 @@
package android.app.job;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -238,6 +239,13 @@ public abstract class JobScheduler {
* to defer this job.
*/
public static final int PENDING_JOB_REASON_USER = 15;
+ /**
+ * The override deadline has not transpired.
+ *
+ * @see JobInfo.Builder#setOverrideDeadline(long)
+ */
+ @FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_API)
+ public static final int PENDING_JOB_REASON_CONSTRAINT_DEADLINE = 16;
/** @hide */
@IntDef(prefix = {"PENDING_JOB_REASON_"}, value = {
@@ -259,6 +267,7 @@ public abstract class JobScheduler {
PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION,
PENDING_JOB_REASON_QUOTA,
PENDING_JOB_REASON_USER,
+ PENDING_JOB_REASON_CONSTRAINT_DEADLINE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PendingJobReason {
@@ -458,6 +467,10 @@ public abstract class JobScheduler {
/**
* Returns a reason why the job is pending and not currently executing. If there are multiple
* reasons why a job may be pending, this will only return one of them.
+ *
+ * @apiNote
+ * To know all the potential reasons why the job may be pending,
+ * use {@link #getPendingJobReasons(int)} instead.
*/
@PendingJobReason
public int getPendingJobReason(int jobId) {
@@ -465,6 +478,49 @@ public abstract class JobScheduler {
}
/**
+ * Returns potential reasons why the job with the given {@code jobId} may be pending
+ * and not currently executing.
+ *
+ * The returned array will include {@link PendingJobReason reasons} composed of both
+ * explicitly set constraints on the job and implicit constraints imposed by the system.
+ * The results can be used to debug why a given job may not be currently executing.
+ */
+ @FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_API)
+ @NonNull
+ @PendingJobReason
+ public int[] getPendingJobReasons(int jobId) {
+ return new int[] { PENDING_JOB_REASON_UNDEFINED };
+ }
+
+ /**
+ * For the given {@code jobId}, returns a limited historical view of why the job may have
+ * been pending execution. The returned list is composed of {@link PendingJobReasonsInfo}
+ * objects, each of which include a timestamp since epoch along with an array of
+ * unsatisfied constraints represented by {@link PendingJobReason PendingJobReason constants}.
+ * <p>
+ * These constants could either be explicitly set constraints on the job or implicit
+ * constraints imposed by the system due to various reasons.
+ * The results can be used to debug why a given job may have been pending execution.
+ * <p>
+ * If the only {@link PendingJobReason} for the timestamp is
+ * {@link PendingJobReason#PENDING_JOB_REASON_UNDEFINED}, it could mean that
+ * the job was ready to be executed at that point in time.
+ * <p>
+ * Note: there is no set interval for the timestamps in the returned list since
+ * constraint changes occur based on device status and various other factors.
+ * <p>
+ * Note: the pending job reasons history is not persisted across device reboots.
+ * <p>
+ * @throws IllegalArgumentException if the {@code jobId} is invalid.
+ * @see #getPendingJobReasons(int)
+ */
+ @FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API)
+ @NonNull
+ public List<PendingJobReasonsInfo> getPendingJobReasonsHistory(int jobId) {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
+
+ /**
* Returns {@code true} if the calling app currently holds the
* {@link android.Manifest.permission#RUN_USER_INITIATED_JOBS} permission, allowing it to run
* user-initiated jobs.
diff --git a/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl
new file mode 100644
index 000000000000..1a027020e25f
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.app.job;
+
+ parcelable PendingJobReasonsInfo;
diff --git a/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java
new file mode 100644
index 000000000000..3c96bab80794
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A simple wrapper which includes a timestamp (in millis since epoch)
+ * and an array of {@link JobScheduler.PendingJobReason reasons} at that timestamp
+ * for why a particular job may be pending.
+ */
+@FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API)
+public final class PendingJobReasonsInfo implements Parcelable {
+
+ @CurrentTimeMillisLong
+ private final long mTimestampMillis;
+
+ @NonNull
+ @JobScheduler.PendingJobReason
+ private final int[] mPendingJobReasons;
+
+ public PendingJobReasonsInfo(long timestampMillis,
+ @NonNull @JobScheduler.PendingJobReason int[] reasons) {
+ mTimestampMillis = timestampMillis;
+ mPendingJobReasons = reasons;
+ }
+
+ /**
+ * @return the time (in millis since epoch) associated with the set of pending job reasons.
+ */
+ @CurrentTimeMillisLong
+ public long getTimestampMillis() {
+ return mTimestampMillis;
+ }
+
+ /**
+ * Returns a set of {@link android.app.job.JobScheduler.PendingJobReason reasons} representing
+ * why the job may not have executed at the associated timestamp.
+ * <p>
+ * These reasons could either be explicitly set constraints on the job or implicit
+ * constraints imposed by the system due to various reasons.
+ * <p>
+ * Note: if the only {@link android.app.job.JobScheduler.PendingJobReason} present is
+ * {@link JobScheduler.PendingJobReason#PENDING_JOB_REASON_UNDEFINED}, it could mean
+ * that the job was ready to be executed at that time.
+ */
+ @NonNull
+ @JobScheduler.PendingJobReason
+ public int[] getPendingJobReasons() {
+ return mPendingJobReasons;
+ }
+
+ private PendingJobReasonsInfo(Parcel in) {
+ mTimestampMillis = in.readLong();
+ mPendingJobReasons = in.createIntArray();
+ }
+
+ @NonNull
+ public static final Creator<PendingJobReasonsInfo> CREATOR =
+ new Creator<>() {
+ @Override
+ public PendingJobReasonsInfo createFromParcel(Parcel in) {
+ return new PendingJobReasonsInfo(in);
+ }
+
+ @Override
+ public PendingJobReasonsInfo[] newArray(int size) {
+ return new PendingJobReasonsInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mTimestampMillis);
+ dest.writeIntArray(mPendingJobReasons);
+ }
+}
diff --git a/apex/jobscheduler/service/aconfig/device_idle.aconfig b/apex/jobscheduler/service/aconfig/device_idle.aconfig
index c4d0d1850a18..426031fbeb9c 100644
--- a/apex/jobscheduler/service/aconfig/device_idle.aconfig
+++ b/apex/jobscheduler/service/aconfig/device_idle.aconfig
@@ -17,3 +17,13 @@ flag {
description: "Disable wakelocks for background apps while Light Device Idle is active"
bug: "326607666"
}
+
+flag {
+ name: "use_cpu_time_for_temp_allowlist"
+ namespace: "backstage_power"
+ description: "Use CPU time for temporary allowlists"
+ bug: "376561328"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 3e650da2e66f..41fd4a29cfd1 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -620,8 +620,8 @@ public class DeviceIdleController extends SystemService
* the network and acquire wakelocks. Times are in milliseconds.
*/
@GuardedBy("this")
- private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes
- = new SparseArray<>();
+ @VisibleForTesting
+ final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes = new SparseArray<>();
private NetworkPolicyManagerInternal mNetworkPolicyManagerInternal;
@@ -1941,7 +1941,8 @@ public class DeviceIdleController extends SystemService
private static final int MSG_REPORT_IDLE_ON_LIGHT = 3;
private static final int MSG_REPORT_IDLE_OFF = 4;
private static final int MSG_REPORT_ACTIVE = 5;
- private static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+ @VisibleForTesting
+ static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
@VisibleForTesting
static final int MSG_REPORT_STATIONARY_STATUS = 7;
private static final int MSG_FINISH_IDLE_OP = 8;
@@ -2511,6 +2512,11 @@ public class DeviceIdleController extends SystemService
return SystemClock.elapsedRealtime();
}
+ /** Returns the current elapsed realtime in milliseconds. */
+ long getUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
LocationManager getLocationManager() {
if (mLocationManager == null) {
mLocationManager = mContext.getSystemService(LocationManager.class);
@@ -3264,7 +3270,8 @@ public class DeviceIdleController extends SystemService
void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid,
long duration, @TempAllowListType int tempAllowListType, boolean sync,
@ReasonCode int reasonCode, @Nullable String reason) {
- final long timeNow = SystemClock.elapsedRealtime();
+ final long timeNow = Flags.useCpuTimeForTempAllowlist() ? mInjector.getUptimeMillis()
+ : mInjector.getElapsedRealtime();
boolean informWhitelistChanged = false;
int appId = UserHandle.getAppId(uid);
synchronized (this) {
@@ -3350,7 +3357,8 @@ public class DeviceIdleController extends SystemService
}
void checkTempAppWhitelistTimeout(int uid) {
- final long timeNow = SystemClock.elapsedRealtime();
+ final long timeNow = Flags.useCpuTimeForTempAllowlist() ? mInjector.getUptimeMillis()
+ : mInjector.getElapsedRealtime();
final int appId = UserHandle.getAppId(uid);
if (DEBUG) {
Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow);
@@ -5219,6 +5227,17 @@ public class DeviceIdleController extends SystemService
}
}
+ pw.println(" Flags:");
+ pw.print(" ");
+ pw.print(Flags.FLAG_USE_CPU_TIME_FOR_TEMP_ALLOWLIST);
+ pw.print("=");
+ pw.println(Flags.useCpuTimeForTempAllowlist());
+ pw.print(" ");
+ pw.print(Flags.FLAG_REMOVE_IDLE_LOCATION);
+ pw.print("=");
+ pw.println(Flags.removeIdleLocation());
+ pw.println();
+
synchronized (this) {
mConstants.dump(pw);
@@ -5449,7 +5468,8 @@ public class DeviceIdleController extends SystemService
pw.println(" Temp whitelist schedule:");
prefix = " ";
}
- final long timeNow = SystemClock.elapsedRealtime();
+ final long timeNow = Flags.useCpuTimeForTempAllowlist() ? mInjector.getUptimeMillis()
+ : mInjector.getElapsedRealtime();
for (int i = 0; i < size; i++) {
pw.print(prefix);
pw.print("UID=");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index ba8e3e8b48fc..8f44698ffd8d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1550,7 +1550,7 @@ class JobConcurrencyManager {
mActivePkgStats.add(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
packageStats);
- mService.resetPendingJobReasonCache(jobStatus);
+ mService.resetPendingJobReasonsCache(jobStatus);
}
if (mService.getPendingJobQueue().remove(jobStatus)) {
mService.mJobPackageTracker.noteNonpending(jobStatus);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4c1951ae9d39..1c6e40e25a92 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -44,6 +44,7 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
+import android.app.job.PendingJobReasonsInfo;
import android.app.job.UserVisibleJobSummary;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -460,10 +461,10 @@ public class JobSchedulerService extends com.android.server.SystemService
private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>();
/**
- * Cached pending job reasons. Mapping from UID -> namespace -> job ID -> reason.
+ * Cached pending job reasons. Mapping from UID -> namespace -> job ID -> reasons.
*/
- @GuardedBy("mPendingJobReasonCache") // Use its own lock to avoid blocking JS processing
- private final SparseArrayMap<String, SparseIntArray> mPendingJobReasonCache =
+ @GuardedBy("mPendingJobReasonsCache") // Use its own lock to avoid blocking JS processing
+ private final SparseArrayMap<String, SparseArray<int[]>> mPendingJobReasonsCache =
new SparseArrayMap<>();
/**
@@ -2021,139 +2022,137 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
- @JobScheduler.PendingJobReason
- private int getPendingJobReason(int uid, String namespace, int jobId) {
- int reason;
+ @NonNull
+ private int[] getPendingJobReasons(int uid, String namespace, int jobId) {
+ int[] reasons;
// Some apps may attempt to query this frequently, so cache the reason under a separate lock
// so that the rest of JS processing isn't negatively impacted.
- synchronized (mPendingJobReasonCache) {
- SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace);
- if (jobIdToReason != null) {
- reason = jobIdToReason.get(jobId, JobScheduler.PENDING_JOB_REASON_UNDEFINED);
- if (reason != JobScheduler.PENDING_JOB_REASON_UNDEFINED) {
- return reason;
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> jobIdToReasons = mPendingJobReasonsCache.get(uid, namespace);
+ if (jobIdToReasons != null) {
+ reasons = jobIdToReasons.get(jobId);
+ if (reasons != null) {
+ return reasons;
}
}
}
synchronized (mLock) {
- reason = getPendingJobReasonLocked(uid, namespace, jobId);
+ reasons = getPendingJobReasonsLocked(uid, namespace, jobId);
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReason("
- + uid + "," + namespace + "," + jobId + ")=" + reason);
+ Slog.v(TAG, "getPendingJobReasons("
+ + uid + "," + namespace + "," + jobId + ")=" + Arrays.toString(reasons));
}
}
- synchronized (mPendingJobReasonCache) {
- SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace);
- if (jobIdToReason == null) {
- jobIdToReason = new SparseIntArray();
- mPendingJobReasonCache.add(uid, namespace, jobIdToReason);
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> jobIdToReasons = mPendingJobReasonsCache.get(uid, namespace);
+ if (jobIdToReasons == null) {
+ jobIdToReasons = new SparseArray<>();
+ mPendingJobReasonsCache.add(uid, namespace, jobIdToReasons);
}
- jobIdToReason.put(jobId, reason);
+ jobIdToReasons.put(jobId, reasons);
}
- return reason;
+ return reasons;
}
@VisibleForTesting
@JobScheduler.PendingJobReason
int getPendingJobReason(JobStatus job) {
- return getPendingJobReason(job.getUid(), job.getNamespace(), job.getJobId());
+ // keep original method to enable unit testing with flags
+ return getPendingJobReasons(job.getUid(), job.getNamespace(), job.getJobId())[0];
+ }
+
+ @VisibleForTesting
+ @NonNull
+ int[] getPendingJobReasons(JobStatus job) {
+ return getPendingJobReasons(job.getUid(), job.getNamespace(), job.getJobId());
}
- @JobScheduler.PendingJobReason
@GuardedBy("mLock")
- private int getPendingJobReasonLocked(int uid, String namespace, int jobId) {
+ @NonNull
+ private int[] getPendingJobReasonsLocked(int uid, String namespace, int jobId) {
// Very similar code to isReadyToBeExecutedLocked.
-
- JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
+ final JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
if (job == null) {
// Job doesn't exist.
- return JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID };
}
-
if (isCurrentlyRunningLocked(job)) {
- return JobScheduler.PENDING_JOB_REASON_EXECUTING;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_EXECUTING };
}
+ final String debugPrefix = "getPendingJobReasonsLocked: " + job.toShortString();
final boolean jobReady = job.isReady();
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " ready=" + jobReady);
+ Slog.v(TAG, debugPrefix + " ready=" + jobReady);
}
-
if (!jobReady) {
- return job.getPendingJobReason();
+ return job.getPendingJobReasons();
}
final boolean userStarted = areUsersStartedLocked(job);
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " userStarted=" + userStarted);
+ Slog.v(TAG, debugPrefix + " userStarted=" + userStarted);
}
if (!userStarted) {
- return JobScheduler.PENDING_JOB_REASON_USER;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_USER };
}
final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " backingUp=" + backingUp);
+ Slog.v(TAG, debugPrefix + " backingUp=" + backingUp);
}
-
if (backingUp) {
// TODO: Should we make a special reason for this?
- return JobScheduler.PENDING_JOB_REASON_APP;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_APP };
}
- JobRestriction restriction = checkIfRestricted(job);
+ final JobRestriction restriction = checkIfRestricted(job);
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " restriction=" + restriction);
+ Slog.v(TAG, debugPrefix + " restriction=" + restriction);
}
if (restriction != null) {
- return restriction.getPendingReason();
+ // Currently this will return _DEVICE_STATE because of thermal reasons.
+ // TODO (b/372031023): does it make sense to move this along with the
+ // pendingJobReasons() call above and also get the pending reasons from
+ // all of the restriction controllers?
+ return new int[] { restriction.getPendingReason() };
}
- // The following can be a little more expensive (especially jobActive, since we need to
- // go through the array of all potentially active jobs), so we are doing them
- // later... but still before checking with the package manager!
+ // The following can be a little more expensive, so we are doing it later,
+ // but still before checking with the package manager!
final boolean jobPending = mPendingJobQueue.contains(job);
-
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " pending=" + jobPending);
+ Slog.v(TAG, debugPrefix + " pending=" + jobPending);
}
-
if (jobPending) {
- // We haven't started the job for some reason. Presumably, there are too many jobs
- // running.
- return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
- }
-
- final boolean jobActive = mConcurrencyManager.isJobRunningLocked(job);
-
- if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " active=" + jobActive);
- }
- if (jobActive) {
- return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
+ // We haven't started the job - presumably, there are too many jobs running.
+ return new int[] { JobScheduler.PENDING_JOB_REASON_DEVICE_STATE };
}
// Validate that the defined package+service is still present & viable.
final boolean componentUsable = isComponentUsable(job);
-
if (DEBUG) {
- Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
- + " componentUsable=" + componentUsable);
+ Slog.v(TAG, debugPrefix + " componentUsable=" + componentUsable);
}
if (!componentUsable) {
- return JobScheduler.PENDING_JOB_REASON_APP;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_APP };
}
- return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
+ return new int[] { JobScheduler.PENDING_JOB_REASON_UNDEFINED };
+ }
+
+ @NonNull
+ private List<PendingJobReasonsInfo> getPendingJobReasonsHistory(
+ int uid, String namespace, int jobId) {
+ synchronized (mLock) {
+ final JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
+ if (job == null) {
+ // Job doesn't exist.
+ throw new IllegalArgumentException("Invalid job id");
+ }
+
+ return job.getPendingJobReasonsHistory();
+ }
}
private JobInfo getPendingJob(int uid, @Nullable String namespace, int jobId) {
@@ -2195,15 +2194,16 @@ public class JobSchedulerService extends com.android.server.SystemService
// The app process will be killed soon. There's no point keeping its jobs in
// the pending queue to try and start them.
if (mPendingJobQueue.remove(job)) {
- synchronized (mPendingJobReasonCache) {
- SparseIntArray jobIdToReason = mPendingJobReasonCache.get(
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> jobIdToReason = mPendingJobReasonsCache.get(
job.getUid(), job.getNamespace());
if (jobIdToReason == null) {
- jobIdToReason = new SparseIntArray();
- mPendingJobReasonCache.add(job.getUid(), job.getNamespace(),
+ jobIdToReason = new SparseArray<>();
+ mPendingJobReasonsCache.add(job.getUid(), job.getNamespace(),
jobIdToReason);
}
- jobIdToReason.put(job.getJobId(), JobScheduler.PENDING_JOB_REASON_USER);
+ jobIdToReason.put(job.getJobId(),
+ new int[] { JobScheduler.PENDING_JOB_REASON_USER });
}
}
}
@@ -2229,8 +2229,8 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
mJobs.removeJobsOfUnlistedUsers(umi.getUserIds());
}
- synchronized (mPendingJobReasonCache) {
- mPendingJobReasonCache.clear();
+ synchronized (mPendingJobReasonsCache) {
+ mPendingJobReasonsCache.clear();
}
}
@@ -2875,7 +2875,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean update = lastJob != null;
mJobs.add(jobStatus);
// Clear potentially cached INVALID_JOB_ID reason.
- resetPendingJobReasonCache(jobStatus);
+ resetPendingJobReasonsCache(jobStatus);
if (mReadyToRock) {
for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
@@ -2897,9 +2897,9 @@ public class JobSchedulerService extends com.android.server.SystemService
// Deal with any remaining work items in the old job.
jobStatus.stopTrackingJobLocked(incomingJob);
- synchronized (mPendingJobReasonCache) {
- SparseIntArray reasonCache =
- mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace());
+ synchronized (mPendingJobReasonsCache) {
+ SparseArray<int[]> reasonCache =
+ mPendingJobReasonsCache.get(jobStatus.getUid(), jobStatus.getNamespace());
if (reasonCache != null) {
reasonCache.delete(jobStatus.getJobId());
}
@@ -2927,11 +2927,11 @@ public class JobSchedulerService extends com.android.server.SystemService
return removed;
}
- /** Remove the pending job reason for this job from the cache. */
- void resetPendingJobReasonCache(@NonNull JobStatus jobStatus) {
- synchronized (mPendingJobReasonCache) {
- final SparseIntArray reasons =
- mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace());
+ /** Remove the pending job reasons for this job from the cache. */
+ void resetPendingJobReasonsCache(@NonNull JobStatus jobStatus) {
+ synchronized (mPendingJobReasonsCache) {
+ final SparseArray<int[]> reasons =
+ mPendingJobReasonsCache.get(jobStatus.getUid(), jobStatus.getNamespace());
if (reasons != null) {
reasons.delete(jobStatus.getJobId());
}
@@ -3313,18 +3313,18 @@ public class JobSchedulerService extends com.android.server.SystemService
public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) {
if (changedJobs == null) {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
- synchronized (mPendingJobReasonCache) {
- mPendingJobReasonCache.clear();
+ synchronized (mPendingJobReasonsCache) {
+ mPendingJobReasonsCache.clear();
}
} else if (changedJobs.size() > 0) {
synchronized (mLock) {
mChangedJobList.addAll(changedJobs);
}
mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget();
- synchronized (mPendingJobReasonCache) {
+ synchronized (mPendingJobReasonsCache) {
for (int i = changedJobs.size() - 1; i >= 0; --i) {
final JobStatus job = changedJobs.valueAt(i);
- resetPendingJobReasonCache(job);
+ resetPendingJobReasonsCache(job);
}
}
}
@@ -3893,23 +3893,21 @@ public class JobSchedulerService extends com.android.server.SystemService
// Update the pending reason for any jobs that aren't going to be run.
final int numRunnableJobs = runnableJobs.size();
if (numRunnableJobs > 0 && numRunnableJobs != jobsToRun.size()) {
- synchronized (mPendingJobReasonCache) {
+ synchronized (mPendingJobReasonsCache) {
for (int i = 0; i < numRunnableJobs; ++i) {
final JobStatus job = runnableJobs.get(i);
if (jobsToRun.contains(job)) {
- // We're running this job. Skip updating the pending reason.
- continue;
+ continue; // we're running this job - skip updating the pending reason.
}
- SparseIntArray reasons =
- mPendingJobReasonCache.get(job.getUid(), job.getNamespace());
+ SparseArray<int[]> reasons =
+ mPendingJobReasonsCache.get(job.getUid(), job.getNamespace());
if (reasons == null) {
- reasons = new SparseIntArray();
- mPendingJobReasonCache.add(job.getUid(), job.getNamespace(), reasons);
+ reasons = new SparseArray<>();
+ mPendingJobReasonsCache.add(job.getUid(), job.getNamespace(), reasons);
}
- // We're force batching these jobs, so consider it an optimization
- // policy reason.
- reasons.put(job.getJobId(),
- JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION);
+ // we're force batching these jobs - note it as optimization.
+ reasons.put(job.getJobId(), new int[]
+ { JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION });
}
}
}
@@ -5123,11 +5121,28 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override
public int getPendingJobReason(String namespace, int jobId) throws RemoteException {
+ return getPendingJobReasons(validateNamespace(namespace), jobId)[0];
+ }
+
+ @Override
+ public int[] getPendingJobReasons(String namespace, int jobId) throws RemoteException {
final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return JobSchedulerService.this.getPendingJobReasons(
+ uid, validateNamespace(namespace), jobId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ @Override
+ public List<PendingJobReasonsInfo> getPendingJobReasonsHistory(String namespace, int jobId)
+ throws RemoteException {
+ final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- return JobSchedulerService.this.getPendingJobReason(
+ return JobSchedulerService.this.getPendingJobReasonsHistory(
uid, validateNamespace(namespace), jobId);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -5867,6 +5882,12 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND,
android.app.job.Flags.ignoreImportantWhileForeground());
pw.println();
+ pw.print(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API,
+ android.app.job.Flags.getPendingJobReasonsApi());
+ pw.println();
+ pw.print(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API,
+ android.app.job.Flags.getPendingJobReasonsHistoryApi());
+ pw.println();
pw.decreaseIndent();
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 68303e86055c..f3bc9c747f17 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -439,6 +439,12 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
case android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND:
pw.println(android.app.job.Flags.ignoreImportantWhileForeground());
break;
+ case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API:
+ pw.println(android.app.job.Flags.getPendingJobReasonsApi());
+ break;
+ case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API:
+ pw.println(android.app.job.Flags.getPendingJobReasonsHistoryApi());
+ break;
default:
pw.println("Unknown flag: " + flagName);
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 1dc5a714337c..b0784f1c69fd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -32,6 +32,7 @@ import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobWorkItem;
+import android.app.job.PendingJobReasonsInfo;
import android.app.job.UserVisibleJobSummary;
import android.content.ClipData;
import android.content.ComponentName;
@@ -39,6 +40,7 @@ import android.net.Network;
import android.net.NetworkRequest;
import android.net.Uri;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.text.format.DateFormat;
@@ -72,6 +74,7 @@ import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.function.Predicate;
@@ -515,6 +518,10 @@ public final class JobStatus {
private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY];
private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY];
+ private final List<PendingJobReasonsInfo> mPendingJobReasonsHistory = new ArrayList<>();
+ private static final int PENDING_JOB_HISTORY_RETURN_LIMIT = 10;
+ private static final int PENDING_JOB_HISTORY_TRIM_THRESHOLD = 25;
+
/**
* For use only by ContentObserverController: state it is maintaining about content URIs
* being observed.
@@ -1992,6 +1999,16 @@ public final class JobStatus {
mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
}
+ final int unsatisfiedConstraints = ~satisfiedConstraints
+ & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS);
+ populatePendingJobReasonsHistoryMap(isReady, nowElapsed, unsatisfiedConstraints);
+ final int historySize = mPendingJobReasonsHistory.size();
+ if (historySize >= PENDING_JOB_HISTORY_TRIM_THRESHOLD) {
+ // Ensure trimming doesn't occur too often - max history we currently return is 10
+ mPendingJobReasonsHistory.subList(0, historySize - PENDING_JOB_HISTORY_RETURN_LIMIT)
+ .clear();
+ }
+
return true;
}
@@ -2066,15 +2083,10 @@ public final class JobStatus {
}
}
- /**
- * If {@link #isReady()} returns false, this will return a single reason why the job isn't
- * ready. If {@link #isReady()} returns true, this will return
- * {@link JobScheduler#PENDING_JOB_REASON_UNDEFINED}.
- */
- @JobScheduler.PendingJobReason
- public int getPendingJobReason() {
- final int unsatisfiedConstraints = ~satisfiedConstraints
- & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS);
+ @NonNull
+ public ArrayList<Integer> constraintsToPendingJobReasons(int unsatisfiedConstraints) {
+ final ArrayList<Integer> reasons = new ArrayList<>();
+
if ((CONSTRAINT_BACKGROUND_NOT_RESTRICTED & unsatisfiedConstraints) != 0) {
// The BACKGROUND_NOT_RESTRICTED constraint could be unsatisfied either because
// the app is background restricted, or because we're restricting background work
@@ -2084,78 +2096,160 @@ public final class JobStatus {
// (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
// battery saver state.
if (mIsUserBgRestricted) {
- return JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION);
+ } else {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_DEVICE_STATE);
+ }
+ }
+ if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) {
+ if (!reasons.contains(JobScheduler.PENDING_JOB_REASON_DEVICE_STATE)) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_DEVICE_STATE);
}
- return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
}
+
if ((CONSTRAINT_BATTERY_NOT_LOW & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_BATTERY_NOT_LOW & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW);
+ } else {
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_APP_STANDBY);
}
- // Hard-coding right now since the current dynamic constraint sets don't overlap
- // TODO: return based on active dynamic constraint sets when they start overlapping
- return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
}
if ((CONSTRAINT_CHARGING & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_CHARGING & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING);
+ } else {
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ if (!reasons.contains(JobScheduler.PENDING_JOB_REASON_APP_STANDBY)) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_APP_STANDBY);
+ }
}
- // Hard-coding right now since the current dynamic constraint sets don't overlap
- // TODO: return based on active dynamic constraint sets when they start overlapping
- return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
- }
- if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY;
- }
- if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER;
- }
- if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
- }
- if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION;
}
if ((CONSTRAINT_IDLE & unsatisfiedConstraints) != 0) {
if ((CONSTRAINT_IDLE & requiredConstraints) != 0) {
// The developer requested this constraint, so it makes sense to return the
// explicit constraint reason.
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE);
+ } else {
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ if (!reasons.contains(JobScheduler.PENDING_JOB_REASON_APP_STANDBY)) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_APP_STANDBY);
+ }
}
- // Hard-coding right now since the current dynamic constraint sets don't overlap
- // TODO: return based on active dynamic constraint sets when they start overlapping
- return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
+ }
+
+ if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY);
+ }
+ if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER);
+ }
+ if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION);
}
if ((CONSTRAINT_PREFETCH & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH);
}
if ((CONSTRAINT_STORAGE_NOT_LOW & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW);
}
if ((CONSTRAINT_TIMING_DELAY & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY);
}
if ((CONSTRAINT_WITHIN_QUOTA & unsatisfiedConstraints) != 0) {
- return JobScheduler.PENDING_JOB_REASON_QUOTA;
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_QUOTA);
}
+ if (android.app.job.Flags.getPendingJobReasonsApi()) {
+ if ((CONSTRAINT_DEADLINE & unsatisfiedConstraints) != 0) {
+ reasons.addLast(JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEADLINE);
+ }
+ }
+
+ return reasons;
+ }
- if (getEffectiveStandbyBucket() == NEVER_INDEX) {
- Slog.wtf(TAG, "App in NEVER bucket querying pending job reason");
- // The user hasn't officially launched this app.
- return JobScheduler.PENDING_JOB_REASON_USER;
+ /**
+ * This will return all potential reasons why the job is pending.
+ */
+ @NonNull
+ public int[] getPendingJobReasons() {
+ final int unsatisfiedConstraints = ~satisfiedConstraints
+ & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS);
+ final ArrayList<Integer> reasons = constraintsToPendingJobReasons(unsatisfiedConstraints);
+
+ if (reasons.isEmpty()) {
+ if (getEffectiveStandbyBucket() == NEVER_INDEX) {
+ Slog.wtf(TAG, "App in NEVER bucket querying pending job reason");
+ // The user hasn't officially launched this app.
+ reasons.add(JobScheduler.PENDING_JOB_REASON_USER);
+ } else if (serviceProcessName != null) {
+ reasons.add(JobScheduler.PENDING_JOB_REASON_APP);
+ } else {
+ reasons.add(JobScheduler.PENDING_JOB_REASON_UNDEFINED);
+ }
+ }
+
+ final int[] reasonsArr = new int[reasons.size()];
+ for (int i = 0; i < reasonsArr.length; i++) {
+ reasonsArr[i] = reasons.get(i);
+ }
+ return reasonsArr;
+ }
+
+ private void populatePendingJobReasonsHistoryMap(boolean isReady,
+ long constraintTimestamp, int unsatisfiedConstraints) {
+ final long constraintTimestampEpoch = // system_boot_time + constraint_satisfied_time
+ (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + constraintTimestamp;
+
+ if (isReady) {
+ // Job is ready to execute. At this point, if the job doesn't execute, it might be
+ // because of the app itself; if not, note it as undefined (documented in javadoc).
+ mPendingJobReasonsHistory.addLast(
+ new PendingJobReasonsInfo(
+ constraintTimestampEpoch,
+ new int[] { serviceProcessName != null
+ ? JobScheduler.PENDING_JOB_REASON_APP
+ : JobScheduler.PENDING_JOB_REASON_UNDEFINED }));
+ return;
}
- if (serviceProcessName != null) {
- return JobScheduler.PENDING_JOB_REASON_APP;
+
+ final ArrayList<Integer> reasons = constraintsToPendingJobReasons(unsatisfiedConstraints);
+ if (reasons.isEmpty()) {
+ // If the job is not waiting on any constraints to be met, note it as undefined.
+ reasons.add(JobScheduler.PENDING_JOB_REASON_UNDEFINED);
}
- if (!isReady()) {
- Slog.wtf(TAG, "Unknown reason job isn't ready");
+ final int[] reasonsArr = new int[reasons.size()];
+ for (int i = 0; i < reasonsArr.length; i++) {
+ reasonsArr[i] = reasons.get(i);
}
- return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
+ mPendingJobReasonsHistory.addLast(
+ new PendingJobReasonsInfo(constraintTimestampEpoch, reasonsArr));
+ }
+
+ /**
+ * Returns the last {@link #PENDING_JOB_HISTORY_RETURN_LIMIT} constraint changes.
+ */
+ @NonNull
+ public List<PendingJobReasonsInfo> getPendingJobReasonsHistory() {
+ final List<PendingJobReasonsInfo> returnList =
+ new ArrayList<>(PENDING_JOB_HISTORY_RETURN_LIMIT);
+ final int historySize = mPendingJobReasonsHistory.size();
+ if (historySize != 0) {
+ returnList.addAll(
+ mPendingJobReasonsHistory.subList(
+ Math.max(0, historySize - PENDING_JOB_HISTORY_RETURN_LIMIT),
+ historySize));
+ }
+
+ return returnList;
}
/** @return whether or not the @param constraint is satisfied */
diff --git a/api/Android.bp b/api/Android.bp
index ff674c7f8bd3..73262030ee37 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -73,6 +73,7 @@ combined_apis {
"framework-bluetooth",
"framework-configinfrastructure",
"framework-connectivity",
+ "framework-connectivity-b",
"framework-connectivity-t",
"framework-devicelock",
"framework-graphics",
@@ -126,27 +127,54 @@ combined_apis {
}),
}
+// Create a single file containing the latest released version of the whole
+// Android public API.
+java_genrule {
+ name: "android.api.merged.public.latest",
+ srcs: [
+ ":android.api.combined.public.latest",
+ ],
+ out: ["public-latest.txt"],
+ tools: ["metalava"],
+ cmd: metalava_cmd + " merge-signatures --format=2.0 $(in) --out $(out)",
+}
+
+// Make sure that the Android public API is compatible with the
+// previously released public API.
java_genrule {
name: "frameworks-base-api-current-compat",
srcs: [
- ":android.api.public.latest",
+ ":android.api.merged.public.latest",
":android-incompatibilities.api.public.latest",
":frameworks-base-api-current.txt",
],
out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
- "--check-compatibility:api:released $(location :android.api.public.latest) " +
+ "--check-compatibility:api:released $(location :android.api.merged.public.latest) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " +
"--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
"$(location :frameworks-base-api-current.txt)",
}
+// Create a single file containing the latest released version of the whole
+// Android system API.
+java_genrule {
+ name: "android.api.merged.system.latest",
+ srcs: [
+ ":android.api.combined.system.latest",
+ ],
+ out: ["system-latest.txt"],
+ tools: ["metalava"],
+ cmd: metalava_cmd + " merge-signatures --format=2.0 $(in) --out $(out)",
+}
+
+// Make sure that the Android system API is compatible with the
+// previously released system API.
java_genrule {
name: "frameworks-base-api-system-current-compat",
srcs: [
- ":android.api.public.latest",
- ":android.api.system.latest",
+ ":android.api.merged.system.latest",
":android-incompatibilities.api.system.latest",
":frameworks-base-api-current.txt",
":frameworks-base-api-system-current.txt",
@@ -154,20 +182,31 @@ java_genrule {
out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
- "--check-compatibility:api:released $(location :android.api.public.latest) " +
- "--check-compatibility:api:released $(location :android.api.system.latest) " +
+ "--check-compatibility:api:released $(location :android.api.merged.system.latest) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
"--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
"$(location :frameworks-base-api-current.txt) " +
"$(location :frameworks-base-api-system-current.txt)",
}
+// Create a single file containing the latest released version of the whole
+// Android module-lib API.
+java_genrule {
+ name: "android.api.merged.module-lib.latest",
+ srcs: [
+ ":android.api.combined.module-lib.latest",
+ ],
+ out: ["module-lib-latest.txt"],
+ tools: ["metalava"],
+ cmd: metalava_cmd + " merge-signatures --format=2.0 $(in) --out $(out)",
+}
+
+// Make sure that the Android module-lib API is compatible with the
+// previously released module-lib API.
java_genrule {
name: "frameworks-base-api-module-lib-current-compat",
srcs: [
- ":android.api.public.latest",
- ":android.api.system.latest",
- ":android.api.module-lib.latest",
+ ":android.api.merged.module-lib.latest",
":android-incompatibilities.api.module-lib.latest",
":frameworks-base-api-current.txt",
":frameworks-base-api-system-current.txt",
@@ -176,9 +215,7 @@ java_genrule {
out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
- "--check-compatibility:api:released $(location :android.api.public.latest) " +
- "--check-compatibility:api:released $(location :android.api.system.latest) " +
- "--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
+ "--check-compatibility:api:released $(location :android.api.merged.module-lib.latest) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " +
"--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
"$(location :frameworks-base-api-current.txt) " +
@@ -193,7 +230,7 @@ java_genrule {
cmd: "$(location merge_zips) $(out) $(in)",
srcs: [
":api-stubs-docs-non-updatable{.exportable}",
- ":all-modules-public-stubs-source",
+ ":all-modules-public-stubs-source-exportable",
],
visibility: ["//visibility:private"], // Used by make module in //development, mind
}
@@ -350,6 +387,7 @@ stubs_defaults {
"--error NoSettingsProvider",
"--error UnhiddenSystemApi",
"--error UnflaggedApi",
+ "--error FlaggedApiLiteral",
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
// Disable CallbackInterface, as Java 8 default interface methods avoid the extensibility
// issue interfaces had previously.
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index e8fcf4b2b32d..1ebe0cdfabd7 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -129,7 +129,7 @@ droidstubs {
droidstubs {
name: "framework-doc-stubs",
defaults: ["android-non-updatable-doc-stubs-defaults"],
- srcs: [":all-modules-public-stubs-source"],
+ srcs: [":all-modules-public-stubs-source-exportable"],
api_levels_module: "api_versions_public",
aidl: {
include_dirs: [
diff --git a/api/api.go b/api/api.go
index f32bdc32f75d..5ca24de1b46a 100644
--- a/api/api.go
+++ b/api/api.go
@@ -429,8 +429,9 @@ func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContex
func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
props := fgProps{}
- props.Name = proptools.StringPtr("all-modules-public-stubs-source")
- props.Device_common_srcs = createSrcs(modules, "{.public.stubs.source}")
+ props.Name = proptools.StringPtr("all-modules-public-stubs-source-exportable")
+ transformConfigurableArray(modules, "", ".stubs.source")
+ props.Device_common_srcs = createSrcs(modules, "{.exportable}")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(android.FileGroupFactory, &props)
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 14e238768f41..b43905b19239 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -451,7 +451,7 @@ public:
auto token = SurfaceComposerClient::getPhysicalDisplayToken(
event.header.displayId);
- auto firstDisplay = mBootAnimation->mDisplays.front();
+ auto& firstDisplay = mBootAnimation->mDisplays.front();
if (token != firstDisplay.displayToken) {
// ignore hotplug of a secondary display
continue;
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index 3c0e118bbfe7..57ae3548123b 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -17,6 +17,7 @@
#include "idmap2/ResourceContainer.h"
#include <memory>
+#include <mutex>
#include <string>
#include <utility>
#include <vector>
@@ -296,7 +297,7 @@ struct ResState {
} // namespace
struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer {
- static Result<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path);
+ static Result<std::unique_ptr<ApkResourceContainer>> FromPath(std::string path);
// inherited from TargetResourceContainer
Result<bool> DefinesOverlayable() const override;
@@ -320,6 +321,7 @@ struct ApkResourceContainer : public TargetResourceContainer, public OverlayReso
Result<const ResState*> GetState() const;
ZipAssetsProvider* GetZipAssets() const;
+ mutable std::mutex state_lock_;
mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_;
std::string path_;
};
@@ -330,16 +332,17 @@ ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zi
}
Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath(
- const std::string& path) {
+ std::string path) {
auto zip_assets = ZipAssetsProvider::Create(path, 0 /* flags */);
if (zip_assets == nullptr) {
return Error("failed to load zip assets");
}
return std::unique_ptr<ApkResourceContainer>(
- new ApkResourceContainer(std::move(zip_assets), path));
+ new ApkResourceContainer(std::move(zip_assets), std::move(path)));
}
Result<const ResState*> ApkResourceContainer::GetState() const {
+ std::lock_guard lock(state_lock_);
if (auto state = std::get_if<ResState>(&state_); state != nullptr) {
return state;
}
@@ -355,6 +358,7 @@ Result<const ResState*> ApkResourceContainer::GetState() const {
}
ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const {
+ std::lock_guard lock(state_lock_);
if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) {
return zip->get();
}
@@ -427,7 +431,7 @@ Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const {
Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath(
std::string path) {
- auto result = ApkResourceContainer::FromPath(path);
+ auto result = ApkResourceContainer::FromPath(std::move(path));
if (!result) {
return result.GetError();
}
@@ -438,7 +442,7 @@ Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::From
std::string path) {
// Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay.
if (android::IsFabricatedOverlay(path)) {
- auto result = FabricatedOverlayContainer::FromPath(path);
+ auto result = FabricatedOverlayContainer::FromPath(std::move(path));
if (!result) {
return result.GetError();
}
@@ -446,7 +450,7 @@ Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::From
}
// Fallback to loading the container as an APK.
- auto result = ApkResourceContainer::FromPath(path);
+ auto result = ApkResourceContainer::FromPath(std::move(path));
if (!result) {
return result.GetError();
}
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index a413bbd68f60..16f069394639 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -1,13 +1,16 @@
android.content.AsyncTaskLoader$LoadTask
+android.media.MediaCodecInfo$CodecCapabilities$FeatureList
android.net.ConnectivityThread$Singleton
android.os.FileObserver
android.os.NullVibrator
+android.permission.PermissionManager
+android.provider.MediaStore
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
+android.view.HdrRenderState
android.widget.Magnifier
+com.android.internal.jank.InteractionJankMonitor$InstanceHolder
+com.android.internal.util.LatencyTracker$SLatencyTrackerHolder
gov.nist.core.net.DefaultNetworkLayer
-android.net.rtp.AudioGroup
-android.net.rtp.AudioStream
-android.net.rtp.RtpStream
java.util.concurrent.ThreadLocalRandom
java.util.ImmutableCollections
-com.android.internal.jank.InteractionJankMonitor$InstanceHolder
+sun.nio.fs.UnixChannelFactory
diff --git a/core/api/current.txt b/core/api/current.txt
index 2cb7a30eb28c..dc7ccd4efaa9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -241,6 +241,7 @@ package android {
field public static final String PROVIDE_REMOTE_CREDENTIALS = "android.permission.PROVIDE_REMOTE_CREDENTIALS";
field @FlaggedApi("android.security.aapm_api") public static final String QUERY_ADVANCED_PROTECTION_MODE = "android.permission.QUERY_ADVANCED_PROTECTION_MODE";
field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
+ field @FlaggedApi("android.permission.flags.ranging_permission_enabled") public static final String RANGING = "android.permission.RANGING";
field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA";
field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE";
field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
@@ -262,6 +263,7 @@ package android {
field public static final String READ_SMS = "android.permission.READ_SMS";
field public static final String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
field public static final String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
+ field @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final String READ_SYSTEM_PREFERENCES = "android.permission.READ_SYSTEM_PREFERENCES";
field public static final String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
field public static final String REBOOT = "android.permission.REBOOT";
field public static final String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
@@ -312,6 +314,7 @@ package android {
field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
field public static final String TURN_SCREEN_ON = "android.permission.TURN_SCREEN_ON";
+ field @FlaggedApi("android.app.enable_tv_implicit_enter_pip_restriction") public static final String TV_IMPLICIT_ENTER_PIP = "android.permission.TV_IMPLICIT_ENTER_PIP";
field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION";
@@ -333,6 +336,7 @@ package android {
field public static final String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
field public static final String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
field public static final String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
+ field @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final String WRITE_SYSTEM_PREFERENCES = "android.permission.WRITE_SYSTEM_PREFERENCES";
field public static final String WRITE_VOICEMAIL = "com.android.voicemail.permission.WRITE_VOICEMAIL";
}
@@ -474,6 +478,8 @@ package android {
field public static final int alpha = 16843551; // 0x101031f
field public static final int alphabeticModifiers = 16844110; // 0x101054e
field public static final int alphabeticShortcut = 16843235; // 0x10101e3
+ field @FlaggedApi("android.content.pm.change_launcher_badging") public static final int alternateLauncherIcons;
+ field @FlaggedApi("android.content.pm.change_launcher_badging") public static final int alternateLauncherLabels;
field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
field @Deprecated public static final int amPmBackgroundColor = 16843941; // 0x10104a5
@@ -1074,6 +1080,7 @@ package android {
field public static final int layout = 16842994; // 0x10100f2
field public static final int layoutAnimation = 16842988; // 0x10100ec
field public static final int layoutDirection = 16843698; // 0x10103b2
+ field @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") public static final int layoutLabel;
field public static final int layoutMode = 16843738; // 0x10103da
field public static final int layout_above = 16843140; // 0x1010184
field public static final int layout_alignBaseline = 16843142; // 0x1010186
@@ -8023,6 +8030,7 @@ package android.app.admin {
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String CONTENT_PROTECTION_POLICY = "contentProtection";
field public static final String KEYGUARD_DISABLED_FEATURES_POLICY = "keyguardDisabledFeatures";
field public static final String LOCK_TASK_POLICY = "lockTask";
+ field @FlaggedApi("android.app.admin.flags.set_mte_policy_coexistence") public static final String MEMORY_TAGGING_POLICY = "memoryTagging";
field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
field public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
@@ -8069,6 +8077,7 @@ package android.app.admin {
method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@Nullable android.content.ComponentName);
method @Deprecated public boolean getAutoTimeRequired();
method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeZoneEnabled(@Nullable android.content.ComponentName);
+ method @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public int getAutoTimeZonePolicy();
method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public boolean getCameraDisabled(@Nullable android.content.ComponentName);
@@ -8226,6 +8235,7 @@ package android.app.admin {
method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public void setAutoTimeEnabled(@Nullable android.content.ComponentName, boolean);
method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean);
method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZoneEnabled(@Nullable android.content.ComponentName, boolean);
+ method @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZonePolicy(int);
method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public void setCameraDisabled(@Nullable android.content.ComponentName, boolean);
@@ -8344,6 +8354,9 @@ package android.app.admin {
field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_DISABLED = 1; // 0x1
+ field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_ENABLED = 2; // 0x2
+ field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_DISABLED = 1; // 0x1
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_ENABLED = 2; // 0x2
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; // 0x0
@@ -8784,8 +8797,31 @@ package android.app.admin {
package android.app.appfunctions {
+ @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionException extends java.lang.Exception implements android.os.Parcelable {
+ ctor public AppFunctionException(int, @Nullable String);
+ ctor public AppFunctionException(int, @Nullable String, @NonNull android.os.Bundle);
+ method public int describeContents();
+ method public int getErrorCategory();
+ method public int getErrorCode();
+ method @Nullable public String getErrorMessage();
+ method @NonNull public android.os.Bundle getExtras();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.AppFunctionException> CREATOR;
+ field public static final int ERROR_APP_UNKNOWN_ERROR = 3000; // 0xbb8
+ field public static final int ERROR_CANCELLED = 2001; // 0x7d1
+ field public static final int ERROR_CATEGORY_APP = 3; // 0x3
+ field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
+ field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
+ field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
+ field public static final int ERROR_DENIED = 1000; // 0x3e8
+ field public static final int ERROR_DISABLED = 1002; // 0x3ea
+ field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
+ field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
+ field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
+ }
+
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
- method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+ method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
@@ -8797,7 +8833,7 @@ package android.app.appfunctions {
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
ctor public AppFunctionService();
method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+ method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
}
@@ -8819,30 +8855,14 @@ package android.app.appfunctions {
}
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
+ ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument);
+ ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle);
method public int describeContents();
- method public int getErrorCategory();
- method @Nullable public String getErrorMessage();
method @NonNull public android.os.Bundle getExtras();
- method public int getResultCode();
method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
- method public boolean isSuccess();
- method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
- method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
- field public static final int ERROR_CATEGORY_APP = 3; // 0x3
- field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
- field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
- field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
- field public static final String PROPERTY_RETURN_VALUE = "returnValue";
- field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
- field public static final int RESULT_CANCELLED = 2001; // 0x7d1
- field public static final int RESULT_DENIED = 1000; // 0x3e8
- field public static final int RESULT_DISABLED = 1002; // 0x3ea
- field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
- field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
- field public static final int RESULT_OK = 0; // 0x0
- field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
+ field public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue";
}
}
@@ -8865,6 +8885,7 @@ package android.app.assist {
method public void setWebUri(android.net.Uri);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXTRA_APP_FUNCTION_DATA = "android.app.assist.extra.APP_FUNCTION_DATA";
}
public class AssistStructure implements android.os.Parcelable {
@@ -9093,6 +9114,46 @@ package android.app.blob {
}
+package android.app.jank {
+
+ @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats {
+ ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.FrameOverrunHistogram);
+ method @NonNull public android.app.jank.FrameOverrunHistogram getFrameOverrunHistogram();
+ method public long getJankyFrameCount();
+ method public long getTotalFrameCount();
+ method public int getUid();
+ method @NonNull public String getWidgetCategory();
+ method @NonNull public String getWidgetId();
+ method @NonNull public String getWidgetState();
+ field public static final String ANIMATING = "animating";
+ field public static final String ANIMATION = "animation";
+ field public static final String DRAGGING = "dragging";
+ field public static final String FLINGING = "flinging";
+ field public static final String KEYBOARD = "keyboard";
+ field public static final String MEDIA = "media";
+ field public static final String NAVIGATION = "navigation";
+ field public static final String NONE = "none";
+ field public static final String OTHER = "other";
+ field public static final String PLAYBACK = "playback";
+ field public static final String PREDICTIVE_BACK = "predictive_back";
+ field public static final String SCROLL = "scroll";
+ field public static final String SCROLLING = "scrolling";
+ field public static final String SWIPING = "swiping";
+ field public static final String TAPPING = "tapping";
+ field public static final String WIDGET_CATEGORY_UNSPECIFIED = "widget_category_unspecified";
+ field public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
+ field public static final String ZOOMING = "zooming";
+ }
+
+ @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class FrameOverrunHistogram {
+ ctor public FrameOverrunHistogram();
+ method public void addFrameOverrunMillis(int);
+ method @NonNull public int[] getBucketCounters();
+ method @NonNull public int[] getBucketEndpointsMillis();
+ }
+
+}
+
package android.app.job {
public class JobInfo implements android.os.Parcelable {
@@ -9245,6 +9306,8 @@ package android.app.job {
method @Nullable public String getNamespace();
method @Nullable public abstract android.app.job.JobInfo getPendingJob(int);
method public int getPendingJobReason(int);
+ method @FlaggedApi("android.app.job.get_pending_job_reasons_api") @NonNull public int[] getPendingJobReasons(int);
+ method @FlaggedApi("android.app.job.get_pending_job_reasons_history_api") @NonNull public java.util.List<android.app.job.PendingJobReasonsInfo> getPendingJobReasonsHistory(int);
method @NonNull public java.util.Map<java.lang.String,java.util.List<android.app.job.JobInfo>> getPendingJobsInAllNamespaces();
method public abstract int schedule(@NonNull android.app.job.JobInfo);
field public static final int PENDING_JOB_REASON_APP = 1; // 0x1
@@ -9254,6 +9317,7 @@ package android.app.job {
field public static final int PENDING_JOB_REASON_CONSTRAINT_CHARGING = 5; // 0x5
field public static final int PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY = 6; // 0x6
field public static final int PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER = 7; // 0x7
+ field @FlaggedApi("android.app.job.get_pending_job_reasons_api") public static final int PENDING_JOB_REASON_CONSTRAINT_DEADLINE = 16; // 0x10
field public static final int PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE = 8; // 0x8
field public static final int PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY = 9; // 0x9
field public static final int PENDING_JOB_REASON_CONSTRAINT_PREFETCH = 10; // 0xa
@@ -9322,6 +9386,15 @@ package android.app.job {
method @NonNull public android.app.job.JobWorkItem.Builder setMinimumNetworkChunkBytes(long);
}
+ @FlaggedApi("android.app.job.get_pending_job_reasons_history_api") public final class PendingJobReasonsInfo implements android.os.Parcelable {
+ ctor public PendingJobReasonsInfo(long, @NonNull int[]);
+ method public int describeContents();
+ method @NonNull public int[] getPendingJobReasons();
+ method public long getTimestampMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.job.PendingJobReasonsInfo> CREATOR;
+ }
+
}
package android.app.people {
@@ -9687,10 +9760,8 @@ package android.app.wallpaper {
method @Nullable public String getId();
method @Nullable public android.net.Uri getThumbnail();
method @Nullable public CharSequence getTitle();
- method @NonNull public static android.app.wallpaper.WallpaperDescription readFromStream(@NonNull java.io.InputStream) throws java.io.IOException;
method @NonNull public android.app.wallpaper.WallpaperDescription.Builder toBuilder();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException;
field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpaper.WallpaperDescription> CREATOR;
}
@@ -12732,6 +12803,7 @@ package android.content.pm {
method public abstract void onPackagesUnavailable(String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(String[], android.os.UserHandle);
method public void onShortcutsChanged(@NonNull String, @NonNull java.util.List<android.content.pm.ShortcutInfo>, @NonNull android.os.UserHandle);
+ method @FlaggedApi("android.multiuser.add_launcher_user_config") public void onUserConfigChanged(@NonNull android.content.pm.LauncherUserInfo);
}
public static final class LauncherApps.PinItemRequest implements android.os.Parcelable {
@@ -12767,10 +12839,12 @@ package android.content.pm {
@FlaggedApi("android.os.allow_private_profile") public final class LauncherUserInfo implements android.os.Parcelable {
method @FlaggedApi("android.os.allow_private_profile") public int describeContents();
+ method @FlaggedApi("android.multiuser.add_launcher_user_config") @NonNull public android.os.Bundle getUserConfig();
method @FlaggedApi("android.os.allow_private_profile") public int getUserSerialNumber();
method @FlaggedApi("android.os.allow_private_profile") @NonNull public String getUserType();
method @FlaggedApi("android.os.allow_private_profile") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.os.allow_private_profile") @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherUserInfo> CREATOR;
+ field @FlaggedApi("android.multiuser.add_launcher_user_config") public static final String PRIVATE_SPACE_ENTRYPOINT_HIDDEN = "private_space_entrypoint_hidden";
}
public final class ModuleInfo implements android.os.Parcelable {
@@ -13010,6 +13084,7 @@ package android.content.pm {
method public boolean hasParentSessionId();
method public boolean isActive();
method public boolean isApplicationEnabledSettingPersistent();
+ method @FlaggedApi("android.content.pm.sdk_dependency_installer") public boolean isAutoInstallDependenciesEnabled();
method public boolean isCommitted();
method public boolean isMultiPackage();
method public boolean isPreApprovalRequested();
@@ -13045,6 +13120,7 @@ package android.content.pm {
method public void setApplicationEnabledSettingPersistent();
method @Deprecated public void setAutoRevokePermissionsMode(boolean);
method public void setDontKillApp(boolean);
+ method @FlaggedApi("android.content.pm.sdk_dependency_installer") public void setEnableAutoInstallDependencies(boolean);
method public void setInstallLocation(int);
method public void setInstallReason(int);
method public void setInstallScenario(int);
@@ -13701,7 +13777,7 @@ package android.content.pm {
field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40
- field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
+ field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING, android.Manifest.permission.RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100
field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
@@ -16418,6 +16494,7 @@ package android.graphics {
field public static final int FLEX_RGBA_8888 = 42; // 0x2a
field public static final int FLEX_RGB_888 = 41; // 0x29
field public static final int HEIC = 1212500294; // 0x48454946
+ field @FlaggedApi("com.android.internal.camera.flags.camera_heif_gainmap") public static final int HEIC_ULTRAHDR = 4102; // 0x1006
field public static final int JPEG = 256; // 0x100
field public static final int JPEG_R = 4101; // 0x1005
field public static final int NV16 = 16; // 0x10
@@ -16844,6 +16921,7 @@ package android.graphics {
field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int TEXT_RUN_FLAG_LEFT_EDGE = 8192; // 0x2000
field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int TEXT_RUN_FLAG_RIGHT_EDGE = 16384; // 0x4000
field public static final int UNDERLINE_TEXT_FLAG = 8; // 0x8
+ field @FlaggedApi("com.android.text.flags.vertical_text_layout") public static final int VERTICAL_TEXT_FLAG = 4096; // 0x1000
}
public enum Paint.Align {
@@ -18708,6 +18786,7 @@ package android.hardware {
field public static final int DATASPACE_DISPLAY_P3 = 143261696; // 0x88a0000
field public static final int DATASPACE_DYNAMIC_DEPTH = 4098; // 0x1002
field public static final int DATASPACE_HEIF = 4100; // 0x1004
+ field @FlaggedApi("com.android.internal.camera.flags.camera_heif_gainmap") public static final int DATASPACE_HEIF_ULTRAHDR = 4102; // 0x1006
field public static final int DATASPACE_JFIF = 146931712; // 0x8c20000
field public static final int DATASPACE_JPEG_R = 4101; // 0x1005
field public static final int DATASPACE_SCRGB = 411107328; // 0x18810000
@@ -19351,6 +19430,7 @@ package android.hardware.camera2 {
field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
+ field @FlaggedApi("com.android.internal.camera.flags.ae_priority") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_PRIORITY_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
@@ -19668,6 +19748,9 @@ package android.hardware.camera2 {
field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2
field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0
field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1
+ field @FlaggedApi("com.android.internal.camera.flags.ae_priority") public static final int CONTROL_AE_PRIORITY_MODE_OFF = 0; // 0x0
+ field @FlaggedApi("com.android.internal.camera.flags.ae_priority") public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY = 2; // 0x2
+ field @FlaggedApi("com.android.internal.camera.flags.ae_priority") public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY = 1; // 0x1
field public static final int CONTROL_AE_STATE_CONVERGED = 2; // 0x2
field public static final int CONTROL_AE_STATE_FLASH_REQUIRED = 4; // 0x4
field public static final int CONTROL_AE_STATE_INACTIVE = 0; // 0x0
@@ -19761,6 +19844,8 @@ package android.hardware.camera2 {
field public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; // 0x0
field public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; // 0x1
field public static final int CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION = 2; // 0x2
+ field @FlaggedApi("com.android.internal.camera.flags.zoom_method") public static final int CONTROL_ZOOM_METHOD_AUTO = 0; // 0x0
+ field @FlaggedApi("com.android.internal.camera.flags.zoom_method") public static final int CONTROL_ZOOM_METHOD_ZOOM_RATIO = 1; // 0x1
field public static final int DISTORTION_CORRECTION_MODE_FAST = 1; // 0x1
field public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int DISTORTION_CORRECTION_MODE_OFF = 0; // 0x0
@@ -19768,6 +19853,9 @@ package android.hardware.camera2 {
field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int EDGE_MODE_OFF = 0; // 0x0
field public static final int EDGE_MODE_ZERO_SHUTTER_LAG = 3; // 0x3
+ field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") public static final int EXTENSION_NIGHT_MODE_INDICATOR_OFF = 1; // 0x1
+ field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") public static final int EXTENSION_NIGHT_MODE_INDICATOR_ON = 2; // 0x2
+ field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") public static final int EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN = 0; // 0x0
field public static final int FLASH_MODE_OFF = 0; // 0x0
field public static final int FLASH_MODE_SINGLE = 1; // 0x1
field public static final int FLASH_MODE_TORCH = 2; // 0x2
@@ -19947,6 +20035,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> CONTROL_AE_LOCK;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AE_MODE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AE_PRECAPTURE_TRIGGER;
+ field @FlaggedApi("com.android.internal.camera.flags.ae_priority") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AE_PRIORITY_MODE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_TARGET_FPS_RANGE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AF_MODE;
@@ -19965,6 +20054,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SETTINGS_OVERRIDE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.zoom_method") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_ZOOM_METHOD;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> CONTROL_ZOOM_RATIO;
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.CaptureRequest> CREATOR;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE;
@@ -20039,6 +20129,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_AE_LOCK;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_PRECAPTURE_TRIGGER;
+ field @FlaggedApi("com.android.internal.camera.flags.ae_priority") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_PRIORITY_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_STATE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_TARGET_FPS_RANGE;
@@ -20063,10 +20154,12 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SETTINGS_OVERRIDE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.zoom_method") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_ZOOM_METHOD;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> CONTROL_ZOOM_RATIO;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EDGE_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_CURRENT_TYPE;
+ field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_NIGHT_MODE_INDICATOR;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_STRENGTH;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_MODE;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_STATE;
@@ -20884,6 +20977,8 @@ package android.hardware.usb {
method public boolean hasPermission(android.hardware.usb.UsbDevice);
method public boolean hasPermission(android.hardware.usb.UsbAccessory);
method public android.os.ParcelFileDescriptor openAccessory(android.hardware.usb.UsbAccessory);
+ method @FlaggedApi("android.hardware.usb.flags.enable_accessory_stream_api") @NonNull public java.io.InputStream openAccessoryInputStream(@NonNull android.hardware.usb.UsbAccessory);
+ method @FlaggedApi("android.hardware.usb.flags.enable_accessory_stream_api") @NonNull public java.io.OutputStream openAccessoryOutputStream(@NonNull android.hardware.usb.UsbAccessory);
method public android.hardware.usb.UsbDeviceConnection openDevice(android.hardware.usb.UsbDevice);
method public void requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent);
method public void requestPermission(android.hardware.usb.UsbAccessory, android.app.PendingIntent);
@@ -22873,6 +22968,7 @@ package android.media {
method public void onCryptoError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CryptoException);
method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException);
method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int);
+ method @FlaggedApi("android.media.codec.subsession_metrics") public void onMetricsFlushed(@NonNull android.media.MediaCodec, @NonNull android.os.PersistableBundle);
method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo);
method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void onOutputBuffersAvailable(@NonNull android.media.MediaCodec, int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>);
method public abstract void onOutputFormatChanged(@NonNull android.media.MediaCodec, @NonNull android.media.MediaFormat);
@@ -23964,6 +24060,7 @@ package android.media {
field public static final String KEY_MPEGH_COMPATIBLE_SETS = "mpegh-compatible-sets";
field public static final String KEY_MPEGH_PROFILE_LEVEL_INDICATION = "mpegh-profile-level-indication";
field public static final String KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT = "mpegh-reference-channel-layout";
+ field @FlaggedApi("android.media.codec.num_input_slots") public static final String KEY_NUM_SLOTS = "num-slots";
field public static final String KEY_OPERATING_RATE = "operating-rate";
field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
field public static final String KEY_PCM_ENCODING = "pcm-encoding";
@@ -24660,6 +24757,7 @@ package android.media {
field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2; // 0x2
field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0; // 0x0
field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1; // 0x1
+ field @FlaggedApi("com.android.media.flags.enable_new_wired_media_route_2_info_types") public static final int TYPE_AUX_LINE = 19; // 0x13
field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
@@ -24669,6 +24767,8 @@ package android.media {
field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_ARC = 10; // 0xa
field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_EARC = 29; // 0x1d
field public static final int TYPE_HEARING_AID = 23; // 0x17
+ field @FlaggedApi("com.android.media.flags.enable_new_wired_media_route_2_info_types") public static final int TYPE_LINE_ANALOG = 5; // 0x5
+ field @FlaggedApi("com.android.media.flags.enable_new_wired_media_route_2_info_types") public static final int TYPE_LINE_DIGITAL = 6; // 0x6
field @FlaggedApi("android.media.audio.enable_multichannel_group_device") public static final int TYPE_MULTICHANNEL_SPEAKER_GROUP = 32; // 0x20
field public static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003; // 0x3eb
field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0
@@ -32944,11 +33044,16 @@ package android.os {
public class Build {
ctor public Build();
+ method @FlaggedApi("android.os.api_for_backported_fixes") public static int getBackportedFixStatus(long);
method @NonNull public static java.util.List<android.os.Build.Partition> getFingerprintedPartitions();
method @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static int getMajorSdkVersion(int);
method @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static int getMinorSdkVersion(int);
method public static String getRadioVersion();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public static String getSerial();
+ field @FlaggedApi("android.os.api_for_backported_fixes") public static final int BACKPORTED_FIX_STATUS_FIXED = 1; // 0x1
+ field @FlaggedApi("android.os.api_for_backported_fixes") public static final int BACKPORTED_FIX_STATUS_NOT_APPLICABLE = 2; // 0x2
+ field @FlaggedApi("android.os.api_for_backported_fixes") public static final int BACKPORTED_FIX_STATUS_NOT_FIXED = 3; // 0x3
+ field @FlaggedApi("android.os.api_for_backported_fixes") public static final int BACKPORTED_FIX_STATUS_UNKNOWN = 0; // 0x0
field public static final String BOARD;
field public static final String BOOTLOADER;
field public static final String BRAND;
@@ -33965,12 +34070,14 @@ package android.os {
}
public final class PowerManager {
+ method @FlaggedApi("android.os.allow_thermal_thresholds_callback") public void addThermalHeadroomListener(@NonNull android.os.PowerManager.OnThermalHeadroomChangedListener);
+ method @FlaggedApi("android.os.allow_thermal_thresholds_callback") public void addThermalHeadroomListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalHeadroomChangedListener);
method public void addThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
method @Nullable public java.time.Duration getBatteryDischargePrediction();
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
- method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+ method @FloatRange(from=0.0f) public float getThermalHeadroom(@IntRange(from=0, to=60) int);
method @FlaggedApi("android.os.allow_thermal_headroom_thresholds") @NonNull public java.util.Map<java.lang.Integer,java.lang.Float> getThermalHeadroomThresholds();
method public boolean isAllowedInLowPowerStandby(int);
method public boolean isAllowedInLowPowerStandby(@NonNull String);
@@ -33988,6 +34095,7 @@ package android.os {
method public boolean isWakeLockLevelSupported(int);
method public android.os.PowerManager.WakeLock newWakeLock(int, String);
method @RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(@Nullable String);
+ method @FlaggedApi("android.os.allow_thermal_thresholds_callback") public void removeThermalHeadroomListener(@NonNull android.os.PowerManager.OnThermalHeadroomChangedListener);
method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
field @Deprecated @RequiresPermission(value=android.Manifest.permission.TURN_SCREEN_ON, conditional=true) public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
@@ -34020,6 +34128,10 @@ package android.os {
field public static final int THERMAL_STATUS_SHUTDOWN = 6; // 0x6
}
+ @FlaggedApi("android.os.allow_thermal_thresholds_callback") public static interface PowerManager.OnThermalHeadroomChangedListener {
+ method public void onThermalHeadroomChanged(@FloatRange(from=0.0f) float, @FloatRange(from=0.0f) float, @IntRange(from=0) int, @NonNull java.util.Map<java.lang.Integer,java.lang.Float>);
+ }
+
public static interface PowerManager.OnThermalStatusChangedListener {
method public void onThermalStatusChanged(int);
}
@@ -37712,6 +37824,7 @@ package android.provider {
field public static final String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
field public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
field @Deprecated public static final String ACTION_FINGERPRINT_ENROLL = "android.settings.FINGERPRINT_ENROLL";
+ field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_FIRST_DAY_OF_WEEK_SETTINGS = "android.settings.FIRST_DAY_OF_WEEK_SETTINGS";
field public static final String ACTION_HARD_KEYBOARD_SETTINGS = "android.settings.HARD_KEYBOARD_SETTINGS";
field public static final String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
field public static final String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS";
@@ -37732,6 +37845,7 @@ package android.provider {
field public static final String ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING = "android.settings.MANAGE_SUPERVISOR_RESTRICTED_SETTING";
field public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES";
field public static final String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
+ field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_MEASUREMENT_SYSTEM_SETTINGS = "android.settings.MEASUREMENT_SYSTEM_SETTINGS";
field public static final String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
field public static final String ACTION_NETWORK_OPERATOR_SETTINGS = "android.settings.NETWORK_OPERATOR_SETTINGS";
field public static final String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
@@ -37742,12 +37856,14 @@ package android.provider {
field public static final String ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS = "android.settings.NOTIFICATION_LISTENER_DETAIL_SETTINGS";
field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
+ field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_NUMBERING_SYSTEM_SETTINGS = "android.settings.NUMBERING_SYSTEM_SETTINGS";
field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = "android.settings.PROCESS_WIFI_EASY_CONNECT_URI";
field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS";
field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
field public static final String ACTION_REGIONAL_PREFERENCES_SETTINGS = "android.settings.REGIONAL_PREFERENCES_SETTINGS";
+ field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_REGION_SETTINGS = "android.settings.REGION_SETTINGS";
field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
field public static final String ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA";
field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL = "android.settings.REQUEST_MEDIA_ROUTING_CONTROL";
@@ -37763,6 +37879,7 @@ package android.provider {
field public static final String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
field @Deprecated public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
field public static final String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
+ field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_TEMPERATURE_UNIT_SETTINGS = "android.settings.TEMPERATURE_UNIT_SETTINGS";
field public static final String ACTION_USAGE_ACCESS_SETTINGS = "android.settings.USAGE_ACCESS_SETTINGS";
field public static final String ACTION_USER_DICTIONARY_SETTINGS = "android.settings.USER_DICTIONARY_SETTINGS";
field public static final String ACTION_VOICE_CONTROL_AIRPLANE_MODE = "android.settings.VOICE_CONTROL_AIRPLANE_MODE";
@@ -40347,7 +40464,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyProtection.Builder setUserPresenceRequired(boolean);
}
- @FlaggedApi("android.security.keystore_grant_api") public class KeyStoreManager {
+ @FlaggedApi("android.security.keystore_grant_api") public final class KeyStoreManager {
method @NonNull public java.util.List<java.security.cert.X509Certificate> getGrantedCertificateChainFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
method @NonNull public java.security.Key getGrantedKeyFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
method @NonNull public java.security.KeyPair getGrantedKeyPairFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
@@ -40590,7 +40707,7 @@ package android.service.autofill {
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
field public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 2; // 0x2
field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
- field public static final int FLAG_SUPPORTS_FILL_DIALOG = 64; // 0x40
+ field @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public static final int FLAG_SUPPORTS_FILL_DIALOG = 64; // 0x40
}
public final class FillResponse implements android.os.Parcelable {
@@ -41975,6 +42092,193 @@ package android.service.restrictions {
}
+package android.service.settings.preferences {
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class GetValueRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getPreferenceKey();
+ method @NonNull public String getScreenKey();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.GetValueRequest> CREATOR;
+ }
+
+ public static final class GetValueRequest.Builder {
+ ctor public GetValueRequest.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.service.settings.preferences.GetValueRequest build();
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class GetValueResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.service.settings.preferences.SettingsPreferenceMetadata getMetadata();
+ method public int getResultCode();
+ method @Nullable public android.service.settings.preferences.SettingsPreferenceValue getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.GetValueResult> CREATOR;
+ field public static final int RESULT_DISALLOW = 4; // 0x4
+ field public static final int RESULT_INTERNAL_ERROR = 6; // 0x6
+ field public static final int RESULT_INVALID_REQUEST = 5; // 0x5
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_REQUIRE_APP_PERMISSION = 3; // 0x3
+ field public static final int RESULT_UNAVAILABLE = 2; // 0x2
+ field public static final int RESULT_UNSUPPORTED = 1; // 0x1
+ }
+
+ public static final class GetValueResult.Builder {
+ ctor public GetValueResult.Builder(int);
+ method @NonNull public android.service.settings.preferences.GetValueResult build();
+ method @NonNull public android.service.settings.preferences.GetValueResult.Builder setMetadata(@Nullable android.service.settings.preferences.SettingsPreferenceMetadata);
+ method @NonNull public android.service.settings.preferences.GetValueResult.Builder setValue(@Nullable android.service.settings.preferences.SettingsPreferenceValue);
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class MetadataRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.MetadataRequest> CREATOR;
+ }
+
+ public static final class MetadataRequest.Builder {
+ ctor public MetadataRequest.Builder();
+ method @NonNull public android.service.settings.preferences.MetadataRequest build();
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class MetadataResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.service.settings.preferences.SettingsPreferenceMetadata> getMetadataList();
+ method public int getResultCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.MetadataResult> CREATOR;
+ field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_UNSUPPORTED = 1; // 0x1
+ }
+
+ public static final class MetadataResult.Builder {
+ ctor public MetadataResult.Builder(int);
+ method @NonNull public android.service.settings.preferences.MetadataResult build();
+ method @NonNull public android.service.settings.preferences.MetadataResult.Builder setMetadataList(@NonNull java.util.List<android.service.settings.preferences.SettingsPreferenceMetadata>);
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SetValueRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getPreferenceKey();
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceValue getPreferenceValue();
+ method @NonNull public String getScreenKey();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SetValueRequest> CREATOR;
+ }
+
+ public static final class SetValueRequest.Builder {
+ ctor public SetValueRequest.Builder(@NonNull String, @NonNull String, @NonNull android.service.settings.preferences.SettingsPreferenceValue);
+ method @NonNull public android.service.settings.preferences.SetValueRequest build();
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SetValueResult implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getResultCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SetValueResult> CREATOR;
+ field public static final int RESULT_DISABLED = 2; // 0x2
+ field public static final int RESULT_DISALLOW = 7; // 0x7
+ field public static final int RESULT_INTERNAL_ERROR = 9; // 0x9
+ field public static final int RESULT_INVALID_REQUEST = 8; // 0x8
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_REQUIRE_APP_PERMISSION = 5; // 0x5
+ field public static final int RESULT_REQUIRE_USER_CONSENT = 6; // 0x6
+ field public static final int RESULT_RESTRICTED = 3; // 0x3
+ field public static final int RESULT_UNAVAILABLE = 4; // 0x4
+ field public static final int RESULT_UNSUPPORTED = 1; // 0x1
+ }
+
+ public static final class SetValueResult.Builder {
+ ctor public SetValueResult.Builder(int);
+ method @NonNull public android.service.settings.preferences.SetValueResult build();
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceMetadata implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getBreadcrumbs();
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public String getKey();
+ method @Nullable public android.app.PendingIntent getLaunchIntent();
+ method @NonNull public java.util.List<java.lang.String> getReadPermissions();
+ method @NonNull public String getScreenKey();
+ method @Nullable public String getSummary();
+ method @Nullable public String getTitle();
+ method @NonNull public java.util.List<java.lang.String> getWritePermissions();
+ method public int getWriteSensitivity();
+ method public boolean isAvailable();
+ method public boolean isEnabled();
+ method public boolean isRestricted();
+ method public boolean isWritable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceMetadata> CREATOR;
+ field public static final int INTENT_ONLY = 2; // 0x2
+ field public static final int NOT_SENSITIVE = 0; // 0x0
+ field public static final int SENSITIVE = 1; // 0x1
+ }
+
+ public static final class SettingsPreferenceMetadata.Builder {
+ ctor public SettingsPreferenceMetadata.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata build();
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setAvailable(boolean);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setBreadcrumbs(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setEnabled(boolean);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setLaunchIntent(@Nullable android.app.PendingIntent);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setReadPermissions(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setRestricted(boolean);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setSummary(@Nullable String);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setTitle(@Nullable String);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWritable(boolean);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWritePermissions(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWriteSensitivity(int);
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public abstract class SettingsPreferenceService extends android.app.Service {
+ ctor public SettingsPreferenceService();
+ method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
+ method public abstract void onGetAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>);
+ method public abstract void onGetPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>);
+ method public abstract void onSetPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>);
+ field public static final String ACTION_PREFERENCE_SERVICE = "android.service.settings.preferences.action.PREFERENCE_SERVICE";
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable {
+ ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String);
+ method public void close();
+ method public void getAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>);
+ method public void getPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>);
+ method public void setPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>);
+ method public void start();
+ method public void stop();
+ }
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceValue implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getBooleanValue();
+ method public double getDoubleValue();
+ method public long getLongValue();
+ method @Nullable public String getStringValue();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceValue> CREATOR;
+ field public static final int TYPE_BOOLEAN = 0; // 0x0
+ field public static final int TYPE_DOUBLE = 2; // 0x2
+ field public static final int TYPE_LONG = 1; // 0x1
+ field public static final int TYPE_STRING = 3; // 0x3
+ }
+
+ public static final class SettingsPreferenceValue.Builder {
+ ctor public SettingsPreferenceValue.Builder(int);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceValue build();
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setBooleanValue(boolean);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setDoubleValue(double);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setLongValue(long);
+ method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setStringValue(@Nullable String);
+ }
+
+}
+
package android.service.textservice {
public abstract class SpellCheckerService extends android.app.Service {
@@ -44080,6 +44384,8 @@ package android.telephony {
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int CARRIER_ROAMING_NTN_CONNECT_MANUAL = 1; // 0x1
field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0; // 0x0
field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1; // 0x1
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
@@ -44150,11 +44456,14 @@ package android.telephony {
field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array";
field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool";
field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT = "carrier_roaming_ntn_connect_type_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT = "carrier_roaming_ntn_emergency_call_to_satellite_handover_type_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY = "carrier_roaming_satellite_default_services_int_array";
field public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT = "carrier_supported_satellite_notification_hysteresis_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
@@ -44205,6 +44514,7 @@ package android.telephony {
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
field public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = "disable_dun_apn_while_roaming_with_preset_apn_bool";
field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool";
field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
field public static final String KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL = "display_call_strength_indicator_bool";
@@ -44217,6 +44527,8 @@ package android.telephony {
field public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
field public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
field public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT = "emergency_call_to_satellite_t911_handover_timeout_millis_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL = "emergency_messaging_supported_bool";
field public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int";
field public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY = "emergency_number_prefix_string_array";
field public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL = "enable_cross_sim_calling_on_opportunistic_data_bool";
@@ -44263,6 +44575,7 @@ package android.telephony {
field public static final String KEY_MMS_MAX_IMAGE_HEIGHT_INT = "maxImageHeight";
field public static final String KEY_MMS_MAX_IMAGE_WIDTH_INT = "maxImageWidth";
field public static final String KEY_MMS_MAX_MESSAGE_SIZE_INT = "maxMessageSize";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT = "mms_max_ntn_payload_size_bytes_int";
field public static final String KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT = "maxMessageTextSize";
field public static final String KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL = "enableMMSDeliveryReports";
field public static final String KEY_MMS_MMS_ENABLED_BOOL = "enabledMMS";
@@ -44301,6 +44614,7 @@ package android.telephony {
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long";
field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL = "override_wfc_roaming_mode_while_using_ntn_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT = "parameters_used_for_ntn_lte_signal_bar_int";
field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -44319,6 +44633,7 @@ package android.telephony {
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array";
field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL = "remove_satellite_plmn_in_manual_network_scan_bool";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
field public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
@@ -44330,13 +44645,18 @@ package android.telephony {
field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT = "satellite_data_support_mode_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING = "satellite_entitlement_app_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING = "satellite_information_redirect_url_string";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING = "satellite_nidd_apn_name_string";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL = "satellite_roaming_turn_off_session_for_emergency_call_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -44406,6 +44726,9 @@ package android.telephony {
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_DATA_SUPPORT_ALL = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0; // 0x0
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
@@ -51775,6 +52098,7 @@ package android.view {
field public static final int KEYCODE_CHANNEL_DOWN = 167; // 0xa7
field public static final int KEYCODE_CHANNEL_UP = 166; // 0xa6
field public static final int KEYCODE_CLEAR = 28; // 0x1c
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_CLOSE = 321; // 0x141
field public static final int KEYCODE_COMMA = 55; // 0x37
field public static final int KEYCODE_CONTACTS = 207; // 0xcf
field public static final int KEYCODE_COPY = 278; // 0x116
@@ -51787,6 +52111,8 @@ package android.view {
field public static final int KEYCODE_DEMO_APP_2 = 302; // 0x12e
field public static final int KEYCODE_DEMO_APP_3 = 303; // 0x12f
field public static final int KEYCODE_DEMO_APP_4 = 304; // 0x130
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_DICTATE = 319; // 0x13f
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_DO_NOT_DISTURB = 322; // 0x142
field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
field public static final int KEYCODE_DPAD_DOWN = 20; // 0x14
field public static final int KEYCODE_DPAD_DOWN_LEFT = 269; // 0x10d
@@ -51799,7 +52125,7 @@ package android.view {
field public static final int KEYCODE_DVR = 173; // 0xad
field public static final int KEYCODE_E = 33; // 0x21
field public static final int KEYCODE_EISU = 212; // 0xd4
- field @FlaggedApi("com.android.hardware.input.emoji_and_screenshot_keycodes_available") public static final int KEYCODE_EMOJI_PICKER = 317; // 0x13d
+ field public static final int KEYCODE_EMOJI_PICKER = 317; // 0x13d
field public static final int KEYCODE_ENDCALL = 6; // 0x6
field public static final int KEYCODE_ENTER = 66; // 0x42
field public static final int KEYCODE_ENVELOPE = 65; // 0x41
@@ -51811,7 +52137,19 @@ package android.view {
field public static final int KEYCODE_F10 = 140; // 0x8c
field public static final int KEYCODE_F11 = 141; // 0x8d
field public static final int KEYCODE_F12 = 142; // 0x8e
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F13 = 326; // 0x146
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F14 = 327; // 0x147
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F15 = 328; // 0x148
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F16 = 329; // 0x149
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F17 = 330; // 0x14a
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F18 = 331; // 0x14b
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F19 = 332; // 0x14c
field public static final int KEYCODE_F2 = 132; // 0x84
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F20 = 333; // 0x14d
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F21 = 334; // 0x14e
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F22 = 335; // 0x14f
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F23 = 336; // 0x150
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F24 = 337; // 0x151
field public static final int KEYCODE_F3 = 133; // 0x85
field public static final int KEYCODE_F4 = 134; // 0x86
field public static final int KEYCODE_F5 = 135; // 0x87
@@ -51826,6 +52164,7 @@ package android.view {
field public static final int KEYCODE_FOCUS = 80; // 0x50
field public static final int KEYCODE_FORWARD = 125; // 0x7d
field public static final int KEYCODE_FORWARD_DEL = 112; // 0x70
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_FULLSCREEN = 325; // 0x145
field public static final int KEYCODE_FUNCTION = 119; // 0x77
field public static final int KEYCODE_G = 35; // 0x23
field public static final int KEYCODE_GRAVE = 68; // 0x44
@@ -51849,6 +52188,7 @@ package android.view {
field public static final int KEYCODE_LANGUAGE_SWITCH = 204; // 0xcc
field public static final int KEYCODE_LAST_CHANNEL = 229; // 0xe5
field public static final int KEYCODE_LEFT_BRACKET = 71; // 0x47
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_LOCK = 324; // 0x144
field public static final int KEYCODE_M = 41; // 0x29
field public static final int KEYCODE_MACRO_1 = 313; // 0x139
field public static final int KEYCODE_MACRO_2 = 314; // 0x13a
@@ -51886,6 +52226,7 @@ package android.view {
field public static final int KEYCODE_NAVIGATE_NEXT = 261; // 0x105
field public static final int KEYCODE_NAVIGATE_OUT = 263; // 0x107
field public static final int KEYCODE_NAVIGATE_PREVIOUS = 260; // 0x104
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_NEW = 320; // 0x140
field public static final int KEYCODE_NOTIFICATION = 83; // 0x53
field public static final int KEYCODE_NUM = 78; // 0x4e
field public static final int KEYCODE_NUMPAD_0 = 144; // 0x90
@@ -51920,6 +52261,7 @@ package android.view {
field public static final int KEYCODE_PLUS = 81; // 0x51
field public static final int KEYCODE_POUND = 18; // 0x12
field public static final int KEYCODE_POWER = 26; // 0x1a
+ field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_PRINT = 323; // 0x143
field public static final int KEYCODE_PROFILE_SWITCH = 288; // 0x120
field public static final int KEYCODE_PROG_BLUE = 186; // 0xba
field public static final int KEYCODE_PROG_GREEN = 184; // 0xb8
@@ -51932,7 +52274,7 @@ package android.view {
field public static final int KEYCODE_RIGHT_BRACKET = 72; // 0x48
field public static final int KEYCODE_RO = 217; // 0xd9
field public static final int KEYCODE_S = 47; // 0x2f
- field @FlaggedApi("com.android.hardware.input.emoji_and_screenshot_keycodes_available") public static final int KEYCODE_SCREENSHOT = 318; // 0x13e
+ field public static final int KEYCODE_SCREENSHOT = 318; // 0x13e
field public static final int KEYCODE_SCROLL_LOCK = 116; // 0x74
field public static final int KEYCODE_SEARCH = 84; // 0x54
field public static final int KEYCODE_SEMICOLON = 74; // 0x4a
@@ -52997,7 +53339,7 @@ package android.view {
method public void addOnUnhandledKeyEventListener(android.view.View.OnUnhandledKeyEventListener);
method public void addTouchables(java.util.ArrayList<android.view.View>);
method public android.view.ViewPropertyAnimator animate();
- method public void announceForAccessibility(CharSequence);
+ method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_accessibility_announcement_apis") public void announceForAccessibility(CharSequence);
method public void autofill(android.view.autofill.AutofillValue);
method public void autofill(@NonNull android.util.SparseArray<android.view.autofill.AutofillValue>);
method protected boolean awakenScrollBars();
@@ -53457,6 +53799,7 @@ package android.view {
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void removeOnUnhandledKeyEventListener(android.view.View.OnUnhandledKeyEventListener);
+ method @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public void reportAppJankStats(@NonNull android.app.jank.AppJankStats);
method public void requestApplyInsets();
method @Deprecated public void requestFitSystemWindows();
method public final boolean requestFocus();
@@ -53710,6 +54053,7 @@ package android.view {
field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_GLOBAL_SAME_APPLICATION = 4096; // 0x1000
field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
+ field @FlaggedApi("com.android.window.flags.supports_drag_assistant_to_multiwindow") public static final int DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START = 16384; // 0x4000
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 8192; // 0x2000
field @Deprecated public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
@@ -55247,7 +55591,7 @@ package android.view.accessibility {
field public static final int SPEECH_STATE_SPEAKING_END = 2; // 0x2
field public static final int SPEECH_STATE_SPEAKING_START = 1; // 0x1
field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
- field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+ field @Deprecated @FlaggedApi("android.view.accessibility.deprecate_accessibility_announcement_apis") public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
@@ -55386,8 +55730,8 @@ package android.view.accessibility {
method public android.os.Bundle getExtras();
method public CharSequence getHintText();
method public int getInputType();
- method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
- method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
+ method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_ani_label_for_apis") public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
+ method @Deprecated @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") @NonNull public java.util.List<android.view.accessibility.AccessibilityNodeInfo> getLabeledByList();
method public int getLiveRegion();
method public int getMaxTextLength();
@@ -55485,10 +55829,10 @@ package android.view.accessibility {
method public void setHintText(CharSequence);
method public void setImportantForAccessibility(boolean);
method public void setInputType(int);
- method public void setLabelFor(android.view.View);
- method public void setLabelFor(android.view.View, int);
- method public void setLabeledBy(android.view.View);
- method public void setLabeledBy(android.view.View, int);
+ method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_ani_label_for_apis") public void setLabelFor(android.view.View);
+ method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_ani_label_for_apis") public void setLabelFor(android.view.View, int);
+ method @Deprecated @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void setLabeledBy(android.view.View);
+ method @Deprecated @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void setLabeledBy(android.view.View, int);
method public void setLiveRegion(int);
method public void setLongClickable(boolean);
method public void setMaxTextLength(int);
@@ -55573,6 +55917,7 @@ package android.view.accessibility {
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field @FlaggedApi("android.view.accessibility.a11y_character_in_window_api") public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
@@ -56203,13 +56548,13 @@ package android.view.autofill {
method public void notifyViewExited(@NonNull android.view.View, int);
method public void notifyViewVisibilityChanged(@NonNull android.view.View, boolean);
method public void notifyViewVisibilityChanged(@NonNull android.view.View, int, boolean);
- method public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray<android.view.autofill.VirtualViewFillInfo>);
+ method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray<android.view.autofill.VirtualViewFillInfo>);
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
method public void setUserData(@Nullable android.service.autofill.UserData);
- method public boolean showAutofillDialog(@NonNull android.view.View);
- method public boolean showAutofillDialog(@NonNull android.view.View, int);
+ method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public boolean showAutofillDialog(@NonNull android.view.View);
+ method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public boolean showAutofillDialog(@NonNull android.view.View, int);
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
@@ -56975,6 +57320,9 @@ package android.view.inputmethod {
method public String getExtraValueOf(String);
method public int getIconResId();
method @NonNull public String getLanguageTag();
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public CharSequence getLayoutDisplayName(@NonNull android.content.Context, @NonNull android.content.pm.ApplicationInfo);
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public CharSequence getLayoutLabelNonLocalized();
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @StringRes public int getLayoutLabelResource();
method @Deprecated @NonNull public String getLocale();
method public String getMode();
method @NonNull public CharSequence getNameOverride();
@@ -56994,6 +57342,8 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(String);
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLayoutLabelNonLocalized(@NonNull CharSequence);
+ method @FlaggedApi("android.view.inputmethod.ime_switcher_revamp_api") @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLayoutLabelResource(@StringRes int);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
method @NonNull public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setPhysicalKeyboardHint(@Nullable android.icu.util.ULocale, @NonNull String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(String);
@@ -62048,6 +62398,11 @@ package android.window {
method public void markSyncReady();
}
+ @FlaggedApi("com.android.window.flags.predictive_back_system_override_callback") public final class SystemOnBackInvokedCallbacks {
+ method @FlaggedApi("com.android.window.flags.predictive_back_system_override_callback") @NonNull public static android.window.OnBackInvokedCallback finishAndRemoveTaskCallback(@NonNull android.app.Activity);
+ method @FlaggedApi("com.android.window.flags.predictive_back_system_override_callback") @NonNull public static android.window.OnBackInvokedCallback moveTaskToBackCallback(@NonNull android.app.Activity);
+ }
+
@FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public final class TrustedPresentationThresholds implements android.os.Parcelable {
ctor @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public int describeContents();
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1dcafd1d80ea..4ada53e1bf34 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -383,6 +383,36 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+FlaggedApiLiteral: android.Manifest.permission#BIND_APP_FUNCTION_SERVICE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER).
+FlaggedApiLiteral: android.Manifest.permission#BIND_TV_AD_SERVICE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW).
+FlaggedApiLiteral: android.Manifest.permission#MANAGE_DEVICE_POLICY_THREAD_NETWORK:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.net.thread.platform.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED).
+FlaggedApiLiteral: android.Manifest.permission#QUERY_ADVANCED_PROTECTION_MODE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.security.Flags.FLAG_AAPM_API).
+FlaggedApiLiteral: android.Manifest.permission#RANGING:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_RANGING_PERMISSION_ENABLED).
+FlaggedApiLiteral: android.Manifest.permission#REQUEST_OBSERVE_DEVICE_UUID_PRESENCE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.companion.Flags.FLAG_DEVICE_PRESENCE).
+FlaggedApiLiteral: android.R.attr#adServiceTypes:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW).
+FlaggedApiLiteral: android.R.attr#intentMatchingFlags:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.security.Flags.FLAG_ENABLE_INTENT_MATCHING_FLAGS).
+FlaggedApiLiteral: android.R.attr#languageSettingsActivity:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API).
+FlaggedApiLiteral: android.R.attr#optional:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_SDK_LIB_INDEPENDENCE).
+FlaggedApiLiteral: android.R.attr#supplementalDescription:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION).
+FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
+FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
+FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_SUCCESS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
+
+
InvalidNullabilityOverride: android.app.Notification.TvExtender#extend(android.app.Notification.Builder) parameter #0:
Invalid nullability on parameter `builder` in method `extend`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.media.midi.MidiUmpDeviceService#onBind(android.content.Intent) parameter #0:
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 047a0df164b1..ed95fdd52f45 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -46,6 +46,7 @@ package android {
field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BACKUP = "android.permission.BACKUP";
+ field @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") public static final String BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS = "android.permission.BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS";
field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
field public static final String BIND_AMBIENT_CONTEXT_DETECTION_SERVICE = "android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
@@ -138,6 +139,7 @@ package android {
field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS";
field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT";
+ field @FlaggedApi("com.android.art.flags.executable_method_file_offsets") public static final String DYNAMIC_INSTRUMENTATION = "android.permission.DYNAMIC_INSTRUMENTATION";
field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE";
field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES";
field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
@@ -346,6 +348,7 @@ package android {
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
+ field @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") public static final String RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS = "android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -389,6 +392,7 @@ package android {
field public static final String START_CROSS_PROFILE_ACTIVITIES = "android.permission.START_CROSS_PROFILE_ACTIVITIES";
field public static final String START_REVIEW_PERMISSION_DECISIONS = "android.permission.START_REVIEW_PERMISSION_DECISIONS";
field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
+ field @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final String START_VIBRATION_SESSIONS = "android.permission.START_VIBRATION_SESSIONS";
field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -696,6 +700,7 @@ package android.app {
field public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
+ field @FlaggedApi("android.permission.flags.ranging_permission_enabled") public static final String OPSTR_RANGING = "android:ranging";
field @FlaggedApi("android.view.contentprotection.flags.rapid_clear_notifications_by_listener_app_op_enabled") public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER = "android:rapid_clear_notifications_by_listener";
field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate";
@@ -704,7 +709,8 @@ package android.app {
field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
field public static final String OPSTR_READ_MEDIA_VISUAL_USER_SELECTED = "android:read_media_visual_user_selected";
- field @FlaggedApi("android.permission.flags.platform_skin_temperature_enabled") public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
+ field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_OXYGEN_SATURATION = "android:read_oxygen_saturation";
+ field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
field public static final String OPSTR_READ_WRITE_HEALTH_DATA = "android:read_write_health_data";
field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio";
field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
@@ -1266,13 +1272,21 @@ package android.app {
public class WallpaperManager {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
+ method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int);
+ method @FlaggedApi("android.app.customization_packs_apis") public static int getOrientation(@NonNull android.graphics.Point);
method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
+ method @FlaggedApi("android.app.customization_packs_apis") @Nullable public android.os.ParcelFileDescriptor getWallpaperFile(int, boolean);
method @FlaggedApi("android.app.live_wallpaper_content_handling") @Nullable @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.app.wallpaper.WallpaperInstance getWallpaperInstance(int);
method public void setDisplayOffset(android.os.IBinder, int, int);
+ method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(@NonNull java.io.InputStream, @NonNull android.util.SparseArray<android.graphics.Rect>, boolean, int) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(allOf={android.Manifest.permission.SET_WALLPAPER_COMPONENT, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public boolean setWallpaperComponentWithDescription(@NonNull android.app.wallpaper.WallpaperDescription, int);
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponentWithFlags(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float);
+ field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_LANDSCAPE = 1; // 0x1
+ field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_PORTRAIT = 0; // 0x0
+ field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_SQUARE_LANDSCAPE = 3; // 0x3
+ field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_SQUARE_PORTRAIT = 2; // 0x2
}
}
@@ -1354,6 +1368,7 @@ package android.app.admin {
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @FlaggedApi("android.app.admin.flags.remove_managed_profile_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean removeManagedProfile();
method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1363,7 +1378,8 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
- method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+ method @Deprecated @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+ method @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(boolean, @Nullable android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification();
field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
@@ -3558,10 +3574,12 @@ package android.companion.virtual {
method @Deprecated public int getDefaultActivityPolicy();
method @Deprecated public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getDimDuration();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent();
method public int getLockState();
method @Nullable public String getName();
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public java.time.Duration getScreenOffTimeout();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensorConfig> getVirtualSensorConfigs();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -3579,6 +3597,7 @@ package android.companion.virtual {
field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6; // 0x6
field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5
field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
+ field @FlaggedApi("android.companion.virtualdevice.flags.default_device_camera_access_policy") public static final int POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS = 7; // 0x7
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
}
@@ -3594,10 +3613,12 @@ package android.companion.virtual {
method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDimDuration(@NonNull java.time.Duration);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setScreenOffTimeout(@NonNull java.time.Duration);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorCallback);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorDirectChannelCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorDirectChannelCallback);
@@ -5288,13 +5309,19 @@ package android.hardware.display {
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
+ public abstract static class VirtualDisplay.Callback {
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void onRequestedBrightnessChanged(@FloatRange(from=0.0f, to=1.0f) float);
+ }
+
public final class VirtualDisplayConfig implements android.os.Parcelable {
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @FloatRange(from=0.0f, to=1.0f) public float getDefaultBrightness();
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
}
public static final class VirtualDisplayConfig.Builder {
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDefaultBrightness(@FloatRange(from=0.0f, to=1.0f) float);
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
@@ -7022,7 +7049,7 @@ package android.hardware.soundtrigger {
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAllowMultipleTriggers(boolean);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAudioCapabilities(int);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setCaptureRequested(boolean);
- method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@Nullable byte[]);
+ method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@NonNull byte[]);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setKeyphrases(@NonNull java.util.Collection<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
}
@@ -7234,6 +7261,7 @@ package android.media {
field @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public static final int USAGE_CALL_ASSISTANT = 17; // 0x11
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_EMERGENCY = 1000; // 0x3e8
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_SAFETY = 1001; // 0x3e9
+ field @FlaggedApi("android.media.audio.speaker_cleanup_usage") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_SPEAKER_CLEANUP = 1004; // 0x3ec
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_VEHICLE_STATUS = 1002; // 0x3ea
}
@@ -8219,6 +8247,26 @@ package android.media.tv {
method public void onSetMain(boolean);
}
+ @FlaggedApi("android.media.tv.flags.tif_extension_standardization") public final class TvInputServiceExtensionManager {
+ method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public int registerExtensionIBinder(@NonNull String, @NonNull android.os.IBinder);
+ field public static final String IBROADCAST_TIME = "android.media.tv.extension.time.BroadcastTime";
+ field public static final String ICAM_APP_INFO_SERVICE = "android.media.tv.extension.cam.ICamAppInfoService";
+ field public static final String ICLIENT_TOKEN = "android.media.tv.extension.clienttoken.IClientToken";
+ field public static final String IDATA_SERVICE_SIGNAL_INFO = "android.media.tv.extension.teletext.IDataServiceSignalInfo";
+ field public static final String IEVENT_MONITOR = "android.media.tv.extension.event.IEventMonitor";
+ field public static final String IHDMI_SIGNAL_INTERFACE = "android.media.tv.extension.signal.IHdmiSignalInterface";
+ field public static final String IOAD_UPDATE_INTERFACE = "android.media.tv.extension.oad.IOadUpdateInterface";
+ field public static final String IRATING_INTERFACE = "android.media.tv.extension.rating.IRatingInterface";
+ field public static final String IRECORDED_CONTENTS = "android.media.tv.extension.pvr.IRecordedContents";
+ field public static final String ISCAN_INTERFACE = "android.media.tv.extension.scan.IScanInterface";
+ field public static final String ISCREEN_MODE_SETTINGS = "android.media.tv.extension.screenmode.IScreenModeSettings";
+ field public static final String ISERVICE_LIST_EDIT_LISTENER = "android.media.tv.extension.servicedb.IServiceListEditListener";
+ field public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2; // 0x2
+ field public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1; // 0x1
+ field public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3; // 0x3
+ field public static final int REGISTER_SUCCESS = 0; // 0x0
+ }
+
public abstract static class TvRecordingClient.RecordingCallback {
method public void onEvent(String, String, android.os.Bundle);
}
@@ -11647,8 +11695,11 @@ package android.os {
public abstract class Vibrator {
method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+ method @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public boolean areVendorEffectsSupported();
+ method @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public boolean areVendorSessionsSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating();
method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+ method @FlaggedApi("android.os.vibrator.vendor_vibration_effects") @RequiresPermission(allOf={android.Manifest.permission.VIBRATE, android.Manifest.permission.VIBRATE_VENDOR_EFFECTS, android.Manifest.permission.START_VIBRATION_SESSIONS}) public void startVendorSession(@NonNull android.os.VibrationAttributes, @Nullable String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.vibrator.VendorVibrationSession.Callback);
}
public static interface Vibrator.OnVibratorStateChangedListener {
@@ -11800,6 +11851,28 @@ package android.os.storage {
}
+package android.os.vibrator {
+
+ @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public final class VendorVibrationSession implements java.lang.AutoCloseable {
+ method public void cancel();
+ method public void close();
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(@NonNull android.os.VibrationEffect, @Nullable String);
+ field public static final int STATUS_CANCELED = 4; // 0x4
+ field public static final int STATUS_IGNORED = 2; // 0x2
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ field public static final int STATUS_UNKNOWN_ERROR = 5; // 0x5
+ field public static final int STATUS_UNSUPPORTED = 3; // 0x3
+ }
+
+ public static interface VendorVibrationSession.Callback {
+ method public void onFinished(int);
+ method public void onFinishing();
+ method public void onStarted(@NonNull android.os.vibrator.VendorVibrationSession);
+ }
+
+}
+
package android.os.vibrator.persistence {
@FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration {
@@ -12480,8 +12553,19 @@ package android.security.advancedprotection {
}
@FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
+ method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String);
method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures();
method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
+ field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
+ field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
+ field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
+ field public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = "android.security.advancedprotection.feature_disallow_2g";
+ field public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+ field public static final String FEATURE_ID_DISALLOW_USB = "android.security.advancedprotection.feature_disallow_usb";
+ field public static final String FEATURE_ID_DISALLOW_WEP = "android.security.advancedprotection.feature_disallow_wep";
+ field public static final String FEATURE_ID_ENABLE_MTE = "android.security.advancedprotection.feature_enable_mte";
+ field public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = "android.security.advancedprotection.type_blocked_interaction";
+ field public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = "android.security.advancedprotection.type_disabled_setting";
}
}
@@ -18292,7 +18376,12 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS = 7; // 0x7
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5; // 0x5
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4; // 0x4
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_SMS = 6; // 0x6
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_UNKNOWN = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2; // 0x2
@@ -18312,6 +18401,7 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT = 2; // 0x2
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
@@ -18325,6 +18415,8 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_MODEM_STATE_DISABLING_SATELLITE = 9; // 0x9
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_MODEM_STATE_ENABLING_SATELLITE = 8; // 0x8
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
@@ -18332,11 +18424,16 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ACCESS_BARRED = 16; // 0x10
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_DISABLE_IN_PROGRESS = 28; // 0x1c
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS = 27; // 0x1b
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_ENABLE_IN_PROGRESS = 29; // 0x1d
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ERROR = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ILLEGAL_STATE = 23; // 0x17
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8; // 0x8
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7; // 0x7
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_LOCATION_DISABLED = 25; // 0x19
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_LOCATION_NOT_AVAILABLE = 26; // 0x1a
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_BUSY = 22; // 0x16
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_ERROR = 4; // 0x4
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24; // 0x18
@@ -18730,6 +18827,10 @@ package android.webkit {
method @Deprecated public void openFileChooser(android.webkit.ValueCallback<android.net.Uri>, String, String);
}
+ public abstract static class WebChromeClient.FileChooserParams {
+ field @FlaggedApi("android.webkit.file_system_access") public static final long ENABLE_FILE_SYSTEM_ACCESS = 364980165L; // 0x15c127c5L
+ }
+
public abstract class WebHistoryItem implements java.lang.Cloneable {
method @Deprecated public abstract int getId();
}
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 78577e2b4090..7c43891f13f2 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -501,6 +501,52 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
+FlaggedApiLiteral: android.Manifest.permission#ACCESS_LAST_KNOWN_CELL_ID:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES).
+FlaggedApiLiteral: android.Manifest.permission#BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_HEALTH_CONNECT_BACKUP_RESTORE_PERMISSION_ENABLED).
+FlaggedApiLiteral: android.Manifest.permission#BIND_VERIFICATION_AGENT:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_VERIFICATION_SERVICE).
+FlaggedApiLiteral: android.Manifest.permission#EMBED_ANY_APP_IN_UNTRUSTED_MODE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.window.flags.Flags.FLAG_UNTRUSTED_EMBEDDING_ANY_APP_PERMISSION).
+FlaggedApiLiteral: android.Manifest.permission#EXECUTE_APP_FUNCTIONS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER).
+FlaggedApiLiteral: android.Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER).
+FlaggedApiLiteral: android.Manifest.permission#MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW).
+FlaggedApiLiteral: android.Manifest.permission#MANAGE_GLOBAL_SOUND_QUALITY_SERVICE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW).
+FlaggedApiLiteral: android.Manifest.permission#QUARANTINE_APPS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_QUARANTINED_ENABLED).
+FlaggedApiLiteral: android.Manifest.permission#QUERY_DEVICE_STOLEN_STATE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED).
+FlaggedApiLiteral: android.Manifest.permission#READ_BLOCKED_NUMBERS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES).
+FlaggedApiLiteral: android.Manifest.permission#RECEIVE_SANDBOX_TRIGGER_AUDIO:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_VOICE_ACTIVATION_PERMISSION_APIS).
+FlaggedApiLiteral: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.net.platform.flags.Flags.FLAG_REGISTER_NSD_OFFLOAD_ENGINE).
+FlaggedApiLiteral: android.Manifest.permission#RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_HEALTH_CONNECT_BACKUP_RESTORE_PERMISSION_ENABLED).
+FlaggedApiLiteral: android.Manifest.permission#SET_ADVANCED_PROTECTION_MODE:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.security.Flags.FLAG_AAPM_API).
+FlaggedApiLiteral: android.Manifest.permission#SINGLE_USER_TIS_ACCESS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_KIDS_MODE_TVDB_SHARING).
+FlaggedApiLiteral: android.Manifest.permission#THREAD_NETWORK_PRIVILEGED:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM).
+FlaggedApiLiteral: android.Manifest.permission#VERIFICATION_AGENT:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_VERIFICATION_SERVICE).
+FlaggedApiLiteral: android.Manifest.permission#VIBRATE_VENDOR_EFFECTS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS).
+FlaggedApiLiteral: android.Manifest.permission#WRITE_BLOCKED_NUMBERS:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES).
+FlaggedApiLiteral: android.R.attr#backgroundPermission:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED).
+
+
GenericException: android.app.prediction.AppPredictor#finalize():
Methods must not throw generic exceptions (`java.lang.Throwable`)
GenericException: android.hardware.location.ContextHubClient#finalize():
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 98d6f58e6bcf..c8ecfa94ec87 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -402,6 +402,7 @@ package android.app {
method @FlaggedApi("android.service.notification.notification_classification") @NonNull public java.util.Set<java.lang.String> getUnsupportedAdjustmentTypes();
method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean);
+ method @FlaggedApi("android.service.notification.notification_classification") public void setAssistantAdjustmentKeyTypeState(int, boolean);
method @FlaggedApi("android.app.api_rich_ongoing") public void setCanPostPromotedNotifications(@NonNull String, int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
@@ -534,9 +535,13 @@ package android.app {
public class WallpaperManager {
method @Nullable public android.graphics.Bitmap getBitmap();
method @Nullable public android.graphics.Bitmap getBitmapAsUser(int, boolean, int);
+ method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull java.util.List<android.graphics.Point>, int, boolean);
+ method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull android.graphics.Point, @NonNull java.util.List<android.graphics.Point>, @Nullable java.util.Map<android.graphics.Point,android.graphics.Rect>);
method public boolean isLockscreenLiveWallpaperEnabled();
method @Nullable public android.graphics.Rect peekBitmapDimensions();
method @Nullable public android.graphics.Rect peekBitmapDimensions(int);
+ method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithCrops(@Nullable android.graphics.Bitmap, @NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>, boolean, int) throws java.io.IOException;
+ method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(@NonNull java.io.InputStream, @NonNull java.util.Map<android.graphics.Point,android.graphics.Rect>, boolean, int) throws java.io.IOException;
method public void setWallpaperZoomOut(@NonNull android.os.IBinder, float);
method public boolean shouldEnableWideColorGamut();
method public boolean wallpaperSupportsWcg(int);
@@ -3246,6 +3251,14 @@ package android.service.quicksettings {
}
+package android.service.settings.preferences {
+
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable {
+ ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String, boolean, @Nullable android.content.ServiceConnection);
+ }
+
+}
+
package android.service.voice {
public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
@@ -3728,7 +3741,7 @@ package android.view {
method public final int getDisplayId();
method public final void setDisplayId(int);
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
- field public static final int LAST_KEYCODE = 318; // 0x13e
+ field public static final int LAST_KEYCODE = 337; // 0x151
}
public final class KeyboardShortcutGroup implements android.os.Parcelable {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index b4a3abc4ad22..08bb08254476 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -511,6 +511,12 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+FlaggedApiLiteral: android.Manifest.permission#ACCESSIBILITY_MOTION_EVENT_OBSERVING:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.accessibility.Flags.FLAG_MOTION_EVENT_OBSERVING, however this flag doesn't seem to exist).
+FlaggedApiLiteral: android.net.wifi.sharedconnectivity.app.SharedConnectivityManager#getBroadcastReceiver():
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.wifi.flags.Flags.FLAG_SHARED_CONNECTIVITY_BROADCAST_RECEIVER_TEST_API, however this flag doesn't seem to exist).
+
+
InvalidNullabilityOverride: android.window.WindowProviderService#getSystemService(String) parameter #0:
Invalid nullability on parameter `name` in method `getSystemService`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.window.WindowProviderService#onConfigurationChanged(android.content.res.Configuration) parameter #0:
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 9875efe04361..cf5ebbaa37b4 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -28,6 +28,7 @@ filegroup {
exclude_srcs: [
"android/os/*MessageQueue/**/*.java",
"android/ranging/**/*.java",
+ ":dynamic_instrumentation_manager_aidl_sources",
],
visibility: ["//frameworks/base"],
}
@@ -120,6 +121,17 @@ filegroup {
}
filegroup {
+ name: "dynamic_instrumentation_manager_aidl_sources",
+ srcs: ["android/os/instrumentation/*.aidl"],
+}
+
+aidl_interface {
+ name: "dynamic_instrumentation_manager_aidl",
+ srcs: [":dynamic_instrumentation_manager_aidl_sources"],
+ unstable: true,
+}
+
+filegroup {
name: "framework-internal-display-sources",
srcs: ["com/android/internal/display/BrightnessSynchronizer.java"],
visibility: ["//frameworks/base/services/tests/mockingservicestests"],
@@ -206,6 +218,7 @@ filegroup {
"android/os/Temperature.aidl",
"android/os/CoolingDevice.aidl",
"android/os/IThermalEventListener.aidl",
+ "android/os/IThermalHeadroomListener.aidl",
"android/os/IThermalStatusListener.aidl",
"android/os/IThermalService.aidl",
"android/os/IPowerManager.aidl",
@@ -684,16 +697,31 @@ gen_readonly_feature_apis = select(release_flag("RELEASE_USE_SYSTEM_FEATURE_BUIL
// Generates com.android.internal.pm.RoSystemFeatures, optionally compiling in
// details about fixed system features defined by build flags. When disabled,
// the APIs are simply passthrough stubs with no meaningful side effects.
+// TODO(b/203143243): Implement the `--feature=` aggregation directly with a native soong module.
genrule {
name: "systemfeatures-gen-srcs",
cmd: "$(location systemfeatures-gen-tool) com.android.internal.pm.RoSystemFeatures " +
// --readonly=false (default) makes the codegen an effective no-op passthrough API.
" --readonly=" + gen_readonly_feature_apis +
- // For now, only export "android.hardware.type.*" system features APIs.
- // TODO(b/203143243): Use an intermediate soong var that aggregates all declared
- // RELEASE_SYSTEM_FEATURE_* declarations into a single arg.
- " --feature-apis=AUTOMOTIVE,WATCH,TELEVISION,EMBEDDED,PC" +
- " > $(out)",
+ " --feature=AUTOMOTIVE:" + select(release_flag("RELEASE_SYSTEM_FEATURE_AUTOMOTIVE"), {
+ any @ value: value,
+ default: "",
+ }) + " --feature=EMBEDDED:" + select(release_flag("RELEASE_SYSTEM_FEATURE_EMBEDDED"), {
+ any @ value: value,
+ default: "",
+ }) + " --feature=LEANBACK:" + select(release_flag("RELEASE_SYSTEM_FEATURE_LEANBACK"), {
+ any @ value: value,
+ default: "",
+ }) + " --feature=PC:" + select(release_flag("RELEASE_SYSTEM_FEATURE_PC"), {
+ any @ value: value,
+ default: "",
+ }) + " --feature=TELEVISION:" + select(release_flag("RELEASE_SYSTEM_FEATURE_TELEVISION"), {
+ any @ value: value,
+ default: "",
+ }) + " --feature=WATCH:" + select(release_flag("RELEASE_SYSTEM_FEATURE_WATCH"), {
+ any @ value: value,
+ default: "",
+ }) + " > $(out)",
out: [
"RoSystemFeatures.java",
],
diff --git a/core/java/android/adaptiveauth/OWNERS b/core/java/android/adaptiveauth/OWNERS
index 0218a7835586..bc8efa92c16f 100644
--- a/core/java/android/adaptiveauth/OWNERS
+++ b/core/java/android/adaptiveauth/OWNERS
@@ -1 +1 @@
-include /services/core/java/com/android/server/adaptiveauth/OWNERS \ No newline at end of file
+include /services/core/java/com/android/server/security/adaptiveauthentication/OWNERS \ No newline at end of file
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7a1c759a3ec4..419eb7dac5f0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -30,6 +30,7 @@ import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
import static java.lang.Character.MIN_VALUE;
+import android.Manifest;
import android.annotation.AnimRes;
import android.annotation.CallSuper;
import android.annotation.CallbackExecutor;
@@ -53,6 +54,7 @@ import android.app.VoiceInteractor.Request;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.compat.CompatChanges;
+import android.app.jank.JankTracker;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -123,6 +125,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SuperNotCalledException;
import android.view.ActionMode;
+import android.view.Choreographer;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
@@ -174,6 +177,7 @@ import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.util.dump.DumpableContainerImpl;
@@ -1144,6 +1148,9 @@ public class Activity extends ContextThemeWrapper
};
+ @Nullable
+ private JankTracker mJankTracker;
+
private static native String getDlWarning();
/**
@@ -2244,6 +2251,10 @@ public class Activity extends ContextThemeWrapper
// Notify autofill
getAutofillClientController().onActivityPostResumed();
+ if (android.app.jank.Flags.detailedAppJankMetricsApi()) {
+ startAppJankTracking();
+ }
+
mCalled = true;
}
@@ -3193,6 +3204,16 @@ public class Activity extends ContextThemeWrapper
return ActivityTaskManager.getMaxNumPictureInPictureActions(this);
}
+ private boolean isImplicitEnterPipProhibited() {
+ PackageManager pm = getPackageManager();
+ if (android.app.Flags.enableTvImplicitEnterPipRestriction()) {
+ return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && pm.checkPermission(Manifest.permission.TV_IMPLICIT_ENTER_PIP,
+ getPackageName()) == PackageManager.PERMISSION_DENIED;
+ }
+ return false;
+ }
+
/**
* @return Whether this device supports picture-in-picture.
*/
@@ -9192,6 +9213,8 @@ public class Activity extends ContextThemeWrapper
}
dispatchActivityPreResumed();
+ mCanEnterPictureInPicture = true;
+
mFragments.execPendingActions();
mLastNonConfigurationInstances = null;
@@ -9243,9 +9266,17 @@ public class Activity extends ContextThemeWrapper
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:"
+ mComponent.getClassName());
}
+
+ if (isImplicitEnterPipProhibited()) {
+ mCanEnterPictureInPicture = false;
+ }
+
dispatchActivityPrePaused();
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
+ if (android.app.jank.Flags.detailedAppJankMetricsApi()) {
+ stopAppJankTracking();
+ }
mCalled = false;
final long startTime = SystemClock.uptimeMillis();
onPause();
@@ -9265,6 +9296,10 @@ public class Activity extends ContextThemeWrapper
final void performUserLeaving() {
onUserInteraction();
+
+ if (isImplicitEnterPipProhibited()) {
+ mCanEnterPictureInPicture = false;
+ }
onUserLeaveHint();
}
@@ -9924,4 +9959,49 @@ public class Activity extends ContextThemeWrapper
mScreenCaptureCallbackHandler.unregisterScreenCaptureCallback(callback);
}
}
+
+ /**
+ * Enabling jank tracking for this activity but only if certain conditions are met. The
+ * application must have an app category other than undefined and a visible view.
+ */
+ private void startAppJankTracking() {
+ if (!android.app.jank.Flags.detailedAppJankMetricsLoggingEnabled()) {
+ return;
+ }
+ if (mApplication.getApplicationInfo().category == ApplicationInfo.CATEGORY_UNDEFINED) {
+ return;
+ }
+ if (getWindow() != null && getWindow().peekDecorView() != null) {
+ DecorView decorView = (DecorView) getWindow().peekDecorView();
+ if (decorView.getVisibility() == View.VISIBLE) {
+ decorView.setAppJankStatsCallback(new DecorView.AppJankStatsCallback() {
+ @Override
+ public JankTracker getAppJankTracker() {
+ return mJankTracker;
+ }
+ });
+ if (mJankTracker == null) {
+ // TODO b/377960907 use the Choreographer attached to the ViewRootImpl instead.
+ mJankTracker = new JankTracker(Choreographer.getInstance(),
+ decorView);
+ }
+ // TODO b/377674765 confirm this is the string we want logged.
+ mJankTracker.setActivityName(getComponentName().getClassName());
+ mJankTracker.setAppUid(myUid());
+ mJankTracker.enableAppJankTracking();
+ }
+ }
+ }
+
+ /**
+ * Call to disable jank tracking for this activity.
+ */
+ private void stopAppJankTracking() {
+ if (!android.app.jank.Flags.detailedAppJankMetricsLoggingEnabled()) {
+ return;
+ }
+ if (mJankTracker != null) {
+ mJankTracker.disableAppJankTracking();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 36fc65a76d53..1b707f79ab81 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1017,6 +1017,12 @@ public class ActivityManager {
public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 1 << 6;
/**
+ * @hide
+ * Process is guaranteed cpu time (IE. it will not be frozen).
+ */
+ public static final int PROCESS_CAPABILITY_CPU_TIME = 1 << 7;
+
+ /**
* @hide all capabilities, the ORing of all flags in {@link ProcessCapability}.
*
* Don't expose it as TestApi -- we may add new capabilities any time, which could
@@ -1028,7 +1034,8 @@ public class ActivityManager {
| PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
| PROCESS_CAPABILITY_BFSL
| PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK
- | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
+ | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL
+ | PROCESS_CAPABILITY_CPU_TIME;
/**
* All implicit capabilities. This capability set is currently only used for processes under
@@ -1053,6 +1060,7 @@ public class ActivityManager {
pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-');
pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-');
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-');
+ pw.print((caps & PROCESS_CAPABILITY_CPU_TIME) != 0 ? 'T' : '-');
}
/** @hide */
@@ -1065,6 +1073,7 @@ public class ActivityManager {
sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-');
sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-');
sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_CPU_TIME) != 0 ? 'T' : '-');
}
/**
@@ -2764,14 +2773,19 @@ public class ActivityManager {
/**
* Information of organized child tasks.
*
+ * @deprecated No longer used
* @hide
*/
+ @Deprecated
public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>();
/**
* Information about the last snapshot taken for this task.
+ *
+ * @deprecated No longer used
* @hide
*/
+ @Deprecated
public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
public RecentTaskInfo() {
@@ -2793,7 +2807,7 @@ public class ActivityManager {
lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR);
lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR);
lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR);
- super.readFromParcel(source);
+ super.readTaskFromParcel(source);
}
@Override
@@ -2804,7 +2818,7 @@ public class ActivityManager {
dest.writeTypedObject(lastSnapshotData.taskSize, flags);
dest.writeTypedObject(lastSnapshotData.contentInsets, flags);
dest.writeTypedObject(lastSnapshotData.bufferSize, flags);
- super.writeToParcel(dest, flags);
+ super.writeTaskToParcel(dest, flags);
}
public static final @android.annotation.NonNull Creator<RecentTaskInfo> CREATOR
@@ -2988,13 +3002,13 @@ public class ActivityManager {
public void readFromParcel(Parcel source) {
id = source.readInt();
- super.readFromParcel(source);
+ super.readTaskFromParcel(source);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
- super.writeToParcel(dest, flags);
+ super.writeTaskToParcel(dest, flags);
}
public static final @android.annotation.NonNull Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 799df1f9227a..16dcf2ad7e45 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -534,10 +534,9 @@ public class ActivityTaskManager {
dest.writeIntArray(childTaskUserIds);
dest.writeInt(visible ? 1 : 0);
dest.writeInt(position);
- super.writeToParcel(dest, flags);
+ super.writeTaskToParcel(dest, flags);
}
- @Override
void readFromParcel(Parcel source) {
bounds = source.readTypedObject(Rect.CREATOR);
childTaskIds = source.createIntArray();
@@ -546,7 +545,7 @@ public class ActivityTaskManager {
childTaskUserIds = source.createIntArray();
visible = source.readInt() > 0;
position = source.readInt();
- super.readFromParcel(source);
+ super.readTaskFromParcel(source);
}
public static final @NonNull Creator<RootTaskInfo> CREATOR = new Creator<>() {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ca98da76b78f..60b8f80d8f2d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3100,6 +3100,19 @@ public final class ActivityThread extends ClientTransactionHandler
mResourcesManager = ResourcesManager.getInstance();
}
+ /**
+ * Creates and initialize a new system activity thread, to be used for testing. This does not
+ * call {@link #attach}, so it does not modify static state.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static ActivityThread createSystemActivityThreadForTesting() {
+ final var thread = new ActivityThread();
+ thread.mSystemThread = true;
+ initializeSystemThread(thread);
+ return thread;
+ }
+
@UnsupportedAppUsage
public ApplicationThread getApplicationThread()
{
@@ -6806,6 +6819,16 @@ public final class ActivityThread extends ClientTransactionHandler
LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
resApk.updateApplicationInfo(ai, oldPaths);
}
+ if (android.content.res.Flags.systemContextHandleAppInfoChanged() && mSystemThread) {
+ final var systemContext = getSystemContext();
+ if (systemContext.getPackageName().equals(ai.packageName)) {
+ // The system package is not tracked directly, but still needs to receive updates to
+ // its application info.
+ final ArrayList<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, systemContext.getApplicationInfo(), oldPaths);
+ systemContext.mPackageInfo.updateApplicationInfo(ai, oldPaths);
+ }
+ }
ResourcesImpl beforeImpl = getApplication().getResources().getImpl();
@@ -8560,17 +8583,7 @@ public final class ActivityThread extends ClientTransactionHandler
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
- try {
- mInstrumentation = new Instrumentation();
- mInstrumentation.basicInit(this);
- ContextImpl context = ContextImpl.createAppContext(
- this, getSystemContext().mPackageInfo);
- mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null);
- mInitialApplication.onCreate();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate Application():" + e.toString(), e);
- }
+ initializeSystemThread(this);
}
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
@@ -8595,6 +8608,28 @@ public final class ActivityThread extends ClientTransactionHandler
ViewRootImpl.addConfigCallback(configChangedCallback);
}
+ /**
+ * Initializes the given system activity thread, setting up its instrumentation and initial
+ * application. This only has an effect if the given thread is a {@link #mSystemThread}.
+ *
+ * @param thread the given system activity thread to initialize.
+ */
+ private static void initializeSystemThread(@NonNull ActivityThread thread) {
+ if (!thread.mSystemThread) {
+ return;
+ }
+ try {
+ thread.mInstrumentation = new Instrumentation();
+ thread.mInstrumentation.basicInit(thread);
+ ContextImpl context = ContextImpl.createAppContext(
+ thread, thread.getSystemContext().mPackageInfo);
+ thread.mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null);
+ thread.mInitialApplication.onCreate();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to instantiate Application():" + e, e);
+ }
+ }
+
@UnsupportedAppUsage
public static ActivityThread systemMain() {
ThreadedRenderer.initForSystemProcess();
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 4bfa3b340ec9..cf01f50c8026 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AppCompatCallbacks implements Compatibility.BehaviorChangeDelegate {
private final long[] mDisabledChanges;
private final long[] mLoggableChanges;
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 8370c2e522f3..009cd7249dcd 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -101,7 +101,6 @@ public class AppCompatTaskInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
FLAG_UNDEFINED,
- FLAG_BASE,
FLAG_LETTERBOX_EDU_ENABLED,
FLAG_ELIGIBLE_FOR_LETTERBOX_EDU,
FLAG_LETTERBOXED,
@@ -115,6 +114,10 @@ public class AppCompatTaskInfo implements Parcelable {
})
public @interface TopActivityFlag {}
+ /**
+ * A combination of {@link TopActivityFlag}s that have been enabled through
+ * {@link #setTopActivityFlag}.
+ */
@TopActivityFlag
private int mTopActivityFlags;
@@ -167,10 +170,11 @@ public class AppCompatTaskInfo implements Parcelable {
}
/**
- * @return {@code true} if top activity is pillarboxed.
+ * @return {@code true} if the top activity bounds are letterboxed with width <= height.
*/
- public boolean isTopActivityPillarboxed() {
- return topActivityLetterboxWidth < topActivityLetterboxHeight;
+ public boolean isTopActivityPillarboxShaped() {
+ return isTopActivityLetterboxed()
+ && topActivityLetterboxWidth <= topActivityLetterboxHeight;
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2b0e86c3205c..8b37dbd04bec 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -63,6 +63,7 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.IpcDataCache;
import android.os.Looper;
import android.os.PackageTagsList;
import android.os.Parcel;
@@ -78,12 +79,14 @@ import android.permission.PermissionGroupUsage;
import android.permission.PermissionUsageHelper;
import android.permission.flags.Flags;
import android.provider.DeviceConfig;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.Pools;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
@@ -910,159 +913,157 @@ public class AppOpsManager {
/** @hide No operation specified. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int OP_NONE = AppProtoEnums.APP_OP_NONE;
+ public static final int OP_NONE = AppOpEnums.APP_OP_NONE;
/** @hide Access to coarse location information. */
@UnsupportedAppUsage
@TestApi
- public static final int OP_COARSE_LOCATION = AppProtoEnums.APP_OP_COARSE_LOCATION;
+ public static final int OP_COARSE_LOCATION = AppOpEnums.APP_OP_COARSE_LOCATION;
/** @hide Access to fine location information. */
@UnsupportedAppUsage
- public static final int OP_FINE_LOCATION = AppProtoEnums.APP_OP_FINE_LOCATION;
+ public static final int OP_FINE_LOCATION = AppOpEnums.APP_OP_FINE_LOCATION;
/** @hide Causing GPS to run. */
@UnsupportedAppUsage
- public static final int OP_GPS = AppProtoEnums.APP_OP_GPS;
+ public static final int OP_GPS = AppOpEnums.APP_OP_GPS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_VIBRATE = AppProtoEnums.APP_OP_VIBRATE;
+ public static final int OP_VIBRATE = AppOpEnums.APP_OP_VIBRATE;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_READ_CONTACTS = AppProtoEnums.APP_OP_READ_CONTACTS;
+ public static final int OP_READ_CONTACTS = AppOpEnums.APP_OP_READ_CONTACTS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_CONTACTS = AppProtoEnums.APP_OP_WRITE_CONTACTS;
+ public static final int OP_WRITE_CONTACTS = AppOpEnums.APP_OP_WRITE_CONTACTS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_READ_CALL_LOG = AppProtoEnums.APP_OP_READ_CALL_LOG;
+ public static final int OP_READ_CALL_LOG = AppOpEnums.APP_OP_READ_CALL_LOG;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_CALL_LOG = AppProtoEnums.APP_OP_WRITE_CALL_LOG;
+ public static final int OP_WRITE_CALL_LOG = AppOpEnums.APP_OP_WRITE_CALL_LOG;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_READ_CALENDAR = AppProtoEnums.APP_OP_READ_CALENDAR;
+ public static final int OP_READ_CALENDAR = AppOpEnums.APP_OP_READ_CALENDAR;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_CALENDAR = AppProtoEnums.APP_OP_WRITE_CALENDAR;
+ public static final int OP_WRITE_CALENDAR = AppOpEnums.APP_OP_WRITE_CALENDAR;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WIFI_SCAN = AppProtoEnums.APP_OP_WIFI_SCAN;
+ public static final int OP_WIFI_SCAN = AppOpEnums.APP_OP_WIFI_SCAN;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_POST_NOTIFICATION = AppProtoEnums.APP_OP_POST_NOTIFICATION;
+ public static final int OP_POST_NOTIFICATION = AppOpEnums.APP_OP_POST_NOTIFICATION;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_NEIGHBORING_CELLS = AppProtoEnums.APP_OP_NEIGHBORING_CELLS;
+ public static final int OP_NEIGHBORING_CELLS = AppOpEnums.APP_OP_NEIGHBORING_CELLS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_CALL_PHONE = AppProtoEnums.APP_OP_CALL_PHONE;
+ public static final int OP_CALL_PHONE = AppOpEnums.APP_OP_CALL_PHONE;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_READ_SMS = AppProtoEnums.APP_OP_READ_SMS;
+ public static final int OP_READ_SMS = AppOpEnums.APP_OP_READ_SMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_SMS = AppProtoEnums.APP_OP_WRITE_SMS;
+ public static final int OP_WRITE_SMS = AppOpEnums.APP_OP_WRITE_SMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_RECEIVE_SMS = AppProtoEnums.APP_OP_RECEIVE_SMS;
+ public static final int OP_RECEIVE_SMS = AppOpEnums.APP_OP_RECEIVE_SMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_RECEIVE_EMERGECY_SMS =
- AppProtoEnums.APP_OP_RECEIVE_EMERGENCY_SMS;
+ public static final int OP_RECEIVE_EMERGECY_SMS = AppOpEnums.APP_OP_RECEIVE_EMERGENCY_SMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_RECEIVE_MMS = AppProtoEnums.APP_OP_RECEIVE_MMS;
+ public static final int OP_RECEIVE_MMS = AppOpEnums.APP_OP_RECEIVE_MMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_RECEIVE_WAP_PUSH = AppProtoEnums.APP_OP_RECEIVE_WAP_PUSH;
+ public static final int OP_RECEIVE_WAP_PUSH = AppOpEnums.APP_OP_RECEIVE_WAP_PUSH;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_SEND_SMS = AppProtoEnums.APP_OP_SEND_SMS;
+ public static final int OP_SEND_SMS = AppOpEnums.APP_OP_SEND_SMS;
/** @hide */
- public static final int OP_MANAGE_ONGOING_CALLS = AppProtoEnums.APP_OP_MANAGE_ONGOING_CALLS;
+ public static final int OP_MANAGE_ONGOING_CALLS = AppOpEnums.APP_OP_MANAGE_ONGOING_CALLS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_READ_ICC_SMS = AppProtoEnums.APP_OP_READ_ICC_SMS;
+ public static final int OP_READ_ICC_SMS = AppOpEnums.APP_OP_READ_ICC_SMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_ICC_SMS = AppProtoEnums.APP_OP_WRITE_ICC_SMS;
+ public static final int OP_WRITE_ICC_SMS = AppOpEnums.APP_OP_WRITE_ICC_SMS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_SETTINGS = AppProtoEnums.APP_OP_WRITE_SETTINGS;
+ public static final int OP_WRITE_SETTINGS = AppOpEnums.APP_OP_WRITE_SETTINGS;
/** @hide Required to draw on top of other apps. */
@UnsupportedAppUsage
@TestApi
- public static final int OP_SYSTEM_ALERT_WINDOW = AppProtoEnums.APP_OP_SYSTEM_ALERT_WINDOW;
+ public static final int OP_SYSTEM_ALERT_WINDOW = AppOpEnums.APP_OP_SYSTEM_ALERT_WINDOW;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_ACCESS_NOTIFICATIONS =
- AppProtoEnums.APP_OP_ACCESS_NOTIFICATIONS;
+ public static final int OP_ACCESS_NOTIFICATIONS = AppOpEnums.APP_OP_ACCESS_NOTIFICATIONS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_CAMERA = AppProtoEnums.APP_OP_CAMERA;
+ public static final int OP_CAMERA = AppOpEnums.APP_OP_CAMERA;
/** @hide */
@UnsupportedAppUsage
@TestApi
- public static final int OP_RECORD_AUDIO = AppProtoEnums.APP_OP_RECORD_AUDIO;
+ public static final int OP_RECORD_AUDIO = AppOpEnums.APP_OP_RECORD_AUDIO;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_PLAY_AUDIO = AppProtoEnums.APP_OP_PLAY_AUDIO;
+ public static final int OP_PLAY_AUDIO = AppOpEnums.APP_OP_PLAY_AUDIO;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_READ_CLIPBOARD = AppProtoEnums.APP_OP_READ_CLIPBOARD;
+ public static final int OP_READ_CLIPBOARD = AppOpEnums.APP_OP_READ_CLIPBOARD;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WRITE_CLIPBOARD = AppProtoEnums.APP_OP_WRITE_CLIPBOARD;
+ public static final int OP_WRITE_CLIPBOARD = AppOpEnums.APP_OP_WRITE_CLIPBOARD;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_TAKE_MEDIA_BUTTONS = AppProtoEnums.APP_OP_TAKE_MEDIA_BUTTONS;
+ public static final int OP_TAKE_MEDIA_BUTTONS = AppOpEnums.APP_OP_TAKE_MEDIA_BUTTONS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_TAKE_AUDIO_FOCUS = AppProtoEnums.APP_OP_TAKE_AUDIO_FOCUS;
+ public static final int OP_TAKE_AUDIO_FOCUS = AppOpEnums.APP_OP_TAKE_AUDIO_FOCUS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_AUDIO_MASTER_VOLUME = AppProtoEnums.APP_OP_AUDIO_MASTER_VOLUME;
+ public static final int OP_AUDIO_MASTER_VOLUME = AppOpEnums.APP_OP_AUDIO_MASTER_VOLUME;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_AUDIO_VOICE_VOLUME = AppProtoEnums.APP_OP_AUDIO_VOICE_VOLUME;
+ public static final int OP_AUDIO_VOICE_VOLUME = AppOpEnums.APP_OP_AUDIO_VOICE_VOLUME;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_AUDIO_RING_VOLUME = AppProtoEnums.APP_OP_AUDIO_RING_VOLUME;
+ public static final int OP_AUDIO_RING_VOLUME = AppOpEnums.APP_OP_AUDIO_RING_VOLUME;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_AUDIO_MEDIA_VOLUME = AppProtoEnums.APP_OP_AUDIO_MEDIA_VOLUME;
+ public static final int OP_AUDIO_MEDIA_VOLUME = AppOpEnums.APP_OP_AUDIO_MEDIA_VOLUME;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_AUDIO_ALARM_VOLUME = AppProtoEnums.APP_OP_AUDIO_ALARM_VOLUME;
+ public static final int OP_AUDIO_ALARM_VOLUME = AppOpEnums.APP_OP_AUDIO_ALARM_VOLUME;
/** @hide */
@UnsupportedAppUsage
public static final int OP_AUDIO_NOTIFICATION_VOLUME =
- AppProtoEnums.APP_OP_AUDIO_NOTIFICATION_VOLUME;
+ AppOpEnums.APP_OP_AUDIO_NOTIFICATION_VOLUME;
/** @hide */
@UnsupportedAppUsage
public static final int OP_AUDIO_BLUETOOTH_VOLUME =
- AppProtoEnums.APP_OP_AUDIO_BLUETOOTH_VOLUME;
+ AppOpEnums.APP_OP_AUDIO_BLUETOOTH_VOLUME;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_WAKE_LOCK = AppProtoEnums.APP_OP_WAKE_LOCK;
+ public static final int OP_WAKE_LOCK = AppOpEnums.APP_OP_WAKE_LOCK;
/** @hide Continually monitoring location data. */
@UnsupportedAppUsage
public static final int OP_MONITOR_LOCATION =
- AppProtoEnums.APP_OP_MONITOR_LOCATION;
+ AppOpEnums.APP_OP_MONITOR_LOCATION;
/** @hide Continually monitoring location data with a relatively high power request. */
@UnsupportedAppUsage
public static final int OP_MONITOR_HIGH_POWER_LOCATION =
- AppProtoEnums.APP_OP_MONITOR_HIGH_POWER_LOCATION;
+ AppOpEnums.APP_OP_MONITOR_HIGH_POWER_LOCATION;
/** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
@UnsupportedAppUsage
- public static final int OP_GET_USAGE_STATS = AppProtoEnums.APP_OP_GET_USAGE_STATS;
+ public static final int OP_GET_USAGE_STATS = AppOpEnums.APP_OP_GET_USAGE_STATS;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_MUTE_MICROPHONE = AppProtoEnums.APP_OP_MUTE_MICROPHONE;
+ public static final int OP_MUTE_MICROPHONE = AppOpEnums.APP_OP_MUTE_MICROPHONE;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_TOAST_WINDOW = AppProtoEnums.APP_OP_TOAST_WINDOW;
+ public static final int OP_TOAST_WINDOW = AppOpEnums.APP_OP_TOAST_WINDOW;
/** @hide Capture the device's display contents and/or audio */
@UnsupportedAppUsage
- public static final int OP_PROJECT_MEDIA = AppProtoEnums.APP_OP_PROJECT_MEDIA;
+ public static final int OP_PROJECT_MEDIA = AppOpEnums.APP_OP_PROJECT_MEDIA;
/**
* Start (without additional user intervention) a VPN connection, as used by {@link
* android.net.VpnService} along with as Platform VPN connections, as used by {@link
@@ -1075,146 +1076,141 @@ public class AppOpsManager {
* @hide
*/
@UnsupportedAppUsage
- public static final int OP_ACTIVATE_VPN = AppProtoEnums.APP_OP_ACTIVATE_VPN;
+ public static final int OP_ACTIVATE_VPN = AppOpEnums.APP_OP_ACTIVATE_VPN;
/** @hide Access the WallpaperManagerAPI to write wallpapers. */
@UnsupportedAppUsage
- public static final int OP_WRITE_WALLPAPER = AppProtoEnums.APP_OP_WRITE_WALLPAPER;
+ public static final int OP_WRITE_WALLPAPER = AppOpEnums.APP_OP_WRITE_WALLPAPER;
/** @hide Received the assist structure from an app. */
@UnsupportedAppUsage
- public static final int OP_ASSIST_STRUCTURE = AppProtoEnums.APP_OP_ASSIST_STRUCTURE;
+ public static final int OP_ASSIST_STRUCTURE = AppOpEnums.APP_OP_ASSIST_STRUCTURE;
/** @hide Received a screenshot from assist. */
@UnsupportedAppUsage
- public static final int OP_ASSIST_SCREENSHOT = AppProtoEnums.APP_OP_ASSIST_SCREENSHOT;
+ public static final int OP_ASSIST_SCREENSHOT = AppOpEnums.APP_OP_ASSIST_SCREENSHOT;
/** @hide Read the phone state. */
@UnsupportedAppUsage
- public static final int OP_READ_PHONE_STATE = AppProtoEnums.APP_OP_READ_PHONE_STATE;
+ public static final int OP_READ_PHONE_STATE = AppOpEnums.APP_OP_READ_PHONE_STATE;
/** @hide Add voicemail messages to the voicemail content provider. */
@UnsupportedAppUsage
- public static final int OP_ADD_VOICEMAIL = AppProtoEnums.APP_OP_ADD_VOICEMAIL;
+ public static final int OP_ADD_VOICEMAIL = AppOpEnums.APP_OP_ADD_VOICEMAIL;
/** @hide Access APIs for SIP calling over VOIP or WiFi. */
@UnsupportedAppUsage
- public static final int OP_USE_SIP = AppProtoEnums.APP_OP_USE_SIP;
+ public static final int OP_USE_SIP = AppOpEnums.APP_OP_USE_SIP;
/** @hide Intercept outgoing calls. */
@UnsupportedAppUsage
- public static final int OP_PROCESS_OUTGOING_CALLS =
- AppProtoEnums.APP_OP_PROCESS_OUTGOING_CALLS;
+ public static final int OP_PROCESS_OUTGOING_CALLS = AppOpEnums.APP_OP_PROCESS_OUTGOING_CALLS;
/** @hide User the fingerprint API. */
@UnsupportedAppUsage
- public static final int OP_USE_FINGERPRINT = AppProtoEnums.APP_OP_USE_FINGERPRINT;
+ public static final int OP_USE_FINGERPRINT = AppOpEnums.APP_OP_USE_FINGERPRINT;
/** @hide Access to body sensors such as heart rate, etc. */
@UnsupportedAppUsage
- public static final int OP_BODY_SENSORS = AppProtoEnums.APP_OP_BODY_SENSORS;
+ public static final int OP_BODY_SENSORS = AppOpEnums.APP_OP_BODY_SENSORS;
/** @hide Read previously received cell broadcast messages. */
@UnsupportedAppUsage
- public static final int OP_READ_CELL_BROADCASTS = AppProtoEnums.APP_OP_READ_CELL_BROADCASTS;
+ public static final int OP_READ_CELL_BROADCASTS = AppOpEnums.APP_OP_READ_CELL_BROADCASTS;
/** @hide Inject mock location into the system. */
@UnsupportedAppUsage
- public static final int OP_MOCK_LOCATION = AppProtoEnums.APP_OP_MOCK_LOCATION;
+ public static final int OP_MOCK_LOCATION = AppOpEnums.APP_OP_MOCK_LOCATION;
/** @hide Read external storage. */
@UnsupportedAppUsage
- public static final int OP_READ_EXTERNAL_STORAGE = AppProtoEnums.APP_OP_READ_EXTERNAL_STORAGE;
+ public static final int OP_READ_EXTERNAL_STORAGE = AppOpEnums.APP_OP_READ_EXTERNAL_STORAGE;
/** @hide Write external storage. */
@UnsupportedAppUsage
- public static final int OP_WRITE_EXTERNAL_STORAGE =
- AppProtoEnums.APP_OP_WRITE_EXTERNAL_STORAGE;
+ public static final int OP_WRITE_EXTERNAL_STORAGE = AppOpEnums.APP_OP_WRITE_EXTERNAL_STORAGE;
/** @hide Turned on the screen. */
@UnsupportedAppUsage
- public static final int OP_TURN_SCREEN_ON = AppProtoEnums.APP_OP_TURN_SCREEN_ON;
+ public static final int OP_TURN_SCREEN_ON = AppOpEnums.APP_OP_TURN_SCREEN_ON;
/** @hide Get device accounts. */
@UnsupportedAppUsage
- public static final int OP_GET_ACCOUNTS = AppProtoEnums.APP_OP_GET_ACCOUNTS;
+ public static final int OP_GET_ACCOUNTS = AppOpEnums.APP_OP_GET_ACCOUNTS;
/** @hide Control whether an application is allowed to run in the background. */
@UnsupportedAppUsage
- public static final int OP_RUN_IN_BACKGROUND =
- AppProtoEnums.APP_OP_RUN_IN_BACKGROUND;
+ public static final int OP_RUN_IN_BACKGROUND = AppOpEnums.APP_OP_RUN_IN_BACKGROUND;
/** @hide */
@UnsupportedAppUsage
public static final int OP_AUDIO_ACCESSIBILITY_VOLUME =
- AppProtoEnums.APP_OP_AUDIO_ACCESSIBILITY_VOLUME;
+ AppOpEnums.APP_OP_AUDIO_ACCESSIBILITY_VOLUME;
/** @hide Read the phone number. */
@UnsupportedAppUsage
- public static final int OP_READ_PHONE_NUMBERS = AppProtoEnums.APP_OP_READ_PHONE_NUMBERS;
+ public static final int OP_READ_PHONE_NUMBERS = AppOpEnums.APP_OP_READ_PHONE_NUMBERS;
/** @hide Request package installs through package installer */
@UnsupportedAppUsage
public static final int OP_REQUEST_INSTALL_PACKAGES =
- AppProtoEnums.APP_OP_REQUEST_INSTALL_PACKAGES;
+ AppOpEnums.APP_OP_REQUEST_INSTALL_PACKAGES;
/** @hide Enter picture-in-picture. */
@UnsupportedAppUsage
- public static final int OP_PICTURE_IN_PICTURE = AppProtoEnums.APP_OP_PICTURE_IN_PICTURE;
+ public static final int OP_PICTURE_IN_PICTURE = AppOpEnums.APP_OP_PICTURE_IN_PICTURE;
/** @hide Instant app start foreground service. */
@UnsupportedAppUsage
public static final int OP_INSTANT_APP_START_FOREGROUND =
- AppProtoEnums.APP_OP_INSTANT_APP_START_FOREGROUND;
+ AppOpEnums.APP_OP_INSTANT_APP_START_FOREGROUND;
/** @hide Answer incoming phone calls */
@UnsupportedAppUsage
- public static final int OP_ANSWER_PHONE_CALLS = AppProtoEnums.APP_OP_ANSWER_PHONE_CALLS;
+ public static final int OP_ANSWER_PHONE_CALLS = AppOpEnums.APP_OP_ANSWER_PHONE_CALLS;
/** @hide Run jobs when in background */
@UnsupportedAppUsage
- public static final int OP_RUN_ANY_IN_BACKGROUND = AppProtoEnums.APP_OP_RUN_ANY_IN_BACKGROUND;
+ public static final int OP_RUN_ANY_IN_BACKGROUND = AppOpEnums.APP_OP_RUN_ANY_IN_BACKGROUND;
/** @hide Change Wi-Fi connectivity state */
@UnsupportedAppUsage
- public static final int OP_CHANGE_WIFI_STATE = AppProtoEnums.APP_OP_CHANGE_WIFI_STATE;
+ public static final int OP_CHANGE_WIFI_STATE = AppOpEnums.APP_OP_CHANGE_WIFI_STATE;
/** @hide Request package deletion through package installer */
@UnsupportedAppUsage
- public static final int OP_REQUEST_DELETE_PACKAGES =
- AppProtoEnums.APP_OP_REQUEST_DELETE_PACKAGES;
+ public static final int OP_REQUEST_DELETE_PACKAGES = AppOpEnums.APP_OP_REQUEST_DELETE_PACKAGES;
/** @hide Bind an accessibility service. */
@UnsupportedAppUsage
public static final int OP_BIND_ACCESSIBILITY_SERVICE =
- AppProtoEnums.APP_OP_BIND_ACCESSIBILITY_SERVICE;
+ AppOpEnums.APP_OP_BIND_ACCESSIBILITY_SERVICE;
/** @hide Continue handover of a call from another app */
@UnsupportedAppUsage
- public static final int OP_ACCEPT_HANDOVER = AppProtoEnums.APP_OP_ACCEPT_HANDOVER;
+ public static final int OP_ACCEPT_HANDOVER = AppOpEnums.APP_OP_ACCEPT_HANDOVER;
/** @hide Create and Manage IPsec Tunnels */
@UnsupportedAppUsage
- public static final int OP_MANAGE_IPSEC_TUNNELS = AppProtoEnums.APP_OP_MANAGE_IPSEC_TUNNELS;
+ public static final int OP_MANAGE_IPSEC_TUNNELS = AppOpEnums.APP_OP_MANAGE_IPSEC_TUNNELS;
/** @hide Any app start foreground service. */
@UnsupportedAppUsage
@TestApi
- public static final int OP_START_FOREGROUND = AppProtoEnums.APP_OP_START_FOREGROUND;
+ public static final int OP_START_FOREGROUND = AppOpEnums.APP_OP_START_FOREGROUND;
/** @hide */
@UnsupportedAppUsage
- public static final int OP_BLUETOOTH_SCAN = AppProtoEnums.APP_OP_BLUETOOTH_SCAN;
+ public static final int OP_BLUETOOTH_SCAN = AppOpEnums.APP_OP_BLUETOOTH_SCAN;
/** @hide */
- public static final int OP_BLUETOOTH_CONNECT = AppProtoEnums.APP_OP_BLUETOOTH_CONNECT;
+ public static final int OP_BLUETOOTH_CONNECT = AppOpEnums.APP_OP_BLUETOOTH_CONNECT;
/** @hide */
- public static final int OP_BLUETOOTH_ADVERTISE = AppProtoEnums.APP_OP_BLUETOOTH_ADVERTISE;
+ public static final int OP_BLUETOOTH_ADVERTISE = AppOpEnums.APP_OP_BLUETOOTH_ADVERTISE;
/** @hide Use the BiometricPrompt/BiometricManager APIs. */
- public static final int OP_USE_BIOMETRIC = AppProtoEnums.APP_OP_USE_BIOMETRIC;
+ public static final int OP_USE_BIOMETRIC = AppOpEnums.APP_OP_USE_BIOMETRIC;
/** @hide Physical activity recognition. */
- public static final int OP_ACTIVITY_RECOGNITION = AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION;
+ public static final int OP_ACTIVITY_RECOGNITION = AppOpEnums.APP_OP_ACTIVITY_RECOGNITION;
/** @hide Financial app sms read. */
public static final int OP_SMS_FINANCIAL_TRANSACTIONS =
- AppProtoEnums.APP_OP_SMS_FINANCIAL_TRANSACTIONS;
+ AppOpEnums.APP_OP_SMS_FINANCIAL_TRANSACTIONS;
/** @hide Read media of audio type. */
- public static final int OP_READ_MEDIA_AUDIO = AppProtoEnums.APP_OP_READ_MEDIA_AUDIO;
+ public static final int OP_READ_MEDIA_AUDIO = AppOpEnums.APP_OP_READ_MEDIA_AUDIO;
/** @hide Write media of audio type. */
- public static final int OP_WRITE_MEDIA_AUDIO = AppProtoEnums.APP_OP_WRITE_MEDIA_AUDIO;
+ public static final int OP_WRITE_MEDIA_AUDIO = AppOpEnums.APP_OP_WRITE_MEDIA_AUDIO;
/** @hide Read media of video type. */
- public static final int OP_READ_MEDIA_VIDEO = AppProtoEnums.APP_OP_READ_MEDIA_VIDEO;
+ public static final int OP_READ_MEDIA_VIDEO = AppOpEnums.APP_OP_READ_MEDIA_VIDEO;
/** @hide Write media of video type. */
- public static final int OP_WRITE_MEDIA_VIDEO = AppProtoEnums.APP_OP_WRITE_MEDIA_VIDEO;
+ public static final int OP_WRITE_MEDIA_VIDEO = AppOpEnums.APP_OP_WRITE_MEDIA_VIDEO;
/** @hide Read media of image type. */
- public static final int OP_READ_MEDIA_IMAGES = AppProtoEnums.APP_OP_READ_MEDIA_IMAGES;
+ public static final int OP_READ_MEDIA_IMAGES = AppOpEnums.APP_OP_READ_MEDIA_IMAGES;
/** @hide Write media of image type. */
- public static final int OP_WRITE_MEDIA_IMAGES = AppProtoEnums.APP_OP_WRITE_MEDIA_IMAGES;
+ public static final int OP_WRITE_MEDIA_IMAGES = AppOpEnums.APP_OP_WRITE_MEDIA_IMAGES;
/** @hide Has a legacy (non-isolated) view of storage. */
- public static final int OP_LEGACY_STORAGE = AppProtoEnums.APP_OP_LEGACY_STORAGE;
+ public static final int OP_LEGACY_STORAGE = AppOpEnums.APP_OP_LEGACY_STORAGE;
/** @hide Accessing accessibility features */
- public static final int OP_ACCESS_ACCESSIBILITY = AppProtoEnums.APP_OP_ACCESS_ACCESSIBILITY;
+ public static final int OP_ACCESS_ACCESSIBILITY = AppOpEnums.APP_OP_ACCESS_ACCESSIBILITY;
/** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
public static final int OP_READ_DEVICE_IDENTIFIERS =
- AppProtoEnums.APP_OP_READ_DEVICE_IDENTIFIERS;
+ AppOpEnums.APP_OP_READ_DEVICE_IDENTIFIERS;
/** @hide Read location metadata from media */
- public static final int OP_ACCESS_MEDIA_LOCATION = AppProtoEnums.APP_OP_ACCESS_MEDIA_LOCATION;
+ public static final int OP_ACCESS_MEDIA_LOCATION = AppOpEnums.APP_OP_ACCESS_MEDIA_LOCATION;
/** @hide Query all apps on device, regardless of declarations in the calling app manifest */
- public static final int OP_QUERY_ALL_PACKAGES = AppProtoEnums.APP_OP_QUERY_ALL_PACKAGES;
+ public static final int OP_QUERY_ALL_PACKAGES = AppOpEnums.APP_OP_QUERY_ALL_PACKAGES;
/** @hide Access all external storage */
- public static final int OP_MANAGE_EXTERNAL_STORAGE =
- AppProtoEnums.APP_OP_MANAGE_EXTERNAL_STORAGE;
+ public static final int OP_MANAGE_EXTERNAL_STORAGE = AppOpEnums.APP_OP_MANAGE_EXTERNAL_STORAGE;
/** @hide Communicate cross-profile within the same profile group. */
public static final int OP_INTERACT_ACROSS_PROFILES =
- AppProtoEnums.APP_OP_INTERACT_ACROSS_PROFILES;
+ AppOpEnums.APP_OP_INTERACT_ACROSS_PROFILES;
/**
* Start (without additional user intervention) a Platform VPN connection, as used by {@link
* android.net.VpnManager}
@@ -1225,16 +1221,16 @@ public class AppOpsManager {
*
* @hide
*/
- public static final int OP_ACTIVATE_PLATFORM_VPN = AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN;
+ public static final int OP_ACTIVATE_PLATFORM_VPN = AppOpEnums.APP_OP_ACTIVATE_PLATFORM_VPN;
/** @hide Controls whether or not read logs are available for incremental installations. */
- public static final int OP_LOADER_USAGE_STATS = AppProtoEnums.APP_OP_LOADER_USAGE_STATS;
+ public static final int OP_LOADER_USAGE_STATS = AppOpEnums.APP_OP_LOADER_USAGE_STATS;
// App op deprecated/removed.
- private static final int OP_DEPRECATED_1 = AppProtoEnums.APP_OP_DEPRECATED_1;
+ private static final int OP_DEPRECATED_1 = AppOpEnums.APP_OP_DEPRECATED_1;
/** @hide Auto-revoke app permissions if app is unused for an extended period */
public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
- AppProtoEnums.APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
+ AppOpEnums.APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
/**
* Whether {@link #OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED} is allowed to be changed by
@@ -1243,55 +1239,55 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_AUTO_REVOKE_MANAGED_BY_INSTALLER =
- AppProtoEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER;
+ AppOpEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER;
/** @hide */
- public static final int OP_NO_ISOLATED_STORAGE = AppProtoEnums.APP_OP_NO_ISOLATED_STORAGE;
+ public static final int OP_NO_ISOLATED_STORAGE = AppOpEnums.APP_OP_NO_ISOLATED_STORAGE;
/**
* Phone call is using microphone
*
* @hide
*/
- public static final int OP_PHONE_CALL_MICROPHONE = AppProtoEnums.APP_OP_PHONE_CALL_MICROPHONE;
+ public static final int OP_PHONE_CALL_MICROPHONE = AppOpEnums.APP_OP_PHONE_CALL_MICROPHONE;
/**
* Phone call is using camera
*
* @hide
*/
- public static final int OP_PHONE_CALL_CAMERA = AppProtoEnums.APP_OP_PHONE_CALL_CAMERA;
+ public static final int OP_PHONE_CALL_CAMERA = AppOpEnums.APP_OP_PHONE_CALL_CAMERA;
/**
* Audio is being recorded for hotword detection.
*
* @hide
*/
- public static final int OP_RECORD_AUDIO_HOTWORD = AppProtoEnums.APP_OP_RECORD_AUDIO_HOTWORD;
+ public static final int OP_RECORD_AUDIO_HOTWORD = AppOpEnums.APP_OP_RECORD_AUDIO_HOTWORD;
/**
* Manage credentials in the system KeyChain.
*
* @hide
*/
- public static final int OP_MANAGE_CREDENTIALS = AppProtoEnums.APP_OP_MANAGE_CREDENTIALS;
+ public static final int OP_MANAGE_CREDENTIALS = AppOpEnums.APP_OP_MANAGE_CREDENTIALS;
/** @hide */
public static final int OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER =
- AppProtoEnums.APP_OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER;
+ AppOpEnums.APP_OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER;
/**
* App output audio is being recorded
*
* @hide
*/
- public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT;
+ public static final int OP_RECORD_AUDIO_OUTPUT = AppOpEnums.APP_OP_RECORD_AUDIO_OUTPUT;
/**
* App can schedule exact alarm to perform timing based background work
*
* @hide
*/
- public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
+ public static final int OP_SCHEDULE_EXACT_ALARM = AppOpEnums.APP_OP_SCHEDULE_EXACT_ALARM;
/**
* Fine location being accessed by a location source, which is
@@ -1301,7 +1297,7 @@ public class AppOpsManager {
*
* @hide
*/
- public static final int OP_FINE_LOCATION_SOURCE = AppProtoEnums.APP_OP_FINE_LOCATION_SOURCE;
+ public static final int OP_FINE_LOCATION_SOURCE = AppOpEnums.APP_OP_FINE_LOCATION_SOURCE;
/**
* Coarse location being accessed by a location source, which is
@@ -1311,7 +1307,7 @@ public class AppOpsManager {
*
* @hide
*/
- public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE;
+ public static final int OP_COARSE_LOCATION_SOURCE = AppOpEnums.APP_OP_COARSE_LOCATION_SOURCE;
/**
* Allow apps to create the requests to manage the media files without user confirmation.
@@ -1323,13 +1319,13 @@ public class AppOpsManager {
*
* @hide
*/
- public static final int OP_MANAGE_MEDIA = AppProtoEnums.APP_OP_MANAGE_MEDIA;
+ public static final int OP_MANAGE_MEDIA = AppOpEnums.APP_OP_MANAGE_MEDIA;
/** @hide */
- public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING;
+ public static final int OP_UWB_RANGING = AppOpEnums.APP_OP_UWB_RANGING;
/** @hide */
- public static final int OP_NEARBY_WIFI_DEVICES = AppProtoEnums.APP_OP_NEARBY_WIFI_DEVICES;
+ public static final int OP_NEARBY_WIFI_DEVICES = AppOpEnums.APP_OP_NEARBY_WIFI_DEVICES;
/**
* Activity recognition being accessed by an activity recognition source, which
@@ -1339,7 +1335,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_ACTIVITY_RECOGNITION_SOURCE =
- AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION_SOURCE;
+ AppOpEnums.APP_OP_ACTIVITY_RECOGNITION_SOURCE;
/**
* Incoming phone audio is being recorded
@@ -1347,21 +1343,21 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_RECORD_INCOMING_PHONE_AUDIO =
- AppProtoEnums.APP_OP_RECORD_INCOMING_PHONE_AUDIO;
+ AppOpEnums.APP_OP_RECORD_INCOMING_PHONE_AUDIO;
/**
* VPN app establishes a connection through the VpnService API.
*
* @hide
*/
- public static final int OP_ESTABLISH_VPN_SERVICE = AppProtoEnums.APP_OP_ESTABLISH_VPN_SERVICE;
+ public static final int OP_ESTABLISH_VPN_SERVICE = AppOpEnums.APP_OP_ESTABLISH_VPN_SERVICE;
/**
* VPN app establishes a connection through the VpnManager API.
*
* @hide
*/
- public static final int OP_ESTABLISH_VPN_MANAGER = AppProtoEnums.APP_OP_ESTABLISH_VPN_MANAGER;
+ public static final int OP_ESTABLISH_VPN_MANAGER = AppOpEnums.APP_OP_ESTABLISH_VPN_MANAGER;
/**
* Access restricted settings.
@@ -1369,7 +1365,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_ACCESS_RESTRICTED_SETTINGS =
- AppProtoEnums.APP_OP_ACCESS_RESTRICTED_SETTINGS;
+ AppOpEnums.APP_OP_ACCESS_RESTRICTED_SETTINGS;
/**
* Receive microphone audio from an ambient sound detection event
@@ -1377,7 +1373,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_RECEIVE_AMBIENT_TRIGGER_AUDIO =
- AppProtoEnums.APP_OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+ AppOpEnums.APP_OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
/**
* Receive audio from near-field mic (ie. TV remote)
@@ -1387,15 +1383,14 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
- AppProtoEnums.APP_OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
+ AppOpEnums.APP_OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
/**
* App can schedule user-initiated jobs.
*
* @hide
*/
- public static final int OP_RUN_USER_INITIATED_JOBS =
- AppProtoEnums.APP_OP_RUN_USER_INITIATED_JOBS;
+ public static final int OP_RUN_USER_INITIATED_JOBS = AppOpEnums.APP_OP_RUN_USER_INITIATED_JOBS;
/**
* Notify apps that they have been granted URI permission photos
@@ -1403,7 +1398,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_READ_MEDIA_VISUAL_USER_SELECTED =
- AppProtoEnums.APP_OP_READ_MEDIA_VISUAL_USER_SELECTED;
+ AppOpEnums.APP_OP_READ_MEDIA_VISUAL_USER_SELECTED;
/**
* Prevent an app from being suspended.
@@ -1413,7 +1408,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_SYSTEM_EXEMPT_FROM_SUSPENSION =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_SUSPENSION;
+ AppOpEnums.APP_OP_SYSTEM_EXEMPT_FROM_SUSPENSION;
/**
* Prevent an app from dismissible notifications. Starting from Android U, notifications with
@@ -1425,14 +1420,14 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
+ AppOpEnums.APP_OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
/**
* An app op for reading/writing health connect data.
*
* @hide
*/
- public static final int OP_READ_WRITE_HEALTH_DATA = AppProtoEnums.APP_OP_READ_WRITE_HEALTH_DATA;
+ public static final int OP_READ_WRITE_HEALTH_DATA = AppOpEnums.APP_OP_READ_WRITE_HEALTH_DATA;
/**
* Use foreground service with the type
@@ -1441,7 +1436,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_FOREGROUND_SERVICE_SPECIAL_USE =
- AppProtoEnums.APP_OP_FOREGROUND_SERVICE_SPECIAL_USE;
+ AppOpEnums.APP_OP_FOREGROUND_SERVICE_SPECIAL_USE;
/**
* Exempt an app from all power-related restrictions, including app standby and doze.
@@ -1453,7 +1448,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
+ AppOpEnums.APP_OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
/**
* Prevent an app from being placed into hibernation.
@@ -1463,7 +1458,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_SYSTEM_EXEMPT_FROM_HIBERNATION =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_HIBERNATION;
+ AppOpEnums.APP_OP_SYSTEM_EXEMPT_FROM_HIBERNATION;
/**
* Allows an application to start an activity while running in the background.
@@ -1473,7 +1468,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION =
- AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION;
+ AppOpEnums.APP_OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION;
/**
* Allows an application to capture bugreport directly without consent dialog when using the
@@ -1482,33 +1477,31 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
- AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
+ AppOpEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
// App op deprecated/removed.
- private static final int OP_DEPRECATED_2 = AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
+ private static final int OP_DEPRECATED_2 = AppOpEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
/**
* Send an intent to launch instead of posting the notification to the status bar.
*
* @hide
*/
- public static final int OP_USE_FULL_SCREEN_INTENT = AppProtoEnums.APP_OP_USE_FULL_SCREEN_INTENT;
+ public static final int OP_USE_FULL_SCREEN_INTENT = AppOpEnums.APP_OP_USE_FULL_SCREEN_INTENT;
/**
* Hides camera indicator for sandboxed detection apps that directly access the service.
*
* @hide
*/
- public static final int OP_CAMERA_SANDBOXED =
- AppProtoEnums.APP_OP_CAMERA_SANDBOXED;
+ public static final int OP_CAMERA_SANDBOXED = AppOpEnums.APP_OP_CAMERA_SANDBOXED;
/**
* Hides microphone indicator for sandboxed detection apps that directly access the service.
*
* @hide
*/
- public static final int OP_RECORD_AUDIO_SANDBOXED =
- AppProtoEnums.APP_OP_RECORD_AUDIO_SANDBOXED;
+ public static final int OP_RECORD_AUDIO_SANDBOXED = AppOpEnums.APP_OP_RECORD_AUDIO_SANDBOXED;
/**
* Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection
@@ -1517,14 +1510,14 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_RECEIVE_SANDBOX_TRIGGER_AUDIO =
- AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
+ AppOpEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
/**
* This op has been deprecated.
*
*/
private static final int OP_DEPRECATED_3 =
- AppProtoEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
+ AppOpEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
/**
* Creation of an overlay using accessibility services
@@ -1532,20 +1525,20 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_CREATE_ACCESSIBILITY_OVERLAY =
- AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY;
+ AppOpEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY;
/**
* Indicate that the user has enabled or disabled mobile data
* @hide
*/
public static final int OP_ENABLE_MOBILE_DATA_BY_USER =
- AppProtoEnums.APP_OP_ENABLE_MOBILE_DATA_BY_USER;
+ AppOpEnums.APP_OP_ENABLE_MOBILE_DATA_BY_USER;
/**
* See {@link #OPSTR_MEDIA_ROUTING_CONTROL}.
* @hide
*/
- public static final int OP_MEDIA_ROUTING_CONTROL = AppProtoEnums.APP_OP_MEDIA_ROUTING_CONTROL;
+ public static final int OP_MEDIA_ROUTING_CONTROL = AppOpEnums.APP_OP_MEDIA_ROUTING_CONTROL;
/**
* Op code for use by tests to avoid interfering history logs that the wider system might
@@ -1553,7 +1546,7 @@ public class AppOpsManager {
*
* @hide
*/
- public static final int OP_RESERVED_FOR_TESTING = AppProtoEnums.APP_OP_RESERVED_FOR_TESTING;
+ public static final int OP_RESERVED_FOR_TESTING = AppOpEnums.APP_OP_RESERVED_FOR_TESTING;
/**
* Rapid clearing of notifications by a notification listener
@@ -1562,28 +1555,28 @@ public class AppOpsManager {
*/
// See b/289080543 for more details
public static final int OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
- AppProtoEnums.APP_OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER;
+ AppOpEnums.APP_OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER;
/**
* See {@link #OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER}.
* @hide
*/
public static final int OP_READ_SYSTEM_GRAMMATICAL_GENDER =
- AppProtoEnums.APP_OP_READ_SYSTEM_GRAMMATICAL_GENDER;
+ AppOpEnums.APP_OP_READ_SYSTEM_GRAMMATICAL_GENDER;
/**
* This app has been removed..
*
* @hide
*/
- private static final int OP_DEPRECATED_4 = AppProtoEnums.APP_OP_RUN_BACKUP_JOBS;
+ private static final int OP_DEPRECATED_4 = AppOpEnums.APP_OP_RUN_BACKUP_JOBS;
/**
* Whether the app has enabled to receive the icon overlay for fetching archived apps.
*
* @hide
*/
- public static final int OP_ARCHIVE_ICON_OVERLAY = AppProtoEnums.APP_OP_ARCHIVE_ICON_OVERLAY;
+ public static final int OP_ARCHIVE_ICON_OVERLAY = AppOpEnums.APP_OP_ARCHIVE_ICON_OVERLAY;
/**
* Whether the app has enabled compatibility support for unarchival.
@@ -1591,7 +1584,7 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_UNARCHIVAL_CONFIRMATION =
- AppProtoEnums.APP_OP_UNARCHIVAL_CONFIRMATION;
+ AppOpEnums.APP_OP_UNARCHIVAL_CONFIRMATION;
/**
* Allows an app to access location without the traditional location permissions and while the
@@ -1603,7 +1596,7 @@ public class AppOpsManager {
*
* @hide
*/
- public static final int OP_EMERGENCY_LOCATION = AppProtoEnums.APP_OP_EMERGENCY_LOCATION;
+ public static final int OP_EMERGENCY_LOCATION = AppOpEnums.APP_OP_EMERGENCY_LOCATION;
/**
* Allows apps with a NotificationListenerService to receive notifications with sensitive
@@ -1613,17 +1606,27 @@ public class AppOpsManager {
* @hide
*/
public static final int OP_RECEIVE_SENSITIVE_NOTIFICATIONS =
- AppProtoEnums.APP_OP_RECEIVE_SENSITIVE_NOTIFICATIONS;
+ AppOpEnums.APP_OP_RECEIVE_SENSITIVE_NOTIFICATIONS;
/** @hide Access to read heart rate sensor. */
- public static final int OP_READ_HEART_RATE = AppProtoEnums.APP_OP_READ_HEART_RATE;
+ public static final int OP_READ_HEART_RATE = AppOpEnums.APP_OP_READ_HEART_RATE;
/** @hide Access to read skin temperature. */
- public static final int OP_READ_SKIN_TEMPERATURE = AppProtoEnums.APP_OP_READ_SKIN_TEMPERATURE;
+ public static final int OP_READ_SKIN_TEMPERATURE = AppOpEnums.APP_OP_READ_SKIN_TEMPERATURE;
+
+ /**
+ * Allows an app to range with nearby devices using any ranging technology available.
+ *
+ * @hide
+ */
+ public static final int OP_RANGING = AppOpEnums.APP_OP_RANGING;
+
+ /** @hide Access to read oxygen saturation. */
+ public static final int OP_READ_OXYGEN_SATURATION = AppOpEnums.APP_OP_READ_OXYGEN_SATURATION;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 151;
+ public static final int _NUM_OP = 153;
/**
* All app ops represented as strings.
@@ -1778,6 +1781,8 @@ public class AppOpsManager {
OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS,
OPSTR_READ_HEART_RATE,
OPSTR_READ_SKIN_TEMPERATURE,
+ OPSTR_RANGING,
+ OPSTR_READ_OXYGEN_SATURATION,
})
public @interface AppOpString {}
@@ -2520,11 +2525,21 @@ public class AppOpsManager {
@FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate";
+ /** @hide Access to read oxygen saturation. */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
+ public static final String OPSTR_READ_OXYGEN_SATURATION = "android:read_oxygen_saturation";
+
/** @hide Access to read skin temperature. */
@SystemApi
- @FlaggedApi(Flags.FLAG_PLATFORM_SKIN_TEMPERATURE_ENABLED)
+ @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
+ /** @hide Access to ranging */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_RANGING_PERMISSION_ENABLED)
+ public static final String OPSTR_RANGING = "android:ranging";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2596,11 +2611,13 @@ public class AppOpsManager {
OP_BLUETOOTH_ADVERTISE,
OP_UWB_RANGING,
OP_NEARBY_WIFI_DEVICES,
+ Flags.rangingPermissionEnabled() ? OP_RANGING : OP_NONE,
// Notifications
OP_POST_NOTIFICATION,
// Health
Flags.replaceBodySensorPermissionEnabled() ? OP_READ_HEART_RATE : OP_NONE,
- Flags.platformSkinTemperatureEnabled() ? OP_READ_SKIN_TEMPERATURE : OP_NONE,
+ Flags.replaceBodySensorPermissionEnabled() ? OP_READ_SKIN_TEMPERATURE : OP_NONE,
+ Flags.replaceBodySensorPermissionEnabled() ? OP_READ_OXYGEN_SATURATION : OP_NONE,
};
/**
@@ -3115,9 +3132,18 @@ public class AppOpsManager {
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
new AppOpInfo.Builder(OP_READ_SKIN_TEMPERATURE, OPSTR_READ_SKIN_TEMPERATURE,
"READ_SKIN_TEMPERATURE").setPermission(
- Flags.platformSkinTemperatureEnabled()
+ Flags.replaceBodySensorPermissionEnabled()
? HealthPermissions.READ_SKIN_TEMPERATURE : null)
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_RANGING, OPSTR_RANGING, "RANGING")
+ .setPermission(Flags.rangingPermissionEnabled()?
+ Manifest.permission.RANGING : null)
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_READ_OXYGEN_SATURATION, OPSTR_READ_OXYGEN_SATURATION,
+ "READ_OXYGEN_SATURATION").setPermission(
+ Flags.replaceBodySensorPermissionEnabled()
+ ? HealthPermissions.READ_OXYGEN_SATURATION : null)
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
};
// The number of longs needed to form a full bitmask of app ops
@@ -7807,6 +7833,116 @@ public class AppOpsManager {
}
}
+ private static final String APP_OP_MODE_CACHING_API = "getAppOpMode";
+ private static final String APP_OP_MODE_CACHING_NAME = "appOpModeCache";
+ private static final int APP_OP_MODE_CACHING_SIZE = 2048;
+
+ private static final IpcDataCache.QueryHandler<AppOpModeQuery, Integer> sGetAppOpModeQuery =
+ new IpcDataCache.QueryHandler<>() {
+ @Override
+ public Integer apply(AppOpModeQuery query) {
+ IAppOpsService service = getService();
+ try {
+ return service.checkOperationRawForDevice(query.op, query.uid,
+ query.packageName, query.attributionTag, query.virtualDeviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean shouldBypassCache(@NonNull AppOpModeQuery query) {
+ // If the flag to enable the new caching behavior is off, bypass the cache.
+ return !Flags.appopModeCachingEnabled();
+ }
+ };
+
+ // A LRU cache on binder clients that caches AppOp mode by uid, packageName, virtualDeviceId
+ // and attributionTag.
+ private static final IpcDataCache<AppOpModeQuery, Integer> sAppOpModeCache =
+ new IpcDataCache<>(APP_OP_MODE_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ APP_OP_MODE_CACHING_API, APP_OP_MODE_CACHING_NAME, sGetAppOpModeQuery);
+
+ // Ops that we don't want to cache due to:
+ // 1) Discrepancy of attributionTag support in checkOp and noteOp that determines if a package
+ // can bypass user restriction of an op: b/240617242. COARSE_LOCATION and FINE_LOCATION are
+ // the only two ops that are impacted.
+ private static final SparseBooleanArray OPS_WITHOUT_CACHING = new SparseBooleanArray();
+ static {
+ OPS_WITHOUT_CACHING.put(OP_COARSE_LOCATION, true);
+ OPS_WITHOUT_CACHING.put(OP_FINE_LOCATION, true);
+ }
+
+ private static boolean isAppOpModeCachingEnabled(int opCode) {
+ if (!Flags.appopModeCachingEnabled()) {
+ return false;
+ }
+ return !OPS_WITHOUT_CACHING.get(opCode, false);
+ }
+
+ /**
+ * @hide
+ */
+ public static void invalidateAppOpModeCache() {
+ if (Flags.appopModeCachingEnabled()) {
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, APP_OP_MODE_CACHING_API);
+ }
+ }
+
+ /**
+ * Bypass AppOpModeCache in the local process
+ *
+ * @hide
+ */
+ public static void disableAppOpModeCache() {
+ if (Flags.appopModeCachingEnabled()) {
+ sAppOpModeCache.disableLocal();
+ }
+ }
+
+ private static final class AppOpModeQuery {
+ final int op;
+ final int uid;
+ final String packageName;
+ final int virtualDeviceId;
+ final String attributionTag;
+ final String methodName;
+
+ AppOpModeQuery(int op, int uid, @Nullable String packageName, int virtualDeviceId,
+ @Nullable String attributionTag, @Nullable String methodName) {
+ this.op = op;
+ this.uid = uid;
+ this.packageName = packageName;
+ this.virtualDeviceId = virtualDeviceId;
+ this.attributionTag = attributionTag;
+ this.methodName = methodName;
+ }
+
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("AppOpModeQuery(op=%d, uid=%d, packageName=%s, "
+ + "virtualDeviceId=%d, attributionTag=%s, methodName=%s", op, uid,
+ packageName, virtualDeviceId, attributionTag, methodName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(op, uid, packageName, virtualDeviceId, attributionTag);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null) return false;
+ if (this.getClass() != o.getClass()) return false;
+
+ AppOpModeQuery other = (AppOpModeQuery) o;
+ return op == other.op && uid == other.uid && Objects.equals(packageName,
+ other.packageName) && virtualDeviceId == other.virtualDeviceId
+ && Objects.equals(attributionTag, other.attributionTag);
+ }
+ }
+
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
@@ -8861,12 +8997,16 @@ public class AppOpsManager {
private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName,
int virtualDeviceId) {
try {
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- return mService.checkOperationRaw(op, uid, packageName, null);
+ int mode;
+ if (isAppOpModeCachingEnabled(op)) {
+ mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null,
+ "unsafeCheckOpRawNoThrow"));
} else {
- return mService.checkOperationRawForDevice(
+ mode = mService.checkOperationRawForDevice(
op, uid, packageName, null, virtualDeviceId);
}
+ return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -9051,7 +9191,7 @@ public class AppOpsManager {
SyncNotedAppOp syncOp;
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
} else {
syncOp = mService.noteOperationForDevice(op, uid, packageName, attributionTag,
virtualDeviceId, collectionMode == COLLECT_ASYNC, message,
@@ -9294,8 +9434,21 @@ public class AppOpsManager {
@UnsupportedAppUsage
public int checkOp(int op, int uid, String packageName) {
try {
- int mode = mService.checkOperationForDevice(op, uid, packageName,
- Context.DEVICE_ID_DEFAULT);
+ int mode;
+ if (isAppOpModeCachingEnabled(op)) {
+ mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, Context.DEVICE_ID_DEFAULT, null,
+ "checkOp"));
+ if (mode == MODE_FOREGROUND) {
+ // We only cache raw mode. If the mode is FOREGROUND, we need another binder
+ // call to fetch translated value based on the process state.
+ mode = mService.checkOperationForDevice(op, uid, packageName,
+ Context.DEVICE_ID_DEFAULT);
+ }
+ } else {
+ mode = mService.checkOperationForDevice(op, uid, packageName,
+ Context.DEVICE_ID_DEFAULT);
+ }
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -9334,13 +9487,19 @@ public class AppOpsManager {
private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) {
try {
int mode;
- if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
- mode = mService.checkOperation(op, uid, packageName);
+ if (isAppOpModeCachingEnabled(op)) {
+ mode = sAppOpModeCache.query(
+ new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null,
+ "checkOpNoThrow"));
+ if (mode == MODE_FOREGROUND) {
+ // We only cache raw mode. If the mode is FOREGROUND, we need another binder
+ // call to fetch translated value based on the process state.
+ mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
+ }
} else {
mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId);
}
-
- return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
+ return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2cf718ef364f..3ae60d71facd 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1167,6 +1167,7 @@ class ContextImpl extends Context {
@Override
public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
try {
+ intent.collectExtraIntentKeys();
ActivityTaskManager.getService().startActivityAsUser(
mMainThread.getApplicationThread(), getOpPackageName(), getAttributionTag(),
intent, intent.resolveTypeIfNeeded(getContentResolver()),
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index b06fb9e2f284..233dc75b810f 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -177,6 +177,13 @@ final class DisabledWallpaperManager extends WallpaperManager {
}
@Override
+ @NonNull
+ public SparseArray<Rect> getBitmapCrops(int which) {
+ unsupported();
+ return new SparseArray<>();
+ }
+
+ @Override
public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes,
@Nullable Map<Point, Rect> cropHints) {
return unsupported();
@@ -358,8 +365,9 @@ final class DisabledWallpaperManager extends WallpaperManager {
@Override
- public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints,
- boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
+ public int setStreamWithCrops(@NonNull InputStream bitmapData,
+ @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which
+ ) throws IOException {
return unsupportedInt();
}
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index d1e517bbd03c..16444dc5adde 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -398,6 +398,7 @@ public abstract class ForegroundServiceTypePolicy {
new RegularPermission(Manifest.permission.NFC),
new RegularPermission(Manifest.permission.TRANSMIT_IR),
new RegularPermission(Manifest.permission.UWB_RANGING),
+ new RegularPermission(Manifest.permission.RANGING),
new UsbDevicePermission(),
new UsbAccessoryPermission(),
}, false),
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a97fa18a3599..9bb16ae7fa02 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -124,7 +124,7 @@ interface INotificationManager
boolean onlyHasDefaultChannel(String pkg, int uid);
boolean areChannelsBypassingDnd();
ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid);
- List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels);
+ ParceledListSlice getPackagesBypassingDnd(int userId);
boolean isPackagePaused(String pkg);
void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
boolean isPermissionFixed(String pkg, int userId);
@@ -267,4 +267,7 @@ interface INotificationManager
void setAdjustmentTypeSupportedState(in INotificationListener token, String key, boolean supported);
List<String> getUnsupportedAdjustmentTypes();
+
+ int[] getAllowedAdjustmentKeyTypes();
+ void setAssistantAdjustmentKeyTypeState(int type, boolean enabled);
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index f693e9ba11ec..6449ea1742a1 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -97,6 +97,16 @@ interface IWallpaperManager {
List getBitmapCrops(in List<Point> displaySizes, int which, boolean originalBitmap, int userId);
/**
+ * For a given user, if the wallpaper of the specified which is an ImageWallpaper, return
+ * a bundle which is a Map<Integer, Rect> containing the custom cropHints that were sent to
+ * setBitmapWithCrops or setStreamWithCrops. These crops are relative to the original bitmap.
+ * If the wallpaper isn't an ImageWallpaper, return null.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL)")
+ @SuppressWarnings(value={"untyped-collection"})
+ Bundle getCurrentBitmapCrops(int which, int userId);
+
+ /**
* Return how a bitmap of a given size would be cropped for a given list of display sizes when
* set with the given suggested crops.
* @hide
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c6c0395d1b93..3d9c55c0f37a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -155,7 +155,7 @@ public class Notification implements Parcelable
FOREGROUND_SERVICE_IMMEDIATE,
FOREGROUND_SERVICE_DEFERRED
})
- public @interface ServiceNotificationPolicy {};
+ public @interface ServiceNotificationPolicy {}
/**
* If the Notification associated with starting a foreground service has been
@@ -1754,10 +1754,6 @@ public class Notification implements Parcelable
private Icon mSmallIcon;
@UnsupportedAppUsage
private Icon mLargeIcon;
- private Icon mAppIcon;
-
- /** Cache for whether the notification was posted by a headless system app. */
- private Boolean mBelongsToHeadlessSystemApp = null;
@UnsupportedAppUsage
private String mChannelId;
@@ -3247,86 +3243,6 @@ public class Notification implements Parcelable
}
/**
- * Whether this notification was posted by a headless system app.
- *
- * If we don't have enough information to figure this out, this will return false. Therefore,
- * false negatives are possible, but false positives should not be.
- *
- * @hide
- */
- public boolean belongsToHeadlessSystemApp(Context context) {
- Trace.beginSection("Notification#belongsToHeadlessSystemApp");
-
- try {
- if (mBelongsToHeadlessSystemApp != null) {
- return mBelongsToHeadlessSystemApp;
- }
-
- if (context == null) {
- // Without a valid context, we don't know exactly. Let's assume it doesn't belong to
- // a system app, but not cache the value.
- return false;
- }
-
- ApplicationInfo info = getApplicationInfo(context);
- if (info != null) {
- if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- // It's not a system app at all.
- mBelongsToHeadlessSystemApp = false;
- } else {
- // If there's no launch intent, it's probably a headless app.
- final PackageManager pm = context.getPackageManager();
- mBelongsToHeadlessSystemApp = pm.getLaunchIntentForPackage(info.packageName)
- == null;
- }
- } else {
- // If for some reason we don't have the app info, we don't know; best assume it's
- // not a system app.
- return false;
- }
- return mBelongsToHeadlessSystemApp;
- } finally {
- Trace.endSection();
- }
- }
-
- /**
- * Get the resource ID of the app icon from application info.
- * @hide
- */
- public int getHeaderAppIconRes(Context context) {
- ApplicationInfo info = getApplicationInfo(context);
- if (info != null) {
- return info.icon;
- }
- return 0;
- }
-
- /**
- * Load the app icon drawable from the package manager. This could result in a binder call.
- * @hide
- */
- public Drawable loadHeaderAppIcon(Context context) {
- Trace.beginSection("Notification#loadHeaderAppIcon");
-
- try {
- if (context == null) {
- Log.e(TAG, "Cannot load the app icon drawable with a null context");
- return null;
- }
- final PackageManager pm = context.getPackageManager();
- ApplicationInfo info = getApplicationInfo(context);
- if (info == null) {
- Log.e(TAG, "Cannot load the app icon drawable: no application info");
- return null;
- }
- return pm.getApplicationIcon(info);
- } finally {
- Trace.endSection();
- }
- }
-
- /**
* Fetch the application info from the notification, or the context if that isn't available.
*/
private ApplicationInfo getApplicationInfo(Context context) {
@@ -4361,55 +4277,6 @@ public class Notification implements Parcelable
}
/**
- * The colored app icon that can replace the small icon in the notification starting in V.
- *
- * Before using this value, you should first check whether it's actually being used by the
- * notification by calling {@link Notification#shouldUseAppIcon()}.
- *
- * @hide
- */
- public Icon getAppIcon() {
- if (mAppIcon != null) {
- return mAppIcon;
- }
- // If the app icon hasn't been loaded yet, check if we can load it without a context.
- if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
- final ApplicationInfo info = extras.getParcelable(
- EXTRA_BUILDER_APPLICATION_INFO,
- ApplicationInfo.class);
- if (info != null) {
- int appIconRes = info.icon;
- if (appIconRes == 0) {
- Log.w(TAG, "Failed to get the app icon: no icon in application info");
- return null;
- }
- mAppIcon = Icon.createWithResource(info.packageName, appIconRes);
- return mAppIcon;
- } else {
- Log.e(TAG, "Failed to get the app icon: "
- + "there's an EXTRA_BUILDER_APPLICATION_INFO in extras but it's null");
- }
- } else {
- Log.w(TAG, "Failed to get the app icon: no application info in extras");
- }
- return null;
- }
-
- /**
- * Whether the notification is using the app icon instead of the small icon.
- * @hide
- */
- public boolean shouldUseAppIcon() {
- if (Flags.notificationsUseAppIconInRow()) {
- if (belongsToHeadlessSystemApp(/* context = */ null)) {
- return false;
- }
- return getAppIcon() != null;
- }
- return false;
- }
-
- /**
* The large icon shown in this notification's content view.
* @see Builder#getLargeIcon()
* @see Builder#setLargeIcon(Icon)
@@ -4566,19 +4433,6 @@ public class Notification implements Parcelable
private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
SystemProperties.getBoolean("notifications.only_title", true);
- /**
- * The lightness difference that has to be added to the primary text color to obtain the
- * secondary text color when the background is light.
- */
- private static final int LIGHTNESS_TEXT_DIFFERENCE_LIGHT = 20;
-
- /**
- * The lightness difference that has to be added to the primary text color to obtain the
- * secondary text color when the background is dark.
- * A bit less then the above value, since it looks better on dark backgrounds.
- */
- private static final int LIGHTNESS_TEXT_DIFFERENCE_DARK = -10;
-
private Context mContext;
private Notification mN;
private Bundle mUserExtras = new Bundle();
@@ -5149,7 +5003,7 @@ public class Notification implements Parcelable
/**
* Sets a very short string summarizing the most critical information contained in the
- * notification. Suggested max length is 5 characters, and there is no guarantee how much or
+ * notification. Suggested max length is 7 characters, and there is no guarantee how much or
* how little of this text will be shown.
*/
@FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
@@ -6451,36 +6305,12 @@ public class Notification implements Parcelable
}
private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
- if (Flags.notificationsUseAppIcon()) {
- // Override small icon with app icon
- mN.mSmallIcon = Icon.createWithResource(mContext,
- mN.getHeaderAppIconRes(mContext));
- } else if (mN.mSmallIcon == null && mN.icon != 0) {
+ if (mN.mSmallIcon == null && mN.icon != 0) {
mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
}
-
- boolean usingAppIcon = false;
- if (Flags.notificationsUseAppIconInRow() && !mN.belongsToHeadlessSystemApp(mContext)) {
- // Use the app icon in the view
- int appIconRes = mN.getHeaderAppIconRes(mContext);
- if (appIconRes != 0) {
- mN.mAppIcon = Icon.createWithResource(mContext, appIconRes);
- contentView.setImageViewIcon(R.id.icon, mN.mAppIcon);
- contentView.setBoolean(R.id.icon, "setShouldShowAppIcon", true);
- usingAppIcon = true;
- } else {
- Log.w(TAG, "bindSmallIcon: could not get the app icon");
- }
- }
- if (!usingAppIcon) {
- contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
- }
+ contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
-
- // Don't change color if we're using the app icon.
- if (!Flags.notificationsUseAppIcon() && !usingAppIcon) {
- processSmallIconColor(mN.mSmallIcon, contentView, p);
- }
+ processSmallIconColor(mN.mSmallIcon, contentView, p);
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 768b70c28632..c49b02210dd4 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1848,6 +1848,20 @@ public class NotificationManager {
/**
* @hide
*/
+ @TestApi
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type, boolean enabled) {
+ INotificationManager service = getService();
+ try {
+ service.setAssistantAdjustmentKeyTypeState(type, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
public List<String> getEnabledNotificationListenerPackages() {
INotificationManager service = getService();
try {
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index a39cf84a4ebe..1dc774285a32 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -17,6 +17,7 @@
package android.app;
import static android.text.TextUtils.formatSimple;
+import static com.android.internal.util.Preconditions.checkArgumentPositive;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,7 +32,10 @@ import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,6 +44,7 @@ import com.android.internal.os.BackgroundThread;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import dalvik.annotation.optimization.NeverCompile;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
@@ -90,9 +95,6 @@ public class PropertyInvalidatedCache<Query, Result> {
* caching on behalf of other processes.
*/
public boolean shouldBypassCache(@NonNull Q query) {
- if(android.multiuser.Flags.propertyInvalidatedCacheBypassMismatchedUids()) {
- return Binder.getCallingUid() != Process.myUid();
- }
return false;
}
};
@@ -201,6 +203,23 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * The list of known and legal modules. The list is not sorted.
+ */
+ private static final String[] sValidModule = {
+ MODULE_SYSTEM, MODULE_BLUETOOTH, MODULE_TELEPHONY, MODULE_TEST,
+ };
+
+ /**
+ * Verify that the module string is in the legal list. Throw if it is not.
+ */
+ private static void throwIfInvalidModule(@NonNull String name) {
+ for (int i = 0; i < sValidModule.length; i++) {
+ if (sValidModule[i].equals(name)) return;
+ }
+ throw new IllegalArgumentException("invalid module: " + name);
+ }
+
+ /**
* All legal keys start with one of the following strings.
*/
private static final String[] sValidKeyPrefix = {
@@ -254,8 +273,11 @@ public class PropertyInvalidatedCache<Query, Result> {
// written to global store.
private static final int NONCE_BYPASS = 3;
+ // The largest reserved nonce value. Update this whenever a reserved nonce is added.
+ private static final int MAX_RESERVED_NONCE = NONCE_BYPASS;
+
private static boolean isReservedNonce(long n) {
- return n >= NONCE_UNSET && n <= NONCE_BYPASS;
+ return n >= NONCE_UNSET && n <= MAX_RESERVED_NONCE;
}
/**
@@ -293,7 +315,7 @@ public class PropertyInvalidatedCache<Query, Result> {
private long mMisses = 0;
@GuardedBy("mLock")
- private long[] mSkips = new long[]{ 0, 0, 0, 0 };
+ private long[] mSkips = new long[MAX_RESERVED_NONCE + 1];
@GuardedBy("mLock")
private long mMissOverflow = 0;
@@ -370,8 +392,213 @@ public class PropertyInvalidatedCache<Query, Result> {
}
}
+ /**
+ * An array of hash maps, indexed by calling UID. The class behaves a bit like a hash map
+ * except that it uses the calling UID internally.
+ */
+ private class CacheMap<Query, Result> {
+
+ // Create a new map for a UID, using the parent's configuration for max size.
+ private LinkedHashMap<Query, Result> createMap() {
+ return new LinkedHashMap<Query, Result>(
+ 2 /* start small */,
+ 0.75f /* default load factor */,
+ true /* LRU access order */) {
+ @GuardedBy("mLock")
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ final int size = size();
+ if (size > mHighWaterMark) {
+ mHighWaterMark = size;
+ }
+ if (size > mMaxEntries) {
+ mMissOverflow++;
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+
+ // An array of maps, indexed by UID.
+ private final SparseArray<LinkedHashMap<Query, Result>> mCache = new SparseArray<>();
+
+ // If true, isolate the hash entries by calling UID. If this is false, allow the cache
+ // entries to be combined in a single hash map.
+ private final boolean mIsolated;
+
+ // Collect statistics.
+ private final boolean mStatistics;
+
+ // An array of booleans to indicate if a UID has been involved in a map access. A value
+ // exists for every UID that was ever involved during cache access. This is updated only
+ // if statistics are being collected.
+ private final SparseBooleanArray mUidSeen;
+
+ // A hash map that ignores the UID. This is used in look-aside fashion just for hit/miss
+ // statistics. This is updated only if statistics are being collected.
+ private final ArraySet<Query> mShadowCache;
+
+ // Shadow statistics. Only hits and misses need to be recorded. These are updated only
+ // if statistics are being collected. The "SelfHits" records hits when the UID is the
+ // process uid.
+ private int mShadowHits;
+ private int mShadowMisses;
+ private int mShadowSelfHits;
+
+ // The process UID.
+ private final int mSelfUid;
+
+ // True in test mode. In test mode, the cache uses Binder.getWorkSource() as the UID.
+ private final boolean mTestMode;
+
+ /**
+ * Create a CacheMap. UID isolation is enabled if the input parameter is true and if the
+ * isolation feature is enabled.
+ */
+ CacheMap(boolean isolate, boolean testMode) {
+ mIsolated = Flags.picIsolateCacheByUid() && isolate;
+ mStatistics = Flags.picIsolatedCacheStatistics() && mIsolated;
+ if (mStatistics) {
+ mUidSeen = new SparseBooleanArray();
+ mShadowCache = new ArraySet<>();
+ } else {
+ mUidSeen = null;
+ mShadowCache = null;
+ }
+ mSelfUid = Process.myUid();
+ mTestMode = testMode;
+ }
+
+ // Return the UID for this cache invocation. If uid isolation is disabled, the value of 0
+ // is returned, which effectively places all entries in a single hash map.
+ private int callerUid() {
+ if (!mIsolated) {
+ return 0;
+ } else if (mTestMode) {
+ return Binder.getCallingWorkSourceUid();
+ } else {
+ return Binder.getCallingUid();
+ }
+ }
+
+ /**
+ * Lookup an entry in the cache.
+ */
+ Result get(Query query) {
+ final int uid = callerUid();
+
+ // Shadow statistics
+ if (mStatistics) {
+ if (mShadowCache.contains(query)) {
+ mShadowHits++;
+ if (uid == mSelfUid) {
+ mShadowSelfHits++;
+ }
+ } else {
+ mShadowMisses++;
+ }
+ }
+
+ var map = mCache.get(uid);
+ if (map != null) {
+ return map.get(query);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Remove an entry from the cache.
+ */
+ void remove(Query query) {
+ final int uid = callerUid();
+ if (mStatistics) {
+ mShadowCache.remove(query);
+ }
+
+ var map = mCache.get(uid);
+ if (map != null) {
+ map.remove(query);
+ }
+ }
+
+ /**
+ * Record an entry in the cache.
+ */
+ void put(Query query, Result result) {
+ final int uid = callerUid();
+ if (mStatistics) {
+ mShadowCache.add(query);
+ mUidSeen.put(uid, true);
+ }
+
+ var map = mCache.get(uid);
+ if (map == null) {
+ map = createMap();
+ mCache.put(uid, map);
+ }
+ map.put(query, result);
+ }
+
+ /**
+ * Return the number of entries in the cache.
+ */
+ int size() {
+ int total = 0;
+ for (int i = 0; i < mCache.size(); i++) {
+ var map = mCache.valueAt(i);
+ total += map.size();
+ }
+ return total;
+ }
+
+ /**
+ * Clear the entries in the cache. Update the shadow statistics.
+ */
+ void clear() {
+ if (mStatistics) {
+ mShadowCache.clear();
+ }
+
+ mCache.clear();
+ }
+
+ // Dump basic statistics, if any are collected. Do nothing if statistics are not enabled.
+ void dump(PrintWriter pw) {
+ if (mStatistics) {
+ pw.println(formatSimple(" ShadowHits: %d, ShadowMisses: %d, ShadowSize: %d",
+ mShadowHits, mShadowMisses, mShadowCache.size()));
+ pw.println(formatSimple(" ShadowUids: %d, SelfUid: %d",
+ mUidSeen.size(), mShadowSelfHits));
+ }
+ }
+
+ // Dump detailed statistics
+ void dumpDetailed(PrintWriter pw) {
+ for (int i = 0; i < mCache.size(); i++) {
+ int uid = mCache.keyAt(i);
+ var map = mCache.valueAt(i);
+
+ Set<Map.Entry<Query, Result>> cacheEntries = map.entrySet();
+ if (cacheEntries.size() == 0) {
+ break;
+ }
+
+ pw.println(" Contents:");
+ pw.println(formatSimple(" Uid: %d\n", uid));
+ for (Map.Entry<Query, Result> entry : cacheEntries) {
+ String key = Objects.toString(entry.getKey());
+ String value = Objects.toString(entry.getValue());
+
+ pw.println(formatSimple(" Key: %s\n Value: %s\n", key, value));
+ }
+ }
+ }
+ }
+
@GuardedBy("mLock")
- private final LinkedHashMap<Query, Result> mCache;
+ private final CacheMap<Query, Result> mCache;
/**
* The nonce handler for this cache.
@@ -695,12 +922,10 @@ public class PropertyInvalidatedCache<Query, Result> {
// The shared memory.
private volatile NonceStore mStore;
- // The index of the nonce in shared memory.
+ // The index of the nonce in shared memory. This changes from INVALID only when the local
+ // object is completely initialized.
private volatile int mHandle = NonceStore.INVALID_NONCE_INDEX;
- // True if the string has been stored, ever.
- private volatile boolean mRecorded = false;
-
// A short name that is saved in shared memory. This is the portion of the property name
// that follows the prefix.
private final String mShortName;
@@ -714,48 +939,63 @@ public class PropertyInvalidatedCache<Query, Result> {
}
}
+ // Initialize the mStore and mHandle variables. This function does nothing if the
+ // variables are already initialized. Synchronization ensures that initialization happens
+ // no more than once. The function returns the new value of mHandle.
+ //
+ // If the "update" boolean is true, then the property is registered with the nonce store
+ // before the associated handle is fetched.
+ private int initialize(boolean update) {
+ synchronized (mLock) {
+ int handle = mHandle;
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ if (mStore == null) {
+ mStore = NonceStore.getInstance();
+ if (mStore == null) {
+ return NonceStore.INVALID_NONCE_INDEX;
+ }
+ }
+ if (update) {
+ mStore.storeName(mShortName);
+ }
+ handle = mStore.getHandleForName(mShortName);
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ return NonceStore.INVALID_NONCE_INDEX;
+ }
+ // The handle must be valid.
+ mHandle = handle;
+ }
+ return handle;
+ }
+ }
+
// Fetch the nonce from shared memory. If the shared memory is not available, return
// UNSET. If the shared memory is available but the nonce name is not known (it may not
// have been invalidated by the server yet), return UNSET.
@Override
long getNonceInternal() {
- if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
- if (mStore == null) {
- mStore = NonceStore.getInstance();
- if (mStore == null) {
- return NONCE_UNSET;
- }
- }
- mHandle = mStore.getHandleForName(mShortName);
- if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ int handle = mHandle;
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ handle = initialize(false);
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
return NONCE_UNSET;
}
}
- return mStore.getNonce(mHandle);
+ return mStore.getNonce(handle);
}
- // Set the nonce in shared mmory. If the shared memory is not available, throw an
- // exception. Otherwise, if the nonce name has never been recorded, record it now and
- // fetch the handle for the name. If the handle cannot be created, throw an exception.
+ // Set the nonce in shared memory. If the shared memory is not available or if the nonce
+ // cannot be registered in shared memory, throw an exception.
@Override
void setNonceInternal(long value) {
- if (mHandle == NonceStore.INVALID_NONCE_INDEX || !mRecorded) {
- if (mStore == null) {
- mStore = NonceStore.getInstance();
- if (mStore == null) {
- throw new IllegalStateException("setNonce: shared memory not ready");
- }
- }
- // Always store the name before fetching the handle. storeName() is idempotent
- // but does take a little time, so this code calls it just once.
- mStore.storeName(mShortName);
- mRecorded = true;
- mHandle = mStore.getHandleForName(mShortName);
- if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
- throw new IllegalStateException("setNonce: shared memory store failed");
+ int handle = mHandle;
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ handle = initialize(true);
+ if (handle == NonceStore.INVALID_NONCE_INDEX) {
+ throw new IllegalStateException("unable to assign nonce handle: " + mName);
}
}
- mStore.setNonce(mHandle, value);
+ mStore.setNonce(handle, value);
}
}
@@ -854,6 +1094,87 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * A public argument builder to configure cache behavior. The root instance requires a
+ * module; this is immutable. New instances are created with member methods. It is important
+ * to note that the member methods create new instances: they do not modify 'this'. The api
+ * is allowed to be null in the record constructor to facility reuse of Args instances.
+ * @hide
+ */
+ public static record Args(@NonNull String mModule, @Nullable String mApi,
+ int mMaxEntries, boolean mIsolateUids, boolean mTestMode) {
+
+ // Validation: the module must be one of the known module strings and the maxEntries must
+ // be positive.
+ public Args {
+ throwIfInvalidModule(mModule);
+ checkArgumentPositive(mMaxEntries, "max cache size must be positive");
+ }
+
+ // The base constructor must include the module. Modules do not change in a source file,
+ // so even if the Args is reused, the module will not/should not change. The api is null,
+ // which is not legal, but there is no reasonable default. Clients must call the api
+ // method to set the field properly.
+ public Args(@NonNull String module) {
+ this(module,
+ null, // api
+ 32, // maxEntries
+ true, // isolateUids
+ false // testMode
+ );
+ }
+
+ public Args api(@NonNull String api) {
+ return new Args(mModule, api, mMaxEntries, mIsolateUids, mTestMode);
+ }
+
+ public Args maxEntries(int val) {
+ return new Args(mModule, mApi, val, mIsolateUids, mTestMode);
+ }
+
+ public Args isolateUids(boolean val) {
+ return new Args(mModule, mApi, mMaxEntries, val, mTestMode);
+ }
+
+ public Args testMode(boolean val) {
+ return new Args(mModule, mApi, mMaxEntries, mIsolateUids, val);
+ }
+ }
+
+ /**
+ * Make a new property invalidated cache. The key is computed from the module and api
+ * parameters.
+ *
+ * @param args The cache configuration.
+ * @param cacheName Name of this cache in debug and dumpsys
+ * @param computer The code to compute values that are not in the cache.
+ * @hide
+ */
+ public PropertyInvalidatedCache(@NonNull Args args, @NonNull String cacheName,
+ @Nullable QueryHandler<Query, Result> computer) {
+ mPropertyName = createPropertyName(args.mModule, args.mApi);
+ mCacheName = cacheName;
+ mNonce = getNonceHandler(mPropertyName);
+ mMaxEntries = args.mMaxEntries;
+ mCache = new CacheMap<>(args.mIsolateUids, args.mTestMode);
+ mComputer = (computer != null) ? computer : new DefaultComputer<>(this);
+ registerCache();
+ }
+
+ /**
+ * Burst a property name into module and api. Throw if the key is invalid. This method is
+ * used in to transition legacy cache constructors to the args constructor.
+ */
+ private static Args parseProperty(@NonNull String name) {
+ throwIfInvalidCacheKey(name);
+ // Strip off the leading well-known prefix.
+ String base = name.substring(CACHE_KEY_PREFIX.length() + 1);
+ int dot = base.indexOf(".");
+ String module = base.substring(0, dot);
+ String api = base.substring(dot + 1);
+ return new Args(module).api(api);
+ }
+
+ /**
* Make a new property invalidated cache. This constructor names the cache after the
* property name. New clients should prefer the constructor that takes an explicit
* cache name.
@@ -867,7 +1188,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* @hide
*/
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
- this(maxEntries, propertyName, propertyName);
+ this(parseProperty(propertyName).maxEntries(maxEntries), propertyName, null);
}
/**
@@ -883,13 +1204,7 @@ public class PropertyInvalidatedCache<Query, Result> {
*/
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
- mPropertyName = propertyName;
- mCacheName = cacheName;
- mNonce = getNonceHandler(mPropertyName);
- mMaxEntries = maxEntries;
- mComputer = new DefaultComputer<>(this);
- mCache = createMap();
- registerCache();
+ this(parseProperty(propertyName).maxEntries(maxEntries), cacheName, null);
}
/**
@@ -907,35 +1222,7 @@ public class PropertyInvalidatedCache<Query, Result> {
@TestApi
public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
- mPropertyName = createPropertyName(module, api);
- mCacheName = cacheName;
- mNonce = getNonceHandler(mPropertyName);
- mMaxEntries = maxEntries;
- mComputer = computer;
- mCache = createMap();
- registerCache();
- }
-
- // Create a map. This should be called only from the constructor.
- private LinkedHashMap<Query, Result> createMap() {
- return new LinkedHashMap<Query, Result>(
- 2 /* start small */,
- 0.75f /* default load factor */,
- true /* LRU access order */) {
- @GuardedBy("mLock")
- @Override
- protected boolean removeEldestEntry(Map.Entry eldest) {
- final int size = size();
- if (size > mHighWaterMark) {
- mHighWaterMark = size;
- }
- if (size > mMaxEntries) {
- mMissOverflow++;
- return true;
- }
- return false;
- }
- };
+ this(new Args(module).maxEntries(maxEntries).api(api), cacheName, computer);
}
/**
@@ -1181,7 +1468,8 @@ public class PropertyInvalidatedCache<Query, Result> {
public @Nullable Result query(@NonNull Query query) {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
- if (bypass(query)) {
+ if (!isReservedNonce(currentNonce)
+ && bypass(query)) {
currentNonce = NONCE_BYPASS;
}
for (;;) {
@@ -1649,71 +1937,70 @@ public class PropertyInvalidatedCache<Query, Result> {
return false;
}
- /**
- * helper method to check if dump should be skipped due to zero values
- * @param args takes command arguments to check if -brief is present
- * @return True if dump should be skipped
- */
- private boolean skipDump(String[] args) {
- for (String a : args) {
- if (a.equals(BRIEF)) {
- return (mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
- + mSkips[NONCE_BYPASS] + mHits + mMisses) == 0;
- }
+ @GuardedBy("mLock")
+ private long getSkipsLocked() {
+ int sum = 0;
+ for (int i = 0; i < mSkips.length; i++) {
+ sum += mSkips[i];
}
- return false;
+ return sum;
}
+ // Return true if this cache has had any activity. If the hits, misses, and skips are all
+ // zero then the client never tried to use the cache.
+ private boolean isActive() {
+ synchronized (mLock) {
+ return mHits + mMisses + getSkipsLocked() > 0;
+ }
+ }
+
+ @NeverCompile
private void dumpContents(PrintWriter pw, boolean detailed, String[] args) {
// If the user has requested specific caches and this is not one of them, return
// immediately.
if (detailed && !showDetailed(args)) {
return;
}
+ // Does the user want brief output?
+ boolean brief = false;
+ for (String a : args) brief |= a.equals(BRIEF);
NonceHandler.Stats stats = mNonce.getStats();
synchronized (mLock) {
- if (!skipDump(args)) {
- pw.println(formatSimple(" Cache Name: %s", cacheName()));
- pw.println(formatSimple(" Property: %s", mPropertyName));
- final long skips =
- mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
- + mSkips[NONCE_BYPASS];
- pw.println(formatSimple(
- " Hits: %d, Misses: %d, Skips: %d, Clears: %d",
- mHits, mMisses, skips, mClears));
- pw.println(formatSimple(
- " Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d",
- mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
- mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED]));
- pw.println(formatSimple(
- " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
- mLastSeenNonce, stats.invalidated, stats.corkedInvalidates));
- pw.println(formatSimple(
- " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
- mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
- pw.println(formatSimple(" Enabled: %s", mDisabled ? "false" : "true"));
- pw.println("");
- }
-
- // No specific cache was requested. This is the default, and no details
- // should be dumped.
- if (!detailed) {
- return;
- }
- Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
- if (cacheEntries.size() == 0) {
+ if (brief && !isActive()) {
return;
}
- pw.println(" Contents:");
- for (Map.Entry<Query, Result> entry : cacheEntries) {
- String key = Objects.toString(entry.getKey());
- String value = Objects.toString(entry.getValue());
+ pw.println(formatSimple(" Cache Name: %s", cacheName()));
+ pw.println(formatSimple(" Property: %s", mPropertyName));
+ pw.println(formatSimple(
+ " Hits: %d, Misses: %d, Skips: %d, Clears: %d, Uids: %d",
+ mHits, mMisses, getSkipsLocked(), mClears, mCache.size()));
- pw.println(formatSimple(" Key: %s\n Value: %s\n", key, value));
+ // Print all the skip reasons.
+ pw.format(" Skip-%s: %d", sNonceName[0], mSkips[0]);
+ for (int i = 1; i < mSkips.length; i++) {
+ pw.format(", Skip-%s: %d", sNonceName[i], mSkips[i]);
}
+ pw.println();
+
+ pw.println(formatSimple(
+ " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
+ mLastSeenNonce, stats.invalidated, stats.corkedInvalidates));
+ pw.println(formatSimple(
+ " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
+ mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
+ mCache.dump(pw);
+ pw.println(formatSimple(" Enabled: %s", mDisabled ? "false" : "true"));
+
+ // Dump the contents of the cache.
+ if (detailed) {
+ mCache.dumpDetailed(pw);
+ }
+
+ // Separator between caches.
+ pw.println("");
}
}
@@ -1723,6 +2010,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* specific caches (selection is by cache name or property name); if these switches
* are used then the output includes both cache statistics and cache entries.
*/
+ @NeverCompile
private static void dumpCacheInfo(@NonNull PrintWriter pw, @NonNull String[] args) {
if (!sEnabled) {
pw.println(" Caching is disabled in this process.");
@@ -1755,6 +2043,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* are used then the output includes both cache statistics and cache entries.
* @hide
*/
+ @NeverCompile
public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
// Create a PrintWriter that uses a byte array. The code can safely write to
// this array without fear of blocking. The completed byte array will be sent
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a458b4e45796..f702b85bfcbb 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -174,22 +174,54 @@ public class ResourcesManager {
}
/**
- * Apply the registered library paths to the passed impl object
- * @return the hash code for the current version of the registered paths
+ * Apply the registered library paths to the passed AssetManager. If may create a new
+ * AssetManager if any changes are needed and it isn't allowed to reuse the old one.
+ *
+ * @return new AssetManager and the hash code for the current version of the registered paths
*/
- public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
+ public @NonNull Pair<AssetManager, Integer> updateResourceImplAssetsWithRegisteredLibs(
+ @NonNull AssetManager assets, boolean reuseAssets) {
if (!Flags.registerResourcePaths()) {
- return 0;
+ return new Pair<>(assets, 0);
}
- final var collector = new PathCollector(null);
- final int size = mSharedLibAssetsMap.size();
- for (int i = 0; i < size; i++) {
- final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
- collector.appendKey(libraryKey);
+ final int size;
+ final PathCollector collector;
+
+ synchronized (mLock) {
+ size = mSharedLibAssetsMap.size();
+ if (assets == AssetManager.getSystem()) {
+ return new Pair<>(assets, size);
+ }
+ collector = new PathCollector(resourcesKeyFromAssets(assets));
+ for (int i = 0; i < size; i++) {
+ final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+ collector.appendKey(libraryKey);
+ }
+ }
+ if (collector.isSameAsOriginal()) {
+ return new Pair<>(assets, size);
+ }
+ if (reuseAssets) {
+ assets.addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+ return new Pair<>(assets, size);
+ }
+ final var newAssetsBuilder = new AssetManager.Builder();
+ for (final var asset : assets.getApkAssets()) {
+ if (!asset.isForLoader()) {
+ newAssetsBuilder.addApkAssets(asset);
+ }
}
- impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
- return size;
+ for (final var key : extractApkKeys(collector.collectedKey())) {
+ try {
+ final var asset = loadApkAssets(key);
+ newAssetsBuilder.addApkAssets(asset);
+ } catch (IOException e) {
+ Log.e(TAG, "Couldn't load assets for key " + key, e);
+ }
+ }
+ assets.getLoaders().forEach(newAssetsBuilder::addLoader);
+ return new Pair<>(newAssetsBuilder.build(), size);
}
public static class ApkKey {
@@ -624,6 +656,23 @@ public class ResourcesManager {
return apkKeys;
}
+ private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) {
+ final var libs = new ArrayList<String>();
+ final var overlays = new ArrayList<String>();
+ for (final ApkAssets asset : assets.getApkAssets()) {
+ if (asset.isSystem() || asset.isForLoader()) {
+ continue;
+ }
+ if (asset.isOverlay()) {
+ overlays.add(asset.getAssetPath());
+ } else if (asset.isSharedLib()) {
+ libs.add(asset.getAssetPath());
+ }
+ }
+ return new ResourcesKey(null, null, overlays.toArray(new String[0]),
+ libs.toArray(new String[0]), 0, null, null);
+ }
+
/**
* Creates an AssetManager from the paths within the ResourcesKey.
*
@@ -752,7 +801,7 @@ public class ResourcesManager {
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
- final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
+ final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -1835,31 +1884,32 @@ public class ResourcesManager {
for (int i = 0; i < resourcesCount; i++) {
final WeakReference<Resources> ref = mAllResourceReferences.get(i);
final Resources r = ref != null ? ref.get() : null;
- if (r != null) {
- final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
- if (key != null) {
- final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
- if (impl == null) {
- throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
- }
- r.setImpl(impl);
- } else {
- // ResourcesKey is null which means the ResourcesImpl could belong to a
- // Resources created by application through Resources constructor and was not
- // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
- // have shared library asset paths appended if there are any.
- if (r.getImpl() != null) {
- final ResourcesImpl oldImpl = r.getImpl();
- final AssetManager oldAssets = oldImpl.getAssets();
- // ResourcesImpl constructor will help to append shared library asset paths.
- if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
- final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
- oldImpl.getMetrics(), oldImpl.getConfiguration(),
- oldImpl.getDisplayAdjustments());
+ if (r == null) {
+ continue;
+ }
+ final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+ if (key != null) {
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+ }
+ r.setImpl(impl);
+ } else {
+ // ResourcesKey is null which means the ResourcesImpl could belong to a
+ // Resources created by application through Resources constructor and was not
+ // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
+ // have shared library asset paths appended if there are any.
+ final ResourcesImpl oldImpl = r.getImpl();
+ if (oldImpl != null) {
+ final AssetManager oldAssets = oldImpl.getAssets();
+ // ResourcesImpl constructor will help to append shared library asset paths.
+ if (oldAssets != AssetManager.getSystem()) {
+ if (oldAssets.isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldImpl);
r.setImpl(newImpl);
} else {
- Slog.w(TAG, "Skip appending shared library asset paths for the "
- + "Resource as its assets are not up to date.");
+ Slog.w(TAG, "Skip appending shared library asset paths for "
+ + "the Resources as its assets are not up to date.");
}
}
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 63e039143917..b7285c38290c 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -602,6 +602,15 @@ public class StatusBarManager {
@LoggingOnly
private static final long MEDIA_CONTROL_BLANK_TITLE = 274775190L;
+ /**
+ * Media controls based on {@link android.app.Notification.MediaStyle} notifications will have
+ * actions from the associated {@link androidx.media3.MediaController}, if available.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+ // TODO(b/360196209): Set target SDK to Baklava once available
+ private static final long MEDIA_CONTROL_MEDIA3_ACTIONS = 360196209L;
+
@UnsupportedAppUsage
private Context mContext;
private IStatusBarService mService;
@@ -1270,6 +1279,21 @@ public class StatusBarManager {
}
/**
+ * Checks whether the media controls for a given package should use a Media3 controller
+ *
+ * @param packageName App posting media controls
+ * @param user Current user handle
+ * @return true if Media3 should be used
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ android.Manifest.permission.LOG_COMPAT_CHANGE})
+ public static boolean useMedia3ControllerForApp(String packageName, UserHandle user) {
+ return CompatChanges.isChangeEnabled(MEDIA_CONTROL_MEDIA3_ACTIONS, packageName, user);
+ }
+
+ /**
* Checks whether the supplied activity can {@link Activity#startActivityForResult(Intent, int)}
* a system activity that captures content on the screen to take a screenshot.
*
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 0a4d8f213501..aac963ade726 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -296,22 +296,22 @@ public class TaskInfo {
public boolean isVisibleRequested;
/**
- * Whether this task is sleeping due to sleeping display.
+ * Whether the top activity is to be displayed. See {@link android.R.attr#windowNoDisplay}.
* @hide
*/
- public boolean isSleeping;
+ public boolean isTopActivityNoDisplay;
/**
- * Whether the top activity fillsParent() is false
+ * Whether this task is sleeping due to sleeping display.
* @hide
*/
- public boolean isTopActivityTransparent;
+ public boolean isSleeping;
/**
- * Whether the top activity has specified style floating.
+ * Whether the top activity fillsParent() is false
* @hide
*/
- public boolean isTopActivityStyleFloating;
+ public boolean isTopActivityTransparent;
/**
* The last non-fullscreen bounds the task was launched in or resized to.
@@ -364,8 +364,9 @@ public class TaskInfo {
// Do nothing
}
- private TaskInfo(Parcel source) {
- readFromParcel(source);
+ /** @hide */
+ public TaskInfo(Parcel source) {
+ readTaskFromParcel(source);
}
/**
@@ -482,13 +483,13 @@ public class TaskInfo {
&& isFocused == that.isFocused
&& isVisible == that.isVisible
&& isVisibleRequested == that.isVisibleRequested
+ && isTopActivityNoDisplay == that.isTopActivityNoDisplay
&& isSleeping == that.isSleeping
&& Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId)
&& parentTaskId == that.parentTaskId
&& Objects.equals(topActivity, that.topActivity)
&& isTopActivityTransparent == that.isTopActivityTransparent
- && isTopActivityStyleFloating == that.isTopActivityStyleFloating
- && lastNonFullscreenBounds == this.lastNonFullscreenBounds
+ && Objects.equals(lastNonFullscreenBounds, that.lastNonFullscreenBounds)
&& Objects.equals(capturedLink, that.capturedLink)
&& capturedLinkTimestamp == that.capturedLinkTimestamp
&& requestedVisibleTypes == that.requestedVisibleTypes
@@ -524,7 +525,7 @@ public class TaskInfo {
/**
* Reads the TaskInfo from a parcel.
*/
- void readFromParcel(Parcel source) {
+ void readTaskFromParcel(Parcel source) {
userId = source.readInt();
taskId = source.readInt();
effectiveUid = source.readInt();
@@ -561,11 +562,11 @@ public class TaskInfo {
isFocused = source.readBoolean();
isVisible = source.readBoolean();
isVisibleRequested = source.readBoolean();
+ isTopActivityNoDisplay = source.readBoolean();
isSleeping = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
isTopActivityTransparent = source.readBoolean();
- isTopActivityStyleFloating = source.readBoolean();
lastNonFullscreenBounds = source.readTypedObject(Rect.CREATOR);
capturedLink = source.readTypedObject(Uri.CREATOR);
capturedLinkTimestamp = source.readLong();
@@ -577,8 +578,9 @@ public class TaskInfo {
/**
* Writes the TaskInfo to a parcel.
+ * @hide
*/
- void writeToParcel(Parcel dest, int flags) {
+ public void writeTaskToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeInt(taskId);
dest.writeInt(effectiveUid);
@@ -616,11 +618,11 @@ public class TaskInfo {
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
dest.writeBoolean(isVisibleRequested);
+ dest.writeBoolean(isTopActivityNoDisplay);
dest.writeBoolean(isSleeping);
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
dest.writeBoolean(isTopActivityTransparent);
- dest.writeBoolean(isTopActivityStyleFloating);
dest.writeTypedObject(lastNonFullscreenBounds, flags);
dest.writeTypedObject(capturedLink, flags);
dest.writeLong(capturedLinkTimestamp);
@@ -661,11 +663,11 @@ public class TaskInfo {
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ " isVisibleRequested=" + isVisibleRequested
+ + " isTopActivityNoDisplay=" + isTopActivityNoDisplay
+ " isSleeping=" + isSleeping
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " isTopActivityTransparent=" + isTopActivityTransparent
- + " isTopActivityStyleFloating=" + isTopActivityStyleFloating
+ " lastNonFullscreenBounds=" + lastNonFullscreenBounds
+ " capturedLink=" + capturedLink
+ " capturedLinkTimestamp=" + capturedLinkTimestamp
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 479f3df9affb..abb2dd465576 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -19,9 +19,10 @@ package android.app;
import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT;
+import static android.app.Flags.FLAG_CUSTOMIZATION_PACKS_APIS;
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
-import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
import static com.android.window.flags.Flags.FLAG_MULTI_CROP;
import static com.android.window.flags.Flags.multiCrop;
@@ -342,24 +343,32 @@ public class WallpaperManager {
* Portrait orientation of most screens
* @hide
*/
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
public static final int ORIENTATION_PORTRAIT = 0;
/**
* Landscape orientation of most screens
* @hide
*/
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
public static final int ORIENTATION_LANDSCAPE = 1;
/**
* Portrait orientation with similar width and height (e.g. the inner screen of a foldable)
* @hide
*/
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
public static final int ORIENTATION_SQUARE_PORTRAIT = 2;
/**
* Landscape orientation with similar width and height (e.g. the inner screen of a foldable)
* @hide
*/
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
public static final int ORIENTATION_SQUARE_LANDSCAPE = 3;
/**
@@ -368,7 +377,9 @@ public class WallpaperManager {
* @return the corresponding {@link ScreenOrientation}.
* @hide
*/
- public static @ScreenOrientation int getOrientation(Point screenSize) {
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
+ public static @ScreenOrientation int getOrientation(@NonNull Point screenSize) {
float ratio = ((float) screenSize.x) / screenSize.y;
// ratios between 3/4 and 4/3 are considered square
return ratio >= 4 / 3f ? ORIENTATION_LANDSCAPE
@@ -1623,14 +1634,15 @@ public class WallpaperManager {
* If false, return areas relative to the cropped bitmap.
* @return A List of Rect where the Rect is within the cropped/original bitmap, and corresponds
* to what is displayed. The Rect may have a larger width/height ratio than the screen
- * due to parallax. Return {@code null} if the wallpaper is not an ImageWallpaper.
- * Also return {@code null} when called with which={@link #FLAG_LOCK} if there is a
+ * due to parallax. Return an empty list if the wallpaper is not an ImageWallpaper.
+ * Also return an empty list when called with which={@link #FLAG_LOCK} if there is a
* shared home + lock wallpaper.
* @hide
*/
@FlaggedApi(FLAG_MULTI_CROP)
+ @TestApi
@RequiresPermission(READ_WALLPAPER_INTERNAL)
- @Nullable
+ @NonNull
public List<Rect> getBitmapCrops(@NonNull List<Point> displaySizes,
@SetWallpaperFlags int which, boolean originalBitmap) {
checkExactlyOneWallpaperFlagSet(which);
@@ -1653,6 +1665,52 @@ public class WallpaperManager {
}
/**
+ * For the current user, if the wallpaper of the specified destination is an ImageWallpaper,
+ * return the custom crops of the wallpaper, that have been provided for example via
+ * {@link #setStreamWithCrops}. These crops are relative to the original bitmap.
+ * <p>
+ * This method helps apps that change wallpapers provide an undo option. Calling
+ * {@link #setStreamWithCrops(InputStream, SparseArray, boolean, int)} with this SparseArray and
+ * the current original bitmap file, that can be obtained with {@link #getWallpaperFile(int,
+ * boolean)} with {@code getCropped=false}, will exactly lead to the current wallpaper state.
+ *
+ * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}.
+ * @return A map from {{@link #ORIENTATION_PORTRAIT}, {@link #ORIENTATION_LANDSCAPE},
+ * {@link #ORIENTATION_SQUARE_PORTRAIT}, {{@link #ORIENTATION_SQUARE_LANDSCAPE}}} to
+ * Rect, representing the custom cropHints. The map can be empty and will only contains
+ * entries for screen orientations for which a custom crop was provided. If no custom
+ * crop is provided for an orientation, the system will infer the crop based on the
+ * custom crops of the other orientations; or center-align the full image if no custom
+ * crops are provided at all.
+ * <p>
+ * Return an empty map if the wallpaper is not an ImageWallpaper. Also return
+ * an empty map when called with which={@link #FLAG_LOCK} if there is a shared
+ * home + lock wallpaper.
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
+ @RequiresPermission(READ_WALLPAPER_INTERNAL)
+ @NonNull
+ public SparseArray<Rect> getBitmapCrops(@SetWallpaperFlags int which) {
+ checkExactlyOneWallpaperFlagSet(which);
+ try {
+ Bundle bundle = sGlobals.mService.getCurrentBitmapCrops(which, mContext.getUserId());
+ SparseArray<Rect> result = new SparseArray<>();
+ if (bundle == null) return result;
+ for (String key : bundle.keySet()) {
+ int intKey = Integer.parseInt(key);
+ Rect rect = bundle.getParcelable(key, Rect.class);
+ result.put(intKey, rect);
+ }
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* For preview purposes.
* Return how a bitmap of a given size would be cropped for a given list of display sizes, if
* it was set as wallpaper via {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or
@@ -1664,7 +1722,8 @@ public class WallpaperManager {
* @hide
*/
@FlaggedApi(FLAG_MULTI_CROP)
- @Nullable
+ @TestApi
+ @NonNull
public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes,
@Nullable Map<Point, Rect> cropHints) {
try {
@@ -1890,9 +1949,14 @@ public class WallpaperManager {
* defined kind of wallpaper, either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}.
* @param getCropped If true the cropped file will be retrieved, if false the original will
* be retrieved.
- *
+ * @return A ParcelFileDescriptor for the wallpaper bitmap of the given destination, if it's an
+ * ImageWallpaper wallpaper. Return {@code null} if the wallpaper is not an
+ * ImageWallpaper. Also return {@code null} when called with
+ * which={@link #FLAG_LOCK} if there is a shared home + lock wallpaper.
* @hide
*/
+ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
+ @SystemApi
@Nullable
public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which, boolean getCropped) {
return getWallpaperFile(which, mContext.getUserId(), getCropped);
@@ -2371,7 +2435,6 @@ public class WallpaperManager {
/**
* Version of setBitmap that defines how the wallpaper will be positioned for different
* display sizes.
- * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}.
* @param cropHints map from screen dimensions to a sub-region of the image to display for those
* dimensions. The {@code Rect} sub-region may have a larger width/height ratio
* than the screen dimensions to apply a horizontal parallax effect. If the
@@ -2380,6 +2443,7 @@ public class WallpaperManager {
* @hide
*/
@FlaggedApi(FLAG_MULTI_CROP)
+ @TestApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints,
boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
@@ -2562,7 +2626,6 @@ public class WallpaperManager {
/**
* Version of setStream that defines how the wallpaper will be positioned for different
* display sizes.
- * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}.
* @param cropHints map from screen dimensions to a sub-region of the image to display for those
* dimensions. The {@code Rect} sub-region may have a larger width/height ratio
* than the screen dimensions to apply a horizontal parallax effect. If the
@@ -2571,9 +2634,11 @@ public class WallpaperManager {
* @hide
*/
@FlaggedApi(FLAG_MULTI_CROP)
+ @TestApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
- public int setStreamWithCrops(InputStream bitmapData, @NonNull Map<Point, Rect> cropHints,
- boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
+ public int setStreamWithCrops(@NonNull InputStream bitmapData,
+ @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which)
+ throws IOException {
SparseArray<Rect> crops = new SparseArray<>();
cropHints.forEach((k, v) -> crops.put(getOrientation(k), v));
return setStreamWithCrops(bitmapData, crops, allowBackup, which);
@@ -2583,15 +2648,21 @@ public class WallpaperManager {
* Similar to {@link #setStreamWithCrops(InputStream, Map, boolean, int)}, but using
* {@link ScreenOrientation} as keys of the cropHints map. Used for backup & restore, since
* WallpaperBackupAgent stores orientations rather than the exact display size.
- * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}.
+ * @param bitmapData A stream containing the raw data to install as a wallpaper. This
+ * data can be in any format handled by {@link BitmapRegionDecoder}.
* @param cropHints map from {@link ScreenOrientation} to a sub-region of the image to display
* for that screen orientation.
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
* @hide
*/
@FlaggedApi(FLAG_MULTI_CROP)
+ @SystemApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
- public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints,
- boolean allowBackup, @SetWallpaperFlags int which) throws IOException {
+ public int setStreamWithCrops(@NonNull InputStream bitmapData,
+ @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which)
+ throws IOException {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
diff --git a/core/java/android/app/ZenBypassingApp.java b/core/java/android/app/ZenBypassingApp.java
new file mode 100644
index 000000000000..89bcfa2d2e7d
--- /dev/null
+++ b/core/java/android/app/ZenBypassingApp.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class ZenBypassingApp implements Parcelable {
+
+ @NonNull private String mPkg;
+ private boolean mAllChannelsBypass;
+
+
+ public ZenBypassingApp(@NonNull String pkg, boolean allChannelsBypass) {
+ mPkg = pkg;
+ mAllChannelsBypass = allChannelsBypass;
+ }
+
+ public ZenBypassingApp(Parcel source) {
+ mPkg = source.readString();
+ mAllChannelsBypass = source.readBoolean();
+ }
+
+ @NonNull
+ public String getPkg() {
+ return mPkg;
+ }
+
+ public boolean doAllChannelsBypass() {
+ return mAllChannelsBypass;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mPkg);
+ dest.writeBoolean(mAllChannelsBypass);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<ZenBypassingApp> CREATOR
+ = new Parcelable.Creator<ZenBypassingApp>() {
+ @Override
+ public ZenBypassingApp createFromParcel(Parcel source) {
+ return new ZenBypassingApp(source);
+ }
+ @Override
+ public ZenBypassingApp[] newArray(int size) {
+ return new ZenBypassingApp[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ZenBypassingApp)) return false;
+ ZenBypassingApp that = (ZenBypassingApp) o;
+ return mAllChannelsBypass == that.mAllChannelsBypass && Objects.equals(mPkg,
+ that.mPkg);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPkg, mAllChannelsBypass);
+ }
+
+ @Override
+ public String toString() {
+ return "ZenBypassingApp{" +
+ "mPkg='" + mPkg + '\'' +
+ ", mAllChannelsBypass=" + mAllChannelsBypass +
+ '}';
+ }
+}
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index c0e435c04d3c..35149b5a3135 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -191,6 +191,12 @@ public final class DevicePolicyIdentifiers {
public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
/**
+ * String identifier for {@link DevicePolicyManager#setMtePolicy(int)}.
+ */
+ @FlaggedApi(android.app.admin.flags.Flags.FLAG_SET_MTE_POLICY_COEXISTENCE)
+ public static final String MEMORY_TAGGING_POLICY = "memoryTagging";
+
+ /**
* @hide
*/
public static final String USER_RESTRICTION_PREFIX = "userRestriction_";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 102540c010ae..bff77f951b9f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -55,8 +55,10 @@ import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_REMOVE_MANAGED_PROFILE_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
+import static android.app.admin.flags.Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -8968,12 +8970,9 @@ public class DevicePolicyManager {
/**
* Called by a device owner, a profile owner for the primary user or a profile
* owner of an organization-owned managed profile to turn auto time zone on and off.
- * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
- * to prevent the user from changing this setting.
* <p>
- * If user restriction {@link UserManager#DISALLOW_CONFIG_DATE_TIME} is used,
- * no user will be able set the date and time zone. Instead, the network date
- * and time zone will be used.
+ * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} to prevent the
+ * user from changing this setting, that way no user will be able set the date and time zone.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with or Null if the
* caller is not a device admin.
@@ -8981,13 +8980,17 @@ public class DevicePolicyManager {
* @throws SecurityException if caller is not a device owner, a profile owner for the
* primary user, or a profile owner of an organization-owned managed profile.
*/
- @SupportsCoexistence
@RequiresPermission(value = SET_TIME_ZONE, conditional = true)
public void setAutoTimeZoneEnabled(@Nullable ComponentName admin, boolean enabled) {
throwIfParentInstance("setAutoTimeZone");
if (mService != null) {
try {
- mService.setAutoTimeZoneEnabled(admin, mContext.getPackageName(), enabled);
+ if (Flags.setAutoTimeZoneEnabledCoexistence()) {
+ mService.setAutoTimeZonePolicy(mContext.getPackageName(),
+ enabled ? AUTO_TIME_ZONE_ENABLED : AUTO_TIME_ZONE_DISABLED );
+ } else {
+ mService.setAutoTimeZoneEnabled(admin, mContext.getPackageName(), enabled);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -9017,6 +9020,96 @@ public class DevicePolicyManager {
}
/**
+ * Specifies that the auto time zone state is not controlled by device policy.
+ *
+ * @see #setAutoTimeZonePolicy(int)
+ */
+ @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+ public static final int AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY = 0;
+
+ /**
+ * Specifies the "disabled" auto time zone state.
+ *
+ * @see #setAutoTimeZonePolicy(int)
+ */
+ @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+ public static final int AUTO_TIME_ZONE_DISABLED = 1;
+
+ /**
+ * Specifies the "enabled" auto time zone state.
+ *
+ * @see #setAutoTimeZonePolicy(int)
+ */
+ @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+ public static final int AUTO_TIME_ZONE_ENABLED = 2;
+
+ /**
+ * Flags supplied to {@link #setAutoTimeZonePolicy}(int)}.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "AUTO_TIME_ZONE_" }, value = {
+ AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY,
+ AUTO_TIME_ZONE_DISABLED,
+ AUTO_TIME_ZONE_ENABLED
+ })
+ public @interface AutoTimeZonePolicy {}
+
+ /**
+ * Called by a device owner, a profile owner for the primary user or a profile owner of an
+ * organization-owned managed profile to turn auto time zone on and off.
+ * <p>
+ * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} to prevent the
+ * user from changing this setting, that way no user will be able set the date and time zone.
+ *
+ * @param policy The desired state among {@link #AUTO_TIME_ZONE_ENABLED} to enable it,
+ * {@link #AUTO_TIME_ZONE_DISABLED} to disable it or
+ * {@link #AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY} to unset the policy.
+ * @throws SecurityException if caller is not a device owner, a profile owner for the primary
+ * user, or a profile owner of an organization-owned managed profile, or if the caller does not
+ * hold the required permission.
+ */
+ @SupportsCoexistence
+ @RequiresPermission(value = SET_TIME_ZONE, conditional = true)
+ @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+ public void setAutoTimeZonePolicy(@AutoTimeZonePolicy int policy) {
+ throwIfParentInstance("setAutoTimeZonePolicy");
+ if (mService != null) {
+ try {
+ mService.setAutoTimeZonePolicy(mContext.getPackageName(), policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns auto time zone policy's current state.
+ *
+ * @return One of {@link #AUTO_TIME_ZONE_ENABLED} if enabled, {@link #AUTO_TIME_ZONE_DISABLED}
+ * if disabled and {@link #AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY} if the state is not
+ * controlled by policy.
+ * @throws SecurityException if caller is not a device owner, a profile owner for the
+ * primary user, or a profile owner of an organization-owned managed profile, or if the caller
+ * does not hold the required permission.
+ */
+ @SupportsCoexistence
+ @RequiresPermission(anyOf = {SET_TIME_ZONE, QUERY_ADMIN_POLICY}, conditional = true)
+ @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE)
+ public @AutoTimeZonePolicy int getAutoTimeZonePolicy() {
+ throwIfParentInstance("getAutoTimeZonePolicy");
+ if (mService != null) {
+ try {
+ return mService.getAutoTimeZonePolicy(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY;
+ }
+
+ /**
* TODO (b/137101239): remove this method in follow-up CL
* since it's only used for split system user.
* Called by a device owner to set whether all users created on the device should be ephemeral.
@@ -12550,28 +12643,43 @@ public class DevicePolicyManager {
* @param enabled Whether or not the lockscreen needs to be shown.
* @throws SecurityException if {@code admin} is not a device or profile owner.
* @see #isSecondaryLockscreenEnabled
+ * @deprecated Use {@link #setSecondaryLockscreenEnabled(boolean,PersistableBundle)} instead.
* @hide
- **/
+ */
+ @Deprecated
@SystemApi
+ @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
- setSecondaryLockscreenEnabled(admin, enabled, null);
+ throwIfParentInstance("setSecondaryLockscreenEnabled");
+ if (mService != null) {
+ try {
+ mService.setSecondaryLockscreenEnabled(admin, enabled, null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
* Called by the system supervision app to set whether a secondary lockscreen needs to be shown.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
- * caller is not a device admin.
+ * <p>The secondary lockscreen will by displayed after the primary keyguard security screen
+ * requirements are met.
+ *
+ * <p>This API, and associated APIs, can only be called by the default supervision app.
+ *
* @param enabled Whether or not the lockscreen needs to be shown.
* @param options A {@link PersistableBundle} to supply options to the lock screen.
* @hide
*/
- public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled,
+ @SystemApi
+ @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
+ public void setSecondaryLockscreenEnabled(boolean enabled,
@Nullable PersistableBundle options) {
throwIfParentInstance("setSecondaryLockscreenEnabled");
if (mService != null) {
try {
- mService.setSecondaryLockscreenEnabled(admin, enabled, options);
+ mService.setSecondaryLockscreenEnabled(null, enabled, options);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -16962,6 +17070,30 @@ public class DevicePolicyManager {
}
/**
+ * Removes a manged profile from the device only when called from a managed profile's context
+ *
+ * @param user UserHandle of the profile to be removed
+ * @return {@code true} when removal of managed profile was successful, {@code false} when
+ * removal was unsuccessful or throws IllegalArgumentException when provided user was not a
+ * managed profile
+ * @hide
+ */
+ @SystemApi
+ @UserHandleAware
+ @FlaggedApi(FLAG_REMOVE_MANAGED_PROFILE_ENABLED)
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public boolean removeManagedProfile() {
+ if (mService == null) {
+ throw new IllegalStateException("Could not find DevicePolicyManagerService");
+ }
+ try {
+ return mService.removeManagedProfile(myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called when a managed profile has been provisioned.
*
* @throws SecurityException if the caller does not hold
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a4e2b8f62a23..0b8f53881d07 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -378,6 +378,9 @@ interface IDevicePolicyManager {
void setAutoTimeZoneEnabled(in ComponentName who, String callerPackageName, boolean enabled);
boolean getAutoTimeZoneEnabled(in ComponentName who, String callerPackageName);
+ void setAutoTimeZonePolicy(String callerPackageName, int policy);
+ int getAutoTimeZonePolicy(String callerPackageName);
+
void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers);
boolean getForceEphemeralUsers(in ComponentName who);
@@ -567,6 +570,8 @@ interface IDevicePolicyManager {
void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount);
+ boolean removeManagedProfile(int userId);
+
void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType);
int getDeviceOwnerType(in ComponentName admin);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 3aaca25eca15..404471e266d2 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -276,6 +276,16 @@ flag {
}
flag {
+ name: "suspend_packages_coexistence"
+ namespace: "enterprise"
+ description: "Migrate setPackagesSuspended for unmanaged mode"
+ bug: "335624297"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "backup_connected_apps_settings"
namespace: "enterprise"
description: "backup and restore connected work and personal apps user settings across devices"
@@ -333,13 +343,20 @@ flag {
}
flag {
- name: "user_provisioning_same_state"
- namespace: "enterprise"
- description: "Handle exceptions while setting same provisioning state."
- bug: "326441417"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ name: "user_provisioning_same_state"
+ namespace: "enterprise"
+ description: "Handle exceptions while setting same provisioning state."
+ bug: "326441417"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "remove_managed_profile_enabled"
+ namespace: "enterprise"
+ description: "API that removes a given managed profile."
+ bug: "372652841"
}
flag {
@@ -349,3 +366,26 @@ flag {
description: "Enables coexistence support for Setting MTE policy."
bug: "376213673"
}
+
+flag {
+ name: "enable_supervision_service_sync"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Allows DPMS to enable or disable SupervisionService based on whether the device is being managed by the supervision role holder."
+ bug: "376213673"
+}
+
+flag {
+ name: "split_create_managed_profile_enabled"
+ namespace: "enterprise"
+ description: "Split up existing create and provision managed profile API."
+ bug: "375382324"
+}
+
+flag {
+ name: "secondary_lockscreen_api_enabled"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Add new API for secondary lockscreen"
+ bug: "336297680"
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionException.aidl b/core/java/android/app/appfunctions/AppFunctionException.aidl
new file mode 100644
index 000000000000..7d432243b1b2
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionException.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.app.appfunctions;
+
+import android.app.appfunctions.AppFunctionException;
+
+parcelable AppFunctionException; \ No newline at end of file
diff --git a/core/java/android/app/appfunctions/AppFunctionException.java b/core/java/android/app/appfunctions/AppFunctionException.java
new file mode 100644
index 000000000000..cbd1d932ab00
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionException.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** Represents an app function related errors. */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public final class AppFunctionException extends Exception implements Parcelable {
+ /**
+ * The caller does not have the permission to execute an app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_DENIED = 1000;
+
+ /**
+ * The caller supplied invalid arguments to the execution request.
+ *
+ * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_INVALID_ARGUMENT = 1001;
+
+ /**
+ * The caller tried to execute a disabled app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_DISABLED = 1002;
+
+ /**
+ * The caller tried to execute a function that does not exist.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_FUNCTION_NOT_FOUND = 1003;
+
+ /**
+ * An internal unexpected error coming from the system.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_SYSTEM_ERROR = 2000;
+
+ /**
+ * The operation was cancelled. Use this error code to report that a cancellation is done after
+ * receiving a cancellation signal.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_CANCELLED = 2001;
+
+ /**
+ * An unknown error occurred while processing the call in the AppFunctionService.
+ *
+ * <p>This error is thrown when the service is connected in the remote application but an
+ * unexpected error is thrown from the bound application.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
+ */
+ public static final int ERROR_APP_UNKNOWN_ERROR = 3000;
+
+ /**
+ * The error category is unknown.
+ *
+ * <p>This is the default value for {@link #getErrorCategory}.
+ */
+ public static final int ERROR_CATEGORY_UNKNOWN = 0;
+
+ /**
+ * The error is caused by the app requesting a function execution.
+ *
+ * <p>For example, the caller provided invalid parameters in the execution request e.g. an
+ * invalid function ID.
+ *
+ * <p>Errors in the category fall in the range 1000-1999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
+
+ /**
+ * The error is caused by an issue in the system.
+ *
+ * <p>For example, the AppFunctionService implementation is not found by the system.
+ *
+ * <p>Errors in the category fall in the range 2000-2999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_SYSTEM = 2;
+
+ /**
+ * The error is caused by the app providing the function.
+ *
+ * <p>For example, the app crashed when the system is executing the request.
+ *
+ * <p>Errors in the category fall in the range 3000-3999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_APP = 3;
+
+ private final int mErrorCode;
+ @Nullable private final String mErrorMessage;
+ @NonNull private final Bundle mExtras;
+
+ /**
+ * @param errorCode The error code.
+ * @param errorMessage The error message.
+ */
+ public AppFunctionException(@ErrorCode int errorCode, @Nullable String errorMessage) {
+ this(errorCode, errorMessage, Bundle.EMPTY);
+ }
+
+ /**
+ * @param errorCode The error code.
+ * @param errorMessage The error message.
+ * @param extras The extras associated with this error.
+ */
+ public AppFunctionException(
+ @ErrorCode int errorCode, @Nullable String errorMessage, @NonNull Bundle extras) {
+ super(errorMessage);
+ mErrorCode = errorCode;
+ mErrorMessage = errorMessage;
+ mExtras = Objects.requireNonNull(extras);
+ }
+
+ private AppFunctionException(@NonNull Parcel in) {
+ mErrorCode = in.readInt();
+ mErrorMessage = in.readString8();
+ mExtras = Objects.requireNonNull(in.readBundle(getClass().getClassLoader()));
+ }
+
+ /** Returns one of the {@code ERROR} constants. */
+ @ErrorCode
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /** Returns the error message. */
+ @Nullable
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ /**
+ * Returns the error category.
+ *
+ * <p>This method categorizes errors based on their underlying cause, allowing developers to
+ * implement targeted error handling and provide more informative error messages to users. It
+ * maps ranges of error codes to specific error categories.
+ *
+ * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the error code does not belong to
+ * any error category.
+ *
+ * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
+ * error code ranges.
+ */
+ @ErrorCategory
+ public int getErrorCategory() {
+ if (mErrorCode >= 1000 && mErrorCode < 2000) {
+ return ERROR_CATEGORY_REQUEST_ERROR;
+ }
+ if (mErrorCode >= 2000 && mErrorCode < 3000) {
+ return ERROR_CATEGORY_SYSTEM;
+ }
+ if (mErrorCode >= 3000 && mErrorCode < 4000) {
+ return ERROR_CATEGORY_APP;
+ }
+ return ERROR_CATEGORY_UNKNOWN;
+ }
+
+ /** Returns any extras associated with this error. */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mErrorCode);
+ dest.writeString8(mErrorMessage);
+ dest.writeBundle(mExtras);
+ }
+
+ /**
+ * Error codes.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"ERROR_"},
+ value = {
+ ERROR_DENIED,
+ ERROR_APP_UNKNOWN_ERROR,
+ ERROR_FUNCTION_NOT_FOUND,
+ ERROR_SYSTEM_ERROR,
+ ERROR_INVALID_ARGUMENT,
+ ERROR_DISABLED,
+ ERROR_CANCELLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCode {}
+
+ /**
+ * Error categories.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"ERROR_CATEGORY_"},
+ value = {
+ ERROR_CATEGORY_UNKNOWN,
+ ERROR_CATEGORY_REQUEST_ERROR,
+ ERROR_CATEGORY_APP,
+ ERROR_CATEGORY_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCategory {}
+
+ @NonNull
+ public static final Creator<AppFunctionException> CREATOR =
+ new Creator<>() {
+ @Override
+ public AppFunctionException createFromParcel(Parcel in) {
+ return new AppFunctionException(in);
+ }
+
+ @Override
+ public AppFunctionException[] newArray(int size) {
+ return new AppFunctionException[size];
+ }
+ };
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 5ddb590add4c..ed088fed41c2 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -16,7 +16,7 @@
package android.app.appfunctions;
-import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode;
+import static android.app.appfunctions.AppFunctionException.ERROR_SYSTEM_ERROR;
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import android.Manifest;
@@ -39,7 +39,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Provides access to app functions.
@@ -147,16 +146,16 @@ public final class AppFunctionManager {
* @param request the request to execute the app function
* @param executor the executor to run the callback
* @param cancellationSignal the cancellation signal to cancel the execution.
- * @param callback the callback to receive the function execution result.
+ * @param callback the callback to receive the function execution result or error.
* <p>If the calling app does not own the app function or does not have {@code
* android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
* android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
- * ExecuteAppFunctionResponse.RESULT_DENIED}.
+ * AppFunctionException.ERROR_DENIED}.
* <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS} but the
* function requires {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}, the execution
- * result will contain {@code ExecuteAppFunctionResponse.RESULT_DENIED}
+ * result will contain {@code AppFunctionException.ERROR_DENIED}
* <p>If the function requested for execution is disabled, then the execution result will
- * contain {@code ExecuteAppFunctionResponse.RESULT_DISABLED}
+ * contain {@code AppFunctionException.ERROR_DISABLED}
* <p>If the cancellation signal is issued, the operation is cancelled and no response is
* returned to the caller.
*/
@@ -171,7 +170,9 @@ public final class AppFunctionManager {
@NonNull ExecuteAppFunctionRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+ @NonNull
+ OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
+ callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -186,20 +187,25 @@ public final class AppFunctionManager {
aidlRequest,
new IExecuteAppFunctionCallback.Stub() {
@Override
- public void onResult(ExecuteAppFunctionResponse result) {
+ public void onSuccess(ExecuteAppFunctionResponse result) {
try {
- executor.execute(() -> callback.accept(result));
+ executor.execute(() -> callback.onResult(result));
} catch (RuntimeException e) {
// Ideally shouldn't happen since errors are wrapped into
- // the
- // response, but we catch it here for additional safety.
- callback.accept(
- ExecuteAppFunctionResponse.newFailure(
- getResultCode(e),
- e.getMessage(),
- /* extras= */ null));
+ // the response, but we catch it here for additional safety.
+ executor.execute(
+ () ->
+ callback.onError(
+ new AppFunctionException(
+ ERROR_SYSTEM_ERROR,
+ e.getMessage())));
}
}
+
+ @Override
+ public void onError(AppFunctionException exception) {
+ executor.execute(() -> callback.onError(exception));
+ }
});
if (cancellationTransport != null) {
cancellationSignal.setRemote(cancellationTransport);
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index 06d95f5270c3..3ddda228d145 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -213,9 +213,7 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
setEnabled(original.getEnabled());
}
- /**
- * Sets an indicator specifying the function enabled state.
- */
+ /** Sets an indicator specifying the function enabled state. */
@NonNull
public Builder setEnabled(@EnabledState int enabledState) {
if (enabledState != APP_FUNCTION_STATE_DEFAULT
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 63d187aa11ef..85b6ab2b4e61 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -17,7 +17,6 @@
package android.app.appfunctions;
import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
-import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode;
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -32,10 +31,8 @@ import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.function.Consumer;
/**
* Abstract base class to provide app functions to the system.
@@ -80,7 +77,9 @@ public abstract class AppFunctionService extends Service {
@NonNull ExecuteAppFunctionRequest request,
@NonNull String callingPackage,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+ @NonNull
+ OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
+ callback);
}
/** @hide */
@@ -105,13 +104,22 @@ public abstract class AppFunctionService extends Service {
request,
callingPackage,
buildCancellationSignal(cancellationCallback),
- safeCallback::onResult);
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(ExecuteAppFunctionResponse result) {
+ safeCallback.onResult(result);
+ }
+
+ @Override
+ public void onError(AppFunctionException exception) {
+ safeCallback.onError(exception);
+ }
+ });
} catch (Exception ex) {
// Apps should handle exceptions. But if they don't, report the error on
// behalf of them.
- safeCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- getResultCode(ex), ex.getMessage(), /* extras= */ null));
+ safeCallback.onError(
+ new AppFunctionException(toErrorCode(ex), ex.getMessage()));
}
}
};
@@ -164,12 +172,26 @@ public abstract class AppFunctionService extends Service {
* @param request The function execution request.
* @param callingPackage The package name of the app that is requesting the execution.
* @param cancellationSignal A signal to cancel the execution.
- * @param callback A callback to report back the result.
+ * @param callback A callback to report back the result or error.
*/
@MainThread
public abstract void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull String callingPackage,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+ @NonNull
+ OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
+ callback);
+
+ /**
+ * Returns result codes from throwable.
+ *
+ * @hide
+ */
+ private static @AppFunctionException.ErrorCode int toErrorCode(@NonNull Throwable t) {
+ if (t instanceof IllegalArgumentException) {
+ return AppFunctionException.ERROR_INVALID_ARGUMENT;
+ }
+ return AppFunctionException.ERROR_APP_UNKNOWN_ERROR;
+ }
}
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
index a23f842e6eeb..1869d22ea080 100644
--- a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
@@ -38,7 +38,7 @@ public class AppFunctionStaticMetadataHelper {
public static final String STATIC_SCHEMA_TYPE = "AppFunctionStaticMetadata";
public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault";
public static final String STATIC_PROPERTY_RESTRICT_CALLERS_WITH_EXECUTE_APP_FUNCTIONS =
- "restrictCallersWithExecuteAppFunctions";
+ "restrictCallersWithExecuteAppFunctions";
public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions";
public static final String PROPERTY_FUNCTION_ID = "functionId";
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index 41bb62270e9f..1557815a8468 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -111,8 +111,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable {
* Returns the function parameters. The key is the parameter name, and the value is the
* parameter value.
*
- * <p>The bundle may have missing parameters. Developers are advised to implement defensive
- * handling measures.
+ * <p>The {@link GenericDocument} may have missing parameters. Developers are advised to
+ * implement defensive handling measures.
*
* @see AppFunctionManager on how to determine the expected parameters.
*/
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index cdf02e6f5a09..acad43b782e5 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -19,16 +19,12 @@ package android.app.appfunctions;
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.appsearch.GenericDocument;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/** The response to an app function execution. */
@@ -45,10 +41,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
Bundle extras =
Objects.requireNonNull(
parcel.readBundle(Bundle.class.getClassLoader()));
- int resultCode = parcel.readInt();
- String errorMessage = parcel.readString8();
- return new ExecuteAppFunctionResponse(
- resultWrapper, extras, resultCode, errorMessage);
+ return new ExecuteAppFunctionResponse(resultWrapper.getValue(), extras);
}
@Override
@@ -71,113 +64,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
*
* <p>See {@link #getResultDocument} for more information on extracting the return value.
*/
- public static final String PROPERTY_RETURN_VALUE = "returnValue";
-
- /**
- * The call was successful.
- *
- * <p>This result code does not belong in an error category.
- */
- public static final int RESULT_OK = 0;
-
- /**
- * The caller does not have the permission to execute an app function.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_DENIED = 1000;
-
- /**
- * The caller supplied invalid arguments to the execution request.
- *
- * <p>This error may be considered similar to {@link IllegalArgumentException}.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_INVALID_ARGUMENT = 1001;
-
- /**
- * The caller tried to execute a disabled app function.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_DISABLED = 1002;
-
- /**
- * The caller tried to execute a function that does not exist.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_FUNCTION_NOT_FOUND = 1003;
-
- /**
- * An internal unexpected error coming from the system.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
- */
- public static final int RESULT_SYSTEM_ERROR = 2000;
-
- /**
- * The operation was cancelled. Use this error code to report that a cancellation is done after
- * receiving a cancellation signal.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
- */
- public static final int RESULT_CANCELLED = 2001;
-
- /**
- * An unknown error occurred while processing the call in the AppFunctionService.
- *
- * <p>This error is thrown when the service is connected in the remote application but an
- * unexpected error is thrown from the bound application.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
- */
- public static final int RESULT_APP_UNKNOWN_ERROR = 3000;
-
- /**
- * The error category is unknown.
- *
- * <p>This is the default value for {@link #getErrorCategory}.
- */
- public static final int ERROR_CATEGORY_UNKNOWN = 0;
-
- /**
- * The error is caused by the app requesting a function execution.
- *
- * <p>For example, the caller provided invalid parameters in the execution request e.g. an
- * invalid function ID.
- *
- * <p>Errors in the category fall in the range 1000-1999 inclusive.
- */
- public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
-
- /**
- * The error is caused by an issue in the system.
- *
- * <p>For example, the AppFunctionService implementation is not found by the system.
- *
- * <p>Errors in the category fall in the range 2000-2999 inclusive.
- */
- public static final int ERROR_CATEGORY_SYSTEM = 2;
-
- /**
- * The error is caused by the app providing the function.
- *
- * <p>For example, the app crashed when the system is executing the request.
- *
- * <p>Errors in the category fall in the range 3000-3999 inclusive.
- */
- public static final int ERROR_CATEGORY_APP = 3;
-
- /** The result code of the app function execution. */
- @ResultCode private final int mResultCode;
-
- /**
- * The error message associated with the result, if any. This is {@code null} if the result code
- * is {@link #RESULT_OK}.
- */
- @Nullable private final String mErrorMessage;
+ public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue";
/**
* Returns the return value of the executed function.
@@ -192,103 +79,21 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
/** Returns the additional metadata data relevant to this function execution response. */
@NonNull private final Bundle mExtras;
- private ExecuteAppFunctionResponse(
- @NonNull GenericDocumentWrapper resultDocumentWrapper,
- @NonNull Bundle extras,
- @ResultCode int resultCode,
- @Nullable String errorMessage) {
- mResultDocumentWrapper = Objects.requireNonNull(resultDocumentWrapper);
- mExtras = Objects.requireNonNull(extras);
- mResultCode = resultCode;
- mErrorMessage = errorMessage;
- }
-
/**
- * Returns result codes from throwable.
- *
- * @hide
- */
- @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- static @ResultCode int getResultCode(@NonNull Throwable t) {
- if (t instanceof IllegalArgumentException) {
- return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
- }
- return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
- }
-
- /**
- * Returns a successful response.
- *
* @param resultDocument The return value of the executed function.
- * @param extras The additional metadata for this function execution response.
*/
- @NonNull
- @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- public static ExecuteAppFunctionResponse newSuccess(
- @NonNull GenericDocument resultDocument, @Nullable Bundle extras) {
- Objects.requireNonNull(resultDocument);
- Bundle actualExtras = getActualExtras(extras);
- GenericDocumentWrapper resultDocumentWrapper = new GenericDocumentWrapper(resultDocument);
-
- return new ExecuteAppFunctionResponse(
- resultDocumentWrapper, actualExtras, RESULT_OK, /* errorMessage= */ null);
+ public ExecuteAppFunctionResponse(@NonNull GenericDocument resultDocument) {
+ this(resultDocument, Bundle.EMPTY);
}
/**
- * Returns a failure response.
- *
- * @param resultCode The result code of the app function execution.
+ * @param resultDocument The return value of the executed function.
* @param extras The additional metadata for this function execution response.
- * @param errorMessage The error message associated with the result, if any.
- */
- @NonNull
- @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
- public static ExecuteAppFunctionResponse newFailure(
- @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) {
- if (resultCode == RESULT_OK) {
- throw new IllegalArgumentException("resultCode must not be RESULT_OK");
- }
- Bundle actualExtras = getActualExtras(extras);
- GenericDocumentWrapper emptyWrapper =
- new GenericDocumentWrapper(new GenericDocument.Builder<>("", "", "").build());
- return new ExecuteAppFunctionResponse(emptyWrapper, actualExtras, resultCode, errorMessage);
- }
-
- private static Bundle getActualExtras(@Nullable Bundle extras) {
- if (extras == null) {
- return Bundle.EMPTY;
- }
- return extras;
- }
-
- /**
- * Returns the error category of the {@link ExecuteAppFunctionResponse}.
- *
- * <p>This method categorizes errors based on their underlying cause, allowing developers to
- * implement targeted error handling and provide more informative error messages to users. It
- * maps ranges of result codes to specific error categories.
- *
- * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to
- * ensure correct categorization of the failed response.
- *
- * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to
- * any error category, for example, in the case of a successful result with {@link #RESULT_OK}.
- *
- * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
- * result code ranges.
*/
- @ErrorCategory
- public int getErrorCategory() {
- if (mResultCode >= 1000 && mResultCode < 2000) {
- return ERROR_CATEGORY_REQUEST_ERROR;
- }
- if (mResultCode >= 2000 && mResultCode < 3000) {
- return ERROR_CATEGORY_SYSTEM;
- }
- if (mResultCode >= 3000 && mResultCode < 4000) {
- return ERROR_CATEGORY_APP;
- }
- return ERROR_CATEGORY_UNKNOWN;
+ public ExecuteAppFunctionResponse(
+ @NonNull GenericDocument resultDocument, @NonNull Bundle extras) {
+ mResultDocumentWrapper = new GenericDocumentWrapper(Objects.requireNonNull(resultDocument));
+ mExtras = Objects.requireNonNull(extras);
}
/**
@@ -296,9 +101,6 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
*
* <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
*
- * <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed
- * function does not produce a return value.
- *
* <p>Sample code for extracting the return value:
*
* <pre>
@@ -324,32 +126,6 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
return mExtras;
}
- /**
- * Returns {@code true} if {@link #getResultCode} equals {@link
- * ExecuteAppFunctionResponse#RESULT_OK}.
- */
- public boolean isSuccess() {
- return getResultCode() == RESULT_OK;
- }
-
- /**
- * Returns one of the {@code RESULT} constants defined in {@link ExecuteAppFunctionResponse}.
- */
- @ResultCode
- public int getResultCode() {
- return mResultCode;
- }
-
- /**
- * Returns the error message associated with this result.
- *
- * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}.
- */
- @Nullable
- public String getErrorMessage() {
- return mErrorMessage;
- }
-
@Override
public int describeContents() {
return 0;
@@ -359,43 +135,5 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
mResultDocumentWrapper.writeToParcel(dest, flags);
dest.writeBundle(mExtras);
- dest.writeInt(mResultCode);
- dest.writeString8(mErrorMessage);
}
-
- /**
- * Result codes.
- *
- * @hide
- */
- @IntDef(
- prefix = {"RESULT_"},
- value = {
- RESULT_OK,
- RESULT_DENIED,
- RESULT_APP_UNKNOWN_ERROR,
- RESULT_FUNCTION_NOT_FOUND,
- RESULT_SYSTEM_ERROR,
- RESULT_INVALID_ARGUMENT,
- RESULT_DISABLED,
- RESULT_CANCELLED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ResultCode {}
-
- /**
- * Error categories.
- *
- * @hide
- */
- @IntDef(
- prefix = {"ERROR_CATEGORY_"},
- value = {
- ERROR_CATEGORY_UNKNOWN,
- ERROR_CATEGORY_REQUEST_ERROR,
- ERROR_CATEGORY_APP,
- ERROR_CATEGORY_SYSTEM
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ErrorCategory {}
}
diff --git a/core/java/android/app/appfunctions/GenericDocumentWrapper.java b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
index b29b64e44d21..541ca7458efe 100644
--- a/core/java/android/app/appfunctions/GenericDocumentWrapper.java
+++ b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
@@ -34,9 +34,9 @@ import java.util.Objects;
* <p>{#link {@link Parcel#writeBlob(byte[])}} could take care of whether to pass data via binder
* directly or Android shared memory if the data is large.
*
- * <p>This class performs lazy unparcelling. The `GenericDocument` is only unparcelled
- * from the underlying `Parcel` when {@link #getValue()} is called. This optimization
- * allows the system server to pass through the generic document, without unparcel and parcel it.
+ * <p>This class performs lazy unparcelling. The `GenericDocument` is only unparcelled from the
+ * underlying `Parcel` when {@link #getValue()} is called. This optimization allows the system
+ * server to pass through the generic document, without unparcel and parcel it.
*
* @hide
* @see Parcel#writeBlob(byte[])
@@ -45,8 +45,11 @@ public final class GenericDocumentWrapper implements Parcelable {
@Nullable
@GuardedBy("mLock")
private GenericDocument mGenericDocument;
+
@GuardedBy("mLock")
- @Nullable private Parcel mParcel;
+ @Nullable
+ private Parcel mParcel;
+
private final Object mLock = new Object();
public static final Creator<GenericDocumentWrapper> CREATOR =
diff --git a/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl b/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl
index 5323f9b627e3..69bbc0e5d275 100644
--- a/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl
+++ b/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl
@@ -17,8 +17,10 @@
package android.app.appfunctions;
import android.app.appfunctions.ExecuteAppFunctionResponse;
+import android.app.appfunctions.AppFunctionException;
/** {@hide} */
oneway interface IExecuteAppFunctionCallback {
- void onResult(in ExecuteAppFunctionResponse result);
+ void onSuccess(in ExecuteAppFunctionResponse result);
+ void onError(in AppFunctionException exception);
}
diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
index 00182447e9a8..2426daf5c9f2 100644
--- a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
+++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
@@ -17,17 +17,16 @@
package android.app.appfunctions;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.RemoteException;
import android.util.Log;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
/**
* A wrapper of IExecuteAppFunctionCallback which swallows the {@link RemoteException}. This
- * callback is intended for one-time use only. Subsequent calls to onResult() will be ignored.
+ * callback is intended for one-time use only. Subsequent calls to onResult() or onError() will be
+ * ignored.
*
* @hide
*/
@@ -38,44 +37,41 @@ public class SafeOneTimeExecuteAppFunctionCallback {
@NonNull private final IExecuteAppFunctionCallback mCallback;
- @Nullable private final Consumer<ExecuteAppFunctionResponse> mOnDispatchCallback;
-
public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback) {
- this(callback, /* onDispatchCallback= */ null);
- }
-
- /**
- * @param callback The callback to wrap.
- * @param onDispatchCallback An optional callback invoked after the wrapped callback has been
- * dispatched with a result. This callback receives the result that has been dispatched.
- */
- public SafeOneTimeExecuteAppFunctionCallback(
- @NonNull IExecuteAppFunctionCallback callback,
- @Nullable Consumer<ExecuteAppFunctionResponse> onDispatchCallback) {
mCallback = Objects.requireNonNull(callback);
- mOnDispatchCallback = onDispatchCallback;
}
/** Invoke wrapped callback with the result. */
public void onResult(@NonNull ExecuteAppFunctionResponse result) {
if (!mOnResultCalled.compareAndSet(false, true)) {
- Log.w(TAG, "Ignore subsequent calls to onResult()");
+ Log.w(TAG, "Ignore subsequent calls to onResult/onError()");
return;
}
try {
- mCallback.onResult(result);
+ mCallback.onSuccess(result);
} catch (RemoteException ex) {
// Failed to notify the other end. Ignore.
Log.w(TAG, "Failed to invoke the callback", ex);
}
- if (mOnDispatchCallback != null) {
- mOnDispatchCallback.accept(result);
+ }
+
+ /** Invoke wrapped callback with the error. */
+ public void onError(@NonNull AppFunctionException error) {
+ if (!mOnResultCalled.compareAndSet(false, true)) {
+ Log.w(TAG, "Ignore subsequent calls to onResult/onError()");
+ return;
+ }
+ try {
+ mCallback.onError(error);
+ } catch (RemoteException ex) {
+ // Failed to notify the other end. Ignore.
+ Log.w(TAG, "Failed to invoke the callback", ex);
}
}
/**
- * Disables this callback. Subsequent calls to {@link #onResult(ExecuteAppFunctionResponse)}
- * will be ignored.
+ * Disables this callback. Subsequent calls to {@link #onResult(ExecuteAppFunctionResponse)} or
+ * {@link #onError(AppFunctionException)} will be ignored.
*/
public void disable() {
mOnResultCalled.set(true);
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index a48868906487..43a46ba7885d 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -1,5 +1,6 @@
package android.app.assist;
+import android.annotation.FlaggedApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.Intent;
@@ -15,6 +16,20 @@ import android.os.Parcelable;
* {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
*/
public class AssistContent implements Parcelable {
+ /**
+ * Extra for a {@link Bundle} that provides contextual AppFunction's information about the
+ * content currently being viewed in the application.
+ * <p>
+ * This extra can be optionally supplied in the {@link AssistContent#getExtras()} bundle.
+ * <p>
+ * The schema of the {@link Bundle} in this extra is defined in the AppFunction SDK.
+ *
+ * @see android.app.appfunctions.AppFunctionManager
+ */
+ @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final String EXTRA_APP_FUNCTION_DATA =
+ "android.app.assist.extra.APP_FUNCTION_DATA";
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mIsAppProvidedIntent = false;
private boolean mIsAppProvidedWebUri = false;
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index db663f8ed4c4..7d21cbf955d9 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -31,13 +31,24 @@ import com.android.internal.compat.IPlatformCompat;
* Handles caching of calls to {@link com.android.internal.compat.IPlatformCompat}
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ChangeIdStateCache
extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
private static final String CACHE_KEY = createSystemCacheKey("is_compat_change_enabled");
private static final int MAX_ENTRIES = 2048;
- private static boolean sDisabled = false;
+ private static boolean sDisabled = getDefaultDisabled();
private volatile IPlatformCompat mPlatformCompat;
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static boolean getDefaultDisabled() {
+ return false;
+ }
+
+ private static boolean getDefaultDisabled$ravenwood() {
+ return true; // TODO(b/376676753) Disable the cache for now.
+ }
+
/** @hide */
public ChangeIdStateCache() {
super(MAX_ENTRIES, CACHE_KEY);
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
index 7598d6c90d3d..26d9ab65417e 100644
--- a/core/java/android/app/compat/ChangeIdStateQuery.java
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -35,6 +35,7 @@ import java.util.Objects;
* @hide
*/
@Immutable
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class ChangeIdStateQuery {
static final int QUERY_BY_PACKAGE_NAME = 0;
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index d7b2ab4351a4..643d4c96f7b9 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -39,6 +39,7 @@ import java.util.Set;
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatChanges {
private static final ChangeIdStateCache QUERY_CACHE = new ChangeIdStateCache();
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index ebc2945fb1a0..ffc1eec05667 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -36,6 +36,7 @@ import java.util.Objects;
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PackageOverride {
/** @hide */
diff --git a/core/java/android/app/jank/AppJankStats.java b/core/java/android/app/jank/AppJankStats.java
new file mode 100644
index 000000000000..eea1d2ba5b9e
--- /dev/null
+++ b/core/java/android/app/jank/AppJankStats.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This class stores detailed jank statistics for an individual UI widget. These statistics
+ * provide performance insights for specific UI widget states by correlating the number of
+ * "Janky frames" with the total frames rendered while the widget is in that state. This class
+ * can be used by library widgets to provide the system with more detailed information about
+ * where jank is happening for diagnostic purposes.
+ */
+@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+public final class AppJankStats {
+ // UID of the app
+ private int mUid;
+
+ // The id that has been set for the widget.
+ private String mWidgetId;
+
+ // A general category that the widget applies to.
+ private String mWidgetCategory;
+
+ // The states that the UI elements can report
+ private String mWidgetState;
+
+ // The number of frames reported during this state.
+ private long mTotalFrames;
+
+ // Total number of frames determined to be janky during the reported state.
+ private long mJankyFrames;
+
+ // Histogram of frame duration overruns encoded in predetermined buckets.
+ private FrameOverrunHistogram mFrameOverrunHistogram;
+
+
+ /** Used to indicate no widget category has been set. */
+ public static final String WIDGET_CATEGORY_UNSPECIFIED =
+ "widget_category_unspecified";
+
+ /** UI elements that facilitate scrolling. */
+ public static final String SCROLL = "scroll";
+
+ /** UI elements that facilitate playing animations. */
+ public static final String ANIMATION = "animation";
+
+ /** UI elements that facilitate media playback. */
+ public static final String MEDIA = "media";
+
+ /** UI elements that facilitate in-app navigation. */
+ public static final String NAVIGATION = "navigation";
+
+ /** UI elements that facilitate displaying, hiding or interacting with keyboard. */
+ public static final String KEYBOARD = "keyboard";
+
+ /** UI elements that facilitate predictive back gesture navigation. */
+ public static final String PREDICTIVE_BACK = "predictive_back";
+
+ /** UI elements that don't fall in one or any of the other categories. */
+ public static final String OTHER = "other";
+
+ /** Used to indicate no widget state has been set. */
+ public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
+
+ /** Used to indicate the UI element currently has no state and is idle. */
+ public static final String NONE = "none";
+
+ /** Used to indicate the UI element is currently scrolling. */
+ public static final String SCROLLING = "scrolling";
+
+ /** Used to indicate the UI element is currently being flung. */
+ public static final String FLINGING = "flinging";
+
+ /** Used to indicate the UI element is currently being swiped. */
+ public static final String SWIPING = "swiping";
+
+ /** Used to indicate the UI element is currently being dragged. */
+ public static final String DRAGGING = "dragging";
+
+ /** Used to indicate the UI element is currently zooming. */
+ public static final String ZOOMING = "zooming";
+
+ /** Used to indicate the UI element is currently animating. */
+ public static final String ANIMATING = "animating";
+
+ /** Used to indicate the UI element is currently playing media. */
+ public static final String PLAYBACK = "playback";
+
+ /** Used to indicate the UI element is currently being tapped on, for example on a keyboard. */
+ public static final String TAPPING = "tapping";
+
+
+ /**
+ * @hide
+ */
+ @StringDef(value = {
+ WIDGET_CATEGORY_UNSPECIFIED,
+ SCROLL,
+ ANIMATION,
+ MEDIA,
+ NAVIGATION,
+ KEYBOARD,
+ PREDICTIVE_BACK,
+ OTHER
+ })
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WidgetCategory {
+ }
+ /**
+ * @hide
+ */
+ @StringDef(value = {
+ WIDGET_STATE_UNSPECIFIED,
+ NONE,
+ SCROLLING,
+ FLINGING,
+ SWIPING,
+ DRAGGING,
+ ZOOMING,
+ ANIMATING,
+ PLAYBACK,
+ TAPPING,
+ })
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WidgetState {
+ }
+
+
+ /**
+ * Creates a new AppJankStats object.
+ *
+ * @param appUid the Uid of the App that is collecting jank stats.
+ * @param widgetId the widget id that frames will be associated to.
+ * @param widgetCategory a general functionality category that the widget falls into. Must be
+ * one of the following: SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD,
+ * PREDICTIVE_BACK, OTHER or will be set to WIDGET_CATEGORY_UNSPECIFIED
+ * if no value is passed.
+ * @param widgetState the state the widget was in while frames were counted. Must be one of
+ * the following: NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING,
+ * ANIMATING, PLAYBACK, TAPPING or will be set to WIDGET_STATE_UNSPECIFIED
+ * if no value is passed.
+ * @param totalFrames the total number of frames that were counted for this stat.
+ * @param jankyFrames the total number of janky frames that were counted for this stat.
+ * @param frameOverrunHistogram the histogram with predefined buckets. See
+ * {@link #getFrameOverrunHistogram()} for details.
+ *
+ */
+ public AppJankStats(int appUid, @NonNull String widgetId,
+ @Nullable @WidgetCategory String widgetCategory,
+ @Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames,
+ @NonNull FrameOverrunHistogram frameOverrunHistogram) {
+ mUid = appUid;
+ mWidgetId = widgetId;
+ mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED;
+ mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED;
+ mTotalFrames = totalFrames;
+ mJankyFrames = jankyFrames;
+ mFrameOverrunHistogram = frameOverrunHistogram;
+ }
+
+ /**
+ * Returns the app uid.
+ *
+ * @return the app uid.
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
+ * Returns the id of the widget that reported state changes.
+ *
+ * @return the id of the widget that reported state changes. This value cannot be null.
+ */
+ public @NonNull String getWidgetId() {
+ return mWidgetId;
+ }
+
+ /**
+ * Returns the category that the widget's functionality generally falls into, or
+ * widget_category_unspecified {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in.
+ *
+ * @return the category that the widget's functionality generally falls into, this value cannot
+ * be null.
+ */
+ public @NonNull @WidgetCategory String getWidgetCategory() {
+ return mWidgetCategory;
+ }
+
+ /**
+ * Returns the widget's state that was reported for this stat, or widget_state_unspecified
+ * {@link #WIDGET_STATE_UNSPECIFIED} if no value was passed in.
+ *
+ * @return the widget's state that was reported for this stat. This value cannot be null.
+ */
+ public @NonNull @WidgetState String getWidgetState() {
+ return mWidgetState;
+ }
+
+ /**
+ * Returns the number of frames that were determined to be janky for this stat.
+ *
+ * @return the number of frames that were determined to be janky for this stat.
+ */
+ public long getJankyFrameCount() {
+ return mJankyFrames;
+ }
+
+ /**
+ * Returns the total number of frames counted for this stat.
+ *
+ * @return the total number of frames counted for this stat.
+ */
+ public long getTotalFrameCount() {
+ return mTotalFrames;
+ }
+
+ /**
+ * Returns a Histogram containing frame overrun times in millis grouped into predefined buckets.
+ * See {@link FrameOverrunHistogram} for more information.
+ *
+ * @return Histogram containing frame overrun times in predefined buckets. This value cannot
+ * be null.
+ */
+ public @NonNull FrameOverrunHistogram getFrameOverrunHistogram() {
+ return mFrameOverrunHistogram;
+ }
+}
diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java
new file mode 100644
index 000000000000..e28ac126a90a
--- /dev/null
+++ b/core/java/android/app/jank/FrameOverrunHistogram.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import java.util.Arrays;
+
+/**
+ * This class is intended to be used when reporting {@link AppJankStats} back to the system. It's
+ * intended to be used by library widgets to help facilitate the reporting of frame overrun times
+ * by adding those times into predefined buckets.
+ */
+@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+public class FrameOverrunHistogram {
+ private static int[] sBucketEndpoints = new int[]{
+ Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18,
+ -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40,
+ 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000
+ };
+ private int[] mBucketCounts;
+
+ /**
+ * Create a new instance of FrameOverrunHistogram.
+ */
+ public FrameOverrunHistogram() {
+ mBucketCounts = new int[sBucketEndpoints.length - 1];
+ }
+
+ /**
+ * Increases the count by one for the bucket representing the frame overrun duration.
+ *
+ * @param frameOverrunMillis frame overrun duration in millis, frame overrun is the difference
+ * between a frames deadline and when it was rendered.
+ */
+ public void addFrameOverrunMillis(int frameOverrunMillis) {
+ int countsIndex = getIndexForCountsFromOverrunTime(frameOverrunMillis);
+ mBucketCounts[countsIndex]++;
+ }
+
+ /**
+ * Returns the counts for the all the frame overrun buckets.
+ *
+ * @return an array of integers representing the counts of frame overrun times. This value
+ * cannot be null.
+ */
+ public @NonNull int[] getBucketCounters() {
+ return Arrays.copyOf(mBucketCounts, mBucketCounts.length);
+ }
+
+ /**
+ * Returns the predefined endpoints for the histogram.
+ *
+ * @return array of integers representing the endpoints for the predefined histogram count
+ * buckets. This value cannot be null.
+ */
+ public @NonNull int[] getBucketEndpointsMillis() {
+ return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length);
+ }
+
+ // This takes the overrun time and returns what bucket it belongs to in the counters array.
+ private int getIndexForCountsFromOverrunTime(int overrunTime) {
+ if (overrunTime < 20) {
+ if (overrunTime >= -20) {
+ return (overrunTime + 20) / 2 + 12;
+ }
+ if (overrunTime >= -30) {
+ return (overrunTime + 30) / 5 + 10;
+ }
+ if (overrunTime >= -100) {
+ return (overrunTime + 100) / 10 + 3;
+ }
+ if (overrunTime >= -200) {
+ return (overrunTime + 200) / 50 + 1;
+ }
+ return 0;
+ }
+ if (overrunTime < 30) {
+ return (overrunTime - 20) / 5 + 32;
+ }
+ if (overrunTime < 100) {
+ return (overrunTime - 30) / 10 + 34;
+ }
+ if (overrunTime < 200) {
+ return (overrunTime - 50) / 100 + 41;
+ }
+ if (overrunTime < 1000) {
+ return (overrunTime - 200) / 100 + 43;
+ }
+ return sBucketEndpoints.length - 1;
+ }
+}
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 981a9167c2da..3783a5f9e829 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -87,6 +87,14 @@ public class JankDataProcessor {
}
/**
+ * Merges app jank stats reported by components outside the platform to the current pending
+ * stats
+ */
+ public void mergeJankStats(AppJankStats jankStats, String activityName) {
+ // TODO b/377572463 Add Merging Logic
+ }
+
+ /**
* Returns the aggregate map of different pending jank stats.
*/
@VisibleForTesting
diff --git a/core/java/android/app/jank/JankTracker.java b/core/java/android/app/jank/JankTracker.java
new file mode 100644
index 000000000000..202281f98c97
--- /dev/null
+++ b/core/java/android/app/jank/JankTracker.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.AttachedSurfaceControl;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+
+/**
+ * This class is responsible for registering callbacks that will receive JankData batches.
+ * It handles managing the background thread that JankData will be processed on. As well as acting
+ * as an intermediary between widgets and the state tracker, routing state changes to the tracker.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+public class JankTracker {
+
+ // Tracks states reported by widgets.
+ private StateTracker mStateTracker;
+ // Processes JankData batches and associates frames to widget states.
+ private JankDataProcessor mJankDataProcessor;
+
+ // Background thread responsible for processing JankData batches.
+ private HandlerThread mHandlerThread = new HandlerThread("AppJankTracker");
+ private Handler mHandler = null;
+
+ // Needed so we know when the view is attached to a window.
+ private ViewTreeObserver mViewTreeObserver;
+
+ // Handle to a registered OnJankData listener.
+ private SurfaceControl.OnJankDataListenerRegistration mJankDataListenerRegistration;
+
+ // The interface to the windowing system that enables us to register for JankData.
+ private AttachedSurfaceControl mSurfaceControl;
+ // Name of the activity that is currently tracking Jank metrics.
+ private String mActivityName;
+ // The apps uid.
+ private int mAppUid;
+ // View that gives us access to ViewTreeObserver.
+ private View mDecorView;
+
+ /**
+ * Set by the activity to enable or disable jank tracking. Activities may disable tracking if
+ * they are paused or not enable tracking if they are not visible or if the app category is not
+ * set.
+ */
+ private boolean mTrackingEnabled = false;
+ /**
+ * Set to true once listeners are registered and JankData will start to be received. Both
+ * mTrackingEnabled and mListenersRegistered need to be true for JankData to be processed.
+ */
+ private boolean mListenersRegistered = false;
+
+
+ public JankTracker(Choreographer choreographer, View decorView) {
+ mStateTracker = new StateTracker(choreographer);
+ mJankDataProcessor = new JankDataProcessor(mStateTracker);
+ mDecorView = decorView;
+ mHandlerThread.start();
+ registerWindowListeners();
+ }
+
+ /**
+ * Merges app jank stats reported by components outside the platform to the current pending
+ * stats
+ */
+ public void mergeAppJankStats(AppJankStats appJankStats) {
+ mJankDataProcessor.mergeJankStats(appJankStats, mActivityName);
+ }
+
+ public void setActivityName(@NonNull String activityName) {
+ mActivityName = activityName;
+ }
+
+ public void setAppUid(int uid) {
+ mAppUid = uid;
+ }
+
+ /**
+ * Will add the widget category, id and state as a UI state to associate frames to it.
+ * @param widgetCategory preselected general widget category
+ * @param widgetId developer defined widget id if available.
+ * @param widgetState the current active widget state.
+ */
+ public void addUiState(String widgetCategory, String widgetId, String widgetState) {
+ if (!shouldTrack()) return;
+
+ mStateTracker.putState(widgetCategory, widgetId, widgetState);
+ }
+
+ /**
+ * Will remove the widget category, id and state as a ui state and no longer attribute frames
+ * to it.
+ * @param widgetCategory preselected general widget category
+ * @param widgetId developer defined widget id if available.
+ * @param widgetState no longer active widget state.
+ */
+ public void removeUiState(String widgetCategory, String widgetId, String widgetState) {
+ if (!shouldTrack()) return;
+
+ mStateTracker.removeState(widgetCategory, widgetId, widgetState);
+ }
+
+ /**
+ * Call to update a jank state to a different state.
+ * @param widgetCategory preselected general widget category.
+ * @param widgetId developer defined widget id if available.
+ * @param currentState current state of the widget.
+ * @param nextState the state the widget will be in.
+ */
+ public void updateUiState(String widgetCategory, String widgetId, String currentState,
+ String nextState) {
+ if (!shouldTrack()) return;
+
+ mStateTracker.updateState(widgetCategory, widgetId, currentState, nextState);
+ }
+
+ /**
+ * Will enable jank tracking, and add the activity as a state to associate frames to.
+ */
+ public void enableAppJankTracking() {
+ // Add the activity as a state, this will ensure we track frames to the activity without the
+ // need of a decorated widget to be used.
+ // TODO b/376116199 replace "NONE" with UNSPECIFIED once the API changes are merged.
+ mStateTracker.putState("NONE", mActivityName, "NONE");
+ mTrackingEnabled = true;
+ }
+
+ /**
+ * Will disable jank tracking, and remove the activity as a state to associate frames to.
+ */
+ public void disableAppJankTracking() {
+ mTrackingEnabled = false;
+ // TODO b/376116199 replace "NONE" with UNSPECIFIED once the API changes are merged.
+ mStateTracker.removeState("NONE", mActivityName, "NONE");
+ }
+
+ /**
+ * Retrieve all pending widget states, this is intended for testing purposes only.
+ * @param stateDataList the ArrayList that will be populated with the pending states.
+ */
+ @VisibleForTesting
+ public void getAllUiStates(@NonNull ArrayList<StateTracker.StateData> stateDataList) {
+ mStateTracker.retrieveAllStates(stateDataList);
+ }
+
+ /**
+ * Only intended to be used by tests, the runnable that registers the listeners may not run
+ * in time for tests to pass. This forces them to run immediately.
+ */
+ @VisibleForTesting
+ public void forceListenerRegistration() {
+ mSurfaceControl = mDecorView.getRootSurfaceControl();
+ registerForJankData();
+ // TODO b/376116199 Check if registration is good.
+ mListenersRegistered = true;
+ }
+
+ private void registerForJankData() {
+ if (mSurfaceControl == null) return;
+ /*
+ TODO b/376115668 Register for JankData batches from new JankTracking API
+ */
+ }
+
+ private boolean shouldTrack() {
+ return mTrackingEnabled && mListenersRegistered;
+ }
+
+ /**
+ * Need to know when the decor view gets attached to the window in order to get
+ * AttachedSurfaceControl. In order to register a callback for OnJankDataListener
+ * AttachedSurfaceControl needs to be created which only happens after onWindowAttached is
+ * called. This is why there is a delay in posting the runnable.
+ */
+ private void registerWindowListeners() {
+ if (mDecorView == null) return;
+ mViewTreeObserver = mDecorView.getViewTreeObserver();
+ mViewTreeObserver.addOnWindowAttachListener(new ViewTreeObserver.OnWindowAttachListener() {
+ @Override
+ public void onWindowAttached() {
+ getHandler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ forceListenerRegistration();
+ }
+ }, 1000);
+ }
+
+ @Override
+ public void onWindowDetached() {
+ // TODO b/376116199 do we un-register the callback or just not process the data.
+ }
+ });
+ }
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+ return mHandler;
+ }
+}
diff --git a/core/java/android/app/multitasking.aconfig b/core/java/android/app/multitasking.aconfig
index 9a645192a155..c8455c1f439f 100644
--- a/core/java/android/app/multitasking.aconfig
+++ b/core/java/android/app/multitasking.aconfig
@@ -8,3 +8,11 @@ flag {
description: "Enables PiP UI state callback on entering"
bug: "303718131"
}
+
+flag {
+ name: "enable_tv_implicit_enter_pip_restriction"
+ is_exported: true
+ namespace: "tv_system_ui"
+ description: "Enables restrictions to PiP entry on TV for setAutoEnterEnabled and lifecycle methods"
+ bug: "283115999"
+}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 0fc4291d15ab..6934e9883840 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -8,8 +8,7 @@ container: "system"
flag {
name: "notifications_redesign_app_icons"
namespace: "systemui"
- description: "Notifications Redesign: Use app icons in notification rows (not to be confused with"
- " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)."
+ description: "Notifications Redesign: Use app icons in notification rows"
bug: "371174789"
}
@@ -101,41 +100,6 @@ flag {
}
flag {
- name: "visit_person_uri"
- namespace: "systemui"
- description: "Guards the security fix that ensures all URIs Person.java are valid"
- bug: "281044385"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-# vvv Prototypes for using app icons in notifications vvv
-
-flag {
- name: "notifications_use_app_icon"
- namespace: "systemui"
- description: "Experiment to replace the small icon in a notification with the app icon. This includes the status bar, AOD, shelf and notification row itself."
- bug: "335211019"
-}
-
-flag {
- name: "notifications_use_app_icon_in_row"
- namespace: "systemui"
- description: "Experiment to replace the small icon in a notification row with the app icon."
- bug: "335211019"
-}
-
-flag {
- name: "notifications_use_monochrome_app_icon"
- namespace: "systemui"
- description: "Experiment to replace the notification icon in the status bar and shelf with the monochrome app icon, if available."
- bug: "335211019"
-}
-
-# ^^^ Prototypes for using app icons in notifications ^^^
-
-flag {
name: "notification_expansion_optional"
namespace: "systemui"
description: "Experiment to restore the pre-S behavior where standard notifications are not expandable unless they have actions."
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index f51f748bc71f..61b53f97fea1 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -18,3 +18,20 @@ flag {
description: "Enforce PropertyInvalidatedCache.setTestMode() protocol"
bug: "360897450"
}
+
+flag {
+ namespace: "system_performance"
+ name: "pic_isolate_cache_by_uid"
+ is_fixed_read_only: true
+ description: "Ensure that different UIDs use different caches"
+ bug: "373752556"
+}
+
+flag {
+ namespace: "system_performance"
+ name: "pic_isolated_cache_statistics"
+ is_fixed_read_only: true
+ description: "Collects statistics for cache UID isolation strategies"
+ bug: "373752556"
+}
+
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index bcb5b3636c95..d5e696d49ff4 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -7,4 +7,12 @@ flag {
namespace: "supervision"
description: "Flag to enable the SupervisionService"
bug: "340351729"
-} \ No newline at end of file
+}
+
+flag {
+ name: "supervision_api_on_wear"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag to enable the SupervisionService on Wear devices"
+ bug: "373358935"
+}
diff --git a/core/java/android/app/wallpaper.aconfig b/core/java/android/app/wallpaper.aconfig
index 4b880d030413..f750a844f4ff 100644
--- a/core/java/android/app/wallpaper.aconfig
+++ b/core/java/android/app/wallpaper.aconfig
@@ -22,3 +22,21 @@ flag {
bug: "347235611"
is_exported: true
}
+
+flag {
+ name: "customization_packs_apis"
+ is_exported: true
+ namespace: "systemui"
+ description: "Move APIs related to bitmap and crops to @SystemApi."
+ bug: "372344184"
+}
+
+flag {
+ name: "accurate_wallpaper_downsampling"
+ namespace: "systemui"
+ description: "Accurate downsampling of wallpaper bitmap for high resolution images"
+ bug: "355665230"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index c3d6340be41f..4a142bb5287a 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -18,8 +18,6 @@ package android.app.wallpaper;
import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import android.annotation.FlaggedApi;
import android.app.WallpaperInfo;
import android.content.ComponentName;
@@ -31,7 +29,6 @@ import android.text.Html;
import android.text.Spanned;
import android.text.SpannedString;
import android.util.Log;
-import android.util.Xml;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -43,8 +40,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -118,7 +113,7 @@ public final class WallpaperDescription implements Parcelable {
/** @return the description for this wallpaper */
@NonNull
public List<CharSequence> getDescription() {
- return new ArrayList<>();
+ return mDescription;
}
/** @return the {@link Uri} for the action associated with the wallpaper, or {@code null} if not
@@ -154,46 +149,6 @@ public final class WallpaperDescription implements Parcelable {
return Objects.hash(mComponent, mId);
}
- ////// Stream read/write
-
- /**
- * Writes the content of the {@link WallpaperDescription} to a {@link OutputStream}.
- *
- * <p>The content can be read by {@link #readFromStream}. This method is intended for use by
- * trusted apps only, and the format is not guaranteed to be stable.</p>
- */
- public void writeToStream(@NonNull OutputStream outputStream) throws IOException {
- TypedXmlSerializer serializer = Xml.newFastSerializer();
- serializer.setOutput(outputStream, UTF_8.name());
- serializer.startTag(null, "description");
- try {
- saveToXml(serializer);
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- serializer.endTag(null, "description");
- serializer.flush();
- }
-
- /**
- * Reads a {@link PersistableBundle} from an {@link InputStream}.
- *
- * <p>The stream must be generated by {@link #writeToStream}. This method is intended for use by
- * trusted apps only, and the format is not guaranteed to be stable.</p>
- */
- @NonNull
- public static WallpaperDescription readFromStream(@NonNull InputStream inputStream)
- throws IOException {
- try {
- TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setInput(inputStream, UTF_8.name());
- parser.next();
- return WallpaperDescription.restoreFromXml(parser);
- } catch (XmlPullParserException e) {
- throw new IOException(e);
- }
- }
-
////// XML storage
/** @hide */
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 940a4d208d99..ce515761551c 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -55,7 +55,7 @@ flag {
name: "remote_views_proto"
namespace: "app_widgets"
description: "Enable support for persisting RemoteViews previews to Protobuf"
- bug: "306546610"
+ bug: "364420494"
}
flag {
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 65f9cbefb052..2be27dabcf90 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -160,7 +160,7 @@ public final class VirtualDeviceParams implements Parcelable {
*/
@IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO,
POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CLIPBOARD, POLICY_TYPE_CAMERA,
- POLICY_TYPE_BLOCKED_ACTIVITY})
+ POLICY_TYPE_BLOCKED_ACTIVITY, POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface PolicyType {}
@@ -301,6 +301,21 @@ public final class VirtualDeviceParams implements Parcelable {
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6;
+ /**
+ * Tells the virtual device framework how to handle camera access of the default device by apps
+ * running on the virtual device.
+ *
+ * <ul>
+ * <li>{@link #DEVICE_POLICY_DEFAULT}: Default device camera access will be allowed.
+ * <li>{@link #DEVICE_POLICY_CUSTOM}: Default device camera access will be blocked.
+ * </ul>
+ *
+ * @see Context#DEVICE_ID_DEFAULT
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags
+ .FLAG_DEFAULT_DEVICE_CAMERA_ACCESS_POLICY)
+ public static final int POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS = 7;
+
private final int mLockState;
@NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
@NavigationPolicy
@@ -318,6 +333,8 @@ public final class VirtualDeviceParams implements Parcelable {
@Nullable private final IVirtualSensorCallback mVirtualSensorCallback;
private final int mAudioPlaybackSessionId;
private final int mAudioRecordingSessionId;
+ private final long mDimDuration;
+ private final long mScreenOffTimeout;
private VirtualDeviceParams(
@LockState int lockState,
@@ -333,7 +350,9 @@ public final class VirtualDeviceParams implements Parcelable {
@NonNull List<VirtualSensorConfig> virtualSensorConfigs,
@Nullable IVirtualSensorCallback virtualSensorCallback,
int audioPlaybackSessionId,
- int audioRecordingSessionId) {
+ int audioRecordingSessionId,
+ long dimDuration,
+ long screenOffTimeout) {
mLockState = lockState;
mUsersWithMatchingAccounts =
new ArraySet<>(Objects.requireNonNull(usersWithMatchingAccounts));
@@ -351,6 +370,8 @@ public final class VirtualDeviceParams implements Parcelable {
mVirtualSensorCallback = virtualSensorCallback;
mAudioPlaybackSessionId = audioPlaybackSessionId;
mAudioRecordingSessionId = audioRecordingSessionId;
+ mDimDuration = dimDuration;
+ mScreenOffTimeout = screenOffTimeout;
}
@SuppressWarnings("unchecked")
@@ -371,6 +392,8 @@ public final class VirtualDeviceParams implements Parcelable {
mAudioRecordingSessionId = parcel.readInt();
mHomeComponent = parcel.readTypedObject(ComponentName.CREATOR);
mInputMethodComponent = parcel.readTypedObject(ComponentName.CREATOR);
+ mDimDuration = parcel.readLong();
+ mScreenOffTimeout = parcel.readLong();
}
/**
@@ -382,6 +405,26 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
+ * Returns the dim duration for the displays of this device.
+ *
+ * @see Builder#setDimDuration(Duration)
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public @NonNull Duration getDimDuration() {
+ return Duration.ofMillis(mDimDuration);
+ }
+
+ /**
+ * Returns the screen off timeout of the displays of this device.
+ *
+ * @see Builder#setDimDuration(Duration)
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public @NonNull Duration getScreenOffTimeout() {
+ return Duration.ofMillis(mScreenOffTimeout);
+ }
+
+ /**
* Returns the custom component used as home on all displays owned by this virtual device that
* support home activities.
*
@@ -604,6 +647,8 @@ public final class VirtualDeviceParams implements Parcelable {
dest.writeInt(mAudioRecordingSessionId);
dest.writeTypedObject(mHomeComponent, flags);
dest.writeTypedObject(mInputMethodComponent, flags);
+ dest.writeLong(mDimDuration);
+ dest.writeLong(mScreenOffTimeout);
}
@Override
@@ -638,7 +683,9 @@ public final class VirtualDeviceParams implements Parcelable {
&& Objects.equals(mHomeComponent, that.mHomeComponent)
&& Objects.equals(mInputMethodComponent, that.mInputMethodComponent)
&& mAudioPlaybackSessionId == that.mAudioPlaybackSessionId
- && mAudioRecordingSessionId == that.mAudioRecordingSessionId;
+ && mAudioRecordingSessionId == that.mAudioRecordingSessionId
+ && mDimDuration == that.mDimDuration
+ && mScreenOffTimeout == that.mScreenOffTimeout;
}
@Override
@@ -647,7 +694,7 @@ public final class VirtualDeviceParams implements Parcelable {
mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions,
mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName,
mDevicePolicies, mHomeComponent, mInputMethodComponent, mAudioPlaybackSessionId,
- mAudioRecordingSessionId);
+ mAudioRecordingSessionId, mDimDuration, mScreenOffTimeout);
for (int i = 0; i < mDevicePolicies.size(); i++) {
hashCode = 31 * hashCode + mDevicePolicies.keyAt(i);
hashCode = 31 * hashCode + mDevicePolicies.valueAt(i);
@@ -671,6 +718,8 @@ public final class VirtualDeviceParams implements Parcelable {
+ " mInputMethodComponent=" + mInputMethodComponent
+ " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId
+ " mAudioRecordingSessionId=" + mAudioRecordingSessionId
+ + " mDimDuration=" + mDimDuration
+ + " mScreenOffTimeout=" + mScreenOffTimeout
+ ")";
}
@@ -692,11 +741,13 @@ public final class VirtualDeviceParams implements Parcelable {
pw.println(prefix + "mInputMethodComponent=" + mInputMethodComponent);
pw.println(prefix + "mAudioPlaybackSessionId=" + mAudioPlaybackSessionId);
pw.println(prefix + "mAudioRecordingSessionId=" + mAudioRecordingSessionId);
+ pw.println(prefix + "mDimDuration=" + mDimDuration);
+ pw.println(prefix + "mScreenOffTimeout=" + mScreenOffTimeout);
}
@NonNull
public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
- new Parcelable.Creator<VirtualDeviceParams>() {
+ new Parcelable.Creator<>() {
public VirtualDeviceParams createFromParcel(Parcel in) {
return new VirtualDeviceParams(in);
}
@@ -711,6 +762,8 @@ public final class VirtualDeviceParams implements Parcelable {
*/
public static final class Builder {
+ private static final Duration INFINITE_TIMEOUT = Duration.ofDays(365 * 1000);
+
private @LockState int mLockState = LOCK_STATE_DEFAULT;
@NonNull private Set<UserHandle> mUsersWithMatchingAccounts = Collections.emptySet();
@NonNull private Set<ComponentName> mCrossTaskNavigationExemptions = Collections.emptySet();
@@ -733,6 +786,8 @@ public final class VirtualDeviceParams implements Parcelable {
@Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback;
@Nullable private ComponentName mHomeComponent;
@Nullable private ComponentName mInputMethodComponent;
+ private Duration mDimDuration = Duration.ZERO;
+ private Duration mScreenOffTimeout = Duration.ZERO;
private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub {
@NonNull
@@ -810,6 +865,57 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
+ * Sets the dim duration for all trusted non-mirror displays of the device.
+ *
+ * <p>The system will reduce the display brightness for the specified duration if there
+ * has been no interaction just before the displays turn off.</p>
+ *
+ * <p>If set, the screen off timeout must also be set to a value larger than the dim
+ * duration. If left unset or set to zero, then the display brightness will not be reduced.
+ * </p>
+ *
+ * @throws IllegalArgumentException if the dim duration is negative or if the dim duration
+ * is longer than the screen off timeout.
+ * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+ * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
+ * @see #setScreenOffTimeout
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public Builder setDimDuration(@NonNull Duration dimDuration) {
+ if (Objects.requireNonNull(dimDuration).compareTo(Duration.ZERO) < 0) {
+ throw new IllegalArgumentException("The dim duration cannot be negative");
+ }
+ mDimDuration = dimDuration;
+ return this;
+ }
+
+ /**
+ * Sets the timeout, after which all trusted non-mirror displays of the device will turn
+ * off, if there has been no interaction with the device.
+ *
+ * <p>If dim duration is set, the screen off timeout must be set to a value larger than the
+ * dim duration. If left unset or set to zero, then the displays will never be turned off
+ * due to inactivity.</p>
+ *
+ * @throws IllegalArgumentException if the screen off timeout is negative or if the dim
+ * duration is longer than the screen off timeout.
+ * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+ * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
+ * @see #setDimDuration
+ * @see VirtualDeviceManager.VirtualDevice#goToSleep()
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public Builder setScreenOffTimeout(@NonNull Duration screenOffTimeout) {
+ if (Objects.requireNonNull(screenOffTimeout).compareTo(Duration.ZERO) < 0) {
+ throw new IllegalArgumentException("The screen off timeout cannot be negative");
+ }
+ mScreenOffTimeout = screenOffTimeout;
+ return this;
+ }
+
+ /**
* Specifies a component to be used as home on all displays owned by this virtual device
* that support home activities.
* *
@@ -1205,6 +1311,14 @@ public final class VirtualDeviceParams implements Parcelable {
}
}
+ if (mDimDuration.compareTo(mScreenOffTimeout) > 0) {
+ throw new IllegalArgumentException(
+ "The dim duration cannot be greater than the screen off timeout.");
+ }
+ if (mScreenOffTimeout.compareTo(Duration.ZERO) == 0) {
+ mScreenOffTimeout = INFINITE_TIMEOUT;
+ }
+
if (!Flags.crossDeviceClipboard()) {
mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);
}
@@ -1213,6 +1327,10 @@ public final class VirtualDeviceParams implements Parcelable {
mDevicePolicies.delete(POLICY_TYPE_CAMERA);
}
+ if (!android.companion.virtualdevice.flags.Flags.defaultDeviceCameraAccessPolicy()) {
+ mDevicePolicies.delete(POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS);
+ }
+
if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
mDevicePolicies.delete(POLICY_TYPE_BLOCKED_ACTIVITY);
}
@@ -1250,7 +1368,9 @@ public final class VirtualDeviceParams implements Parcelable {
mVirtualSensorConfigs,
virtualSensorCallbackDelegate,
mAudioPlaybackSessionId,
- mAudioRecordingSessionId);
+ mAudioRecordingSessionId,
+ mDimDuration.toMillis(),
+ mScreenOffTimeout.toMillis());
}
}
}
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index fc9c94dd5b0f..3e6919bac5fa 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -67,13 +67,6 @@ flag {
}
flag {
- name: "stream_camera"
- namespace: "virtual_devices"
- description: "Enable streaming camera to Virtual Devices"
- bug: "291740640"
-}
-
-flag {
name: "persistent_device_id_api"
is_exported: true
namespace: "virtual_devices"
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 9af20164a66d..c47fe236faf0 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -153,3 +153,10 @@ flag {
bug: "371173368"
is_exported: true
}
+
+flag {
+ name: "vdm_settings"
+ namespace: "virtual_devices"
+ description: "Show virtual devices in Settings"
+ bug: "338974320"
+}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index ff0bb25bbccc..cc57dc05d6b1 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -398,6 +398,7 @@ public class ClipData implements Parcelable {
* Retrieve the raw Intent contained in this Item.
*/
public Intent getIntent() {
+ Intent.maybeMarkAsMissingCreatorToken(mIntent);
return mIntent;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 186f7b3e111c..6086f2455a31 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6802,6 +6802,12 @@ public abstract class Context {
public static final String MEDIA_QUALITY_SERVICE = "media_quality";
/**
+ * Service to perform operations needed for dynamic instrumentation.
+ * @hide
+ */
+ public static final String DYNAMIC_INSTRUMENTATION_SERVICE = "dynamic_instrumentation";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6fa5a9b82858..88533049f970 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -108,6 +108,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
+import java.util.function.Consumer;
/**
* An intent is an abstract description of an operation to be performed. It
@@ -892,6 +893,20 @@ public class Intent implements Parcelable, Cloneable {
public static void maybeMarkAsMissingCreatorToken(Object object) {
if (object instanceof Intent intent) {
maybeMarkAsMissingCreatorTokenInternal(intent);
+ } else if (object instanceof Parcelable[] parcelables) {
+ for (Parcelable p : parcelables) {
+ if (p instanceof Intent intent) {
+ maybeMarkAsMissingCreatorTokenInternal(intent);
+ }
+ }
+ } else if (object instanceof ArrayList parcelables) {
+ int N = parcelables.size();
+ for (int i = 0; i < N; i++) {
+ Object p = parcelables.get(i);
+ if (p instanceof Intent intent) {
+ maybeMarkAsMissingCreatorTokenInternal(intent);
+ }
+ }
}
}
@@ -12204,7 +12219,68 @@ public class Intent implements Parcelable, Cloneable {
// Stores a creator token for an intent embedded as an extra intent in a top level intent,
private IBinder mCreatorToken;
// Stores all extra keys whose values are intents for a top level intent.
- private ArraySet<String> mExtraIntentKeys;
+ private ArraySet<NestedIntentKey> mNestedIntentKeys;
+ }
+
+ /**
+ * @hide
+ */
+ public static class NestedIntentKey {
+ /** @hide */
+ @IntDef(flag = true, prefix = {"NESTED_INTENT_KEY_TYPE"}, value = {
+ NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL,
+ NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY,
+ NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST,
+ NESTED_INTENT_KEY_TYPE_CLIP_DATA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface NestedIntentKeyType {
+ }
+
+ /**
+ * This flag indicates the key is for an extra parcel in mExtras.
+ */
+ private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL = 1 << 0;
+
+ /**
+ * This flag indicates the key is for an extra parcel array in mExtras and the index is the
+ * index of that array.
+ */
+ private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY = 1 << 1;
+
+ /**
+ * This flag indicates the key is for an extra parcel list in mExtras and the index is the
+ * index of that list.
+ */
+ private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST = 1 << 2;
+
+ /**
+ * This flag indicates the key is for an extra parcel in mClipData.mItems.
+ */
+ private static final int NESTED_INTENT_KEY_TYPE_CLIP_DATA = 1 << 3;
+
+ private final @NestedIntentKeyType int mType;
+ private final String mKey;
+ private final int mIndex;
+
+ private NestedIntentKey(@NestedIntentKeyType int type, String key, int index) {
+ this.mType = type;
+ this.mKey = key;
+ this.mIndex = index;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NestedIntentKey that = (NestedIntentKey) o;
+ return mType == that.mType && mIndex == that.mIndex && Objects.equals(mKey, that.mKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mKey, mIndex);
+ }
}
private @Nullable CreatorTokenInfo mCreatorTokenInfo;
@@ -12227,8 +12303,8 @@ public class Intent implements Parcelable, Cloneable {
}
/** @hide */
- public Set<String> getExtraIntentKeys() {
- return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys;
+ public Set<NestedIntentKey> getExtraIntentKeys() {
+ return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mNestedIntentKeys;
}
/** @hide */
@@ -12246,45 +12322,178 @@ public class Intent implements Parcelable, Cloneable {
* @hide
*/
public void collectExtraIntentKeys() {
- if (!preventIntentRedirect()) return;
+ if (preventIntentRedirect()) {
+ collectNestedIntentKeysRecur(new ArraySet<>());
+ }
+ }
- if (mExtras != null && !mExtras.isEmpty()) {
+ private void collectNestedIntentKeysRecur(Set<Intent> visited) {
+ if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
- if (mExtras.get(key) instanceof Intent) {
- if (mCreatorTokenInfo == null) {
- mCreatorTokenInfo = new CreatorTokenInfo();
- }
- if (mCreatorTokenInfo.mExtraIntentKeys == null) {
- mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>();
- }
- mCreatorTokenInfo.mExtraIntentKeys.add(key);
+ Object value = mExtras.get(key);
+
+ if (value instanceof Intent intent && !visited.contains(intent)) {
+ handleNestedIntent(intent, visited, new NestedIntentKey(
+ NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0));
+ } else if (value instanceof Parcelable[] parcelables) {
+ handleParcelableArray(parcelables, key, visited);
+ } else if (value instanceof ArrayList<?> parcelables) {
+ handleParcelableList(parcelables, key, visited);
}
}
}
+
+ if (mClipData != null) {
+ for (int i = 0; i < mClipData.getItemCount(); i++) {
+ Intent intent = mClipData.getItemAt(i).mIntent;
+ if (intent != null && !visited.contains(intent)) {
+ handleNestedIntent(intent, visited, new NestedIntentKey(
+ NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA, null, i));
+ }
+ }
+ }
+ }
+
+ private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key) {
+ visited.add(intent);
+ if (mCreatorTokenInfo == null) {
+ mCreatorTokenInfo = new CreatorTokenInfo();
+ }
+ if (mCreatorTokenInfo.mNestedIntentKeys == null) {
+ mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>();
+ }
+ mCreatorTokenInfo.mNestedIntentKeys.add(key);
+ intent.collectNestedIntentKeysRecur(visited);
+ }
+
+ private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited) {
+ for (int i = 0; i < parcelables.length; i++) {
+ if (parcelables[i] instanceof Intent intent && !visited.contains(intent)) {
+ handleNestedIntent(intent, visited, new NestedIntentKey(
+ NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, key, i));
+ }
+ }
}
+ private void handleParcelableList(ArrayList<?> parcelables, String key, Set<Intent> visited) {
+ for (int i = 0; i < parcelables.size(); i++) {
+ if (parcelables.get(i) instanceof Intent intent && !visited.contains(intent)) {
+ handleNestedIntent(intent, visited, new NestedIntentKey(
+ NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, key, i));
+ }
+ }
+ }
+
+ private static final Consumer<Intent> MARK_TRUSTED_TOKEN_PRESENT_ACTION = intent -> {
+ intent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
+ };
+
+ private static final Consumer<Intent> ENABLE_TOKEN_VERIFY_ACTION = intent -> {
+ if (intent.mExtras != null) {
+ intent.mExtras.enableTokenVerification();
+ }
+ };
+
/** @hide */
public void checkCreatorToken() {
- if (mExtras == null) return;
- if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) {
- for (String key : mCreatorTokenInfo.mExtraIntentKeys) {
- try {
- Intent extraIntent = mExtras.getParcelable(key, Intent.class);
- if (extraIntent == null) {
- Log.w(TAG, "The key {" + key
- + "} does not correspond to an intent in the bundle.");
- continue;
+ forEachNestedCreatorToken(MARK_TRUSTED_TOKEN_PRESENT_ACTION, ENABLE_TOKEN_VERIFY_ACTION);
+ if (mExtras != null) {
+ // mark the bundle as intent extras after calls to getParcelable.
+ // otherwise, the logic to mark missing token would run before
+ // mark trusted creator token present.
+ mExtras.enableTokenVerification();
+ }
+ }
+
+ /** @hide */
+ public void forEachNestedCreatorToken(Consumer<? super Intent> action) {
+ forEachNestedCreatorToken(action, null);
+ }
+
+ private void forEachNestedCreatorToken(Consumer<? super Intent> action,
+ Consumer<? super Intent> postAction) {
+ if (mExtras == null && mClipData == null) return;
+
+ if (mCreatorTokenInfo != null && mCreatorTokenInfo.mNestedIntentKeys != null) {
+ int N = mCreatorTokenInfo.mNestedIntentKeys.size();
+ for (int i = 0; i < N; i++) {
+ NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
+ Intent extraIntent = extractIntentFromKey(key);
+
+ if (extraIntent != null) {
+ action.accept(extraIntent);
+ extraIntent.forEachNestedCreatorToken(action);
+ if (postAction != null) {
+ postAction.accept(extraIntent);
}
- extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
- } catch (Exception e) {
- Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e);
+ } else {
+ Log.w(TAG, getLogMessageForKey(key));
}
}
}
- // mark the bundle as intent extras after calls to getParcelable.
- // otherwise, the logic to mark missing token would run before
- // mark trusted creator token present.
- mExtras.setIsIntentExtra();
+ }
+
+ private Intent extractIntentFromKey(NestedIntentKey key) {
+ switch (key.mType) {
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL:
+ return mExtras == null ? null : mExtras.getParcelable(key.mKey, Intent.class);
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY:
+ if (mExtras == null) return null;
+ Intent[] extraIntents = mExtras.getParcelableArray(key.mKey, Intent.class);
+ if (extraIntents != null && key.mIndex < extraIntents.length) {
+ return extraIntents[key.mIndex];
+ }
+ break;
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST:
+ if (mExtras == null) return null;
+ ArrayList<Intent> extraIntentsList = mExtras.getParcelableArrayList(key.mKey,
+ Intent.class);
+ if (extraIntentsList != null && key.mIndex < extraIntentsList.size()) {
+ return extraIntentsList.get(key.mIndex);
+ }
+ break;
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA:
+ if (mClipData == null) return null;
+ if (key.mIndex < mClipData.getItemCount()) {
+ ClipData.Item item = mClipData.getItemAt(key.mIndex);
+ if (item != null) {
+ return item.mIntent;
+ }
+ }
+ break;
+ }
+ return null;
+ }
+
+ private String getLogMessageForKey(NestedIntentKey key) {
+ switch (key.mType) {
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL:
+ return "The key {" + key + "} does not correspond to an intent in the bundle.";
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY:
+ if (mExtras.getParcelableArray(key.mKey, Intent.class) == null) {
+ return "The key {" + key
+ + "} does not correspond to a Parcelable[] in the bundle.";
+ } else {
+ return "Parcelable[" + key.mIndex + "] for key {" + key + "} is not an intent.";
+ }
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST:
+ if (mExtras.getParcelableArrayList(key.mKey, Intent.class) == null) {
+ return "The key {" + key
+ + "} does not correspond to an ArrayList<Parcelable> in the bundle.";
+ } else {
+ return "List.get(" + key.mIndex + ") for key {" + key + "} is not an intent.";
+ }
+ case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA:
+ if (key.mIndex >= mClipData.getItemCount()) {
+ return "Index out of range for clipData items. index: " + key.mIndex
+ + ". item counts: " + mClipData.getItemCount();
+ } else {
+ return "clipData items at index [" + key.mIndex
+ + "] is null or does not contain an intent.";
+ }
+ default:
+ return "Unknown key type: " + key.mType;
+ }
}
/**
@@ -12357,7 +12566,19 @@ public class Intent implements Parcelable, Cloneable {
} else {
out.writeInt(1);
out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken);
- out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys);
+
+ if (mCreatorTokenInfo.mNestedIntentKeys != null) {
+ final int N = mCreatorTokenInfo.mNestedIntentKeys.size();
+ out.writeInt(N);
+ for (int i = 0; i < N; i++) {
+ NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
+ out.writeInt(key.mType);
+ out.writeString8(key.mKey);
+ out.writeInt(key.mIndex);
+ }
+ } else {
+ out.writeInt(0);
+ }
}
}
}
@@ -12422,7 +12643,18 @@ public class Intent implements Parcelable, Cloneable {
if (in.readInt() != 0) {
mCreatorTokenInfo = new CreatorTokenInfo();
mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
- mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null);
+
+ N = in.readInt();
+ if (N > 0) {
+ mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(N);
+ for (int i = 0; i < N; i++) {
+ int type = in.readInt();
+ String key = in.readString8();
+ int index = in.readInt();
+ mCreatorTokenInfo.mNestedIntentKeys.append(
+ new NestedIntentKey(type, key, index));
+ }
+ }
}
}
}
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index 830cbe0e0dd0..ade58c45b024 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.UserHandle;
@@ -34,4 +35,5 @@ oneway interface IOnAppsChangedListener {
void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
void onPackageLoadingProgressChanged(in UserHandle user, String packageName, float progress);
+ void onUserConfigChanged(in LauncherUserInfo launcherUserInfo);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 26f919f99ee9..26b835689b67 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -182,6 +182,8 @@ public class LauncherApps {
*/
public static final int FLAG_CACHE_PEOPLE_TILE_SHORTCUTS = 2;
+ private static final String LAUNCHER_USER_INFO_EXTRA_KEY = "launcher_user_info";
+
/** @hide */
@IntDef(flag = false, prefix = { "FLAG_CACHE_" }, value = {
FLAG_CACHE_NOTIFICATION_SHORTCUTS,
@@ -349,6 +351,19 @@ public class LauncherApps {
*/
public void onPackageLoadingProgressChanged(@NonNull String packageName,
@NonNull UserHandle user, float progress) {}
+
+ /**
+ * Indicates {@link LauncherUserInfo} configs for a user have changed. The new
+ * {@link LauncherUserInfo} is given as a parameter.
+ *
+ * {@link LauncherUserInfo#getUserConfig} to get the updated user configs.
+ *
+ * @param launcherUserInfo The LauncherUserInfo of the user/profile whose configs have
+ * changed.
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ public void onUserConfigChanged(@NonNull LauncherUserInfo launcherUserInfo) {
+ }
}
/**
@@ -2168,6 +2183,21 @@ public class LauncherApps {
}
}
}
+
+ public void onUserConfigChanged(LauncherUserInfo launcherUserInfo) {
+ if (DEBUG) {
+ if (Flags.allowPrivateProfile()
+ && android.multiuser.Flags.addLauncherUserConfig()) {
+ Log.d(TAG, "OnUserConfigChanged for user type " + launcherUserInfo.getUserType()
+ + ", new userConfig: " + launcherUserInfo.getUserConfig());
+ }
+ }
+ synchronized (LauncherApps.this) {
+ for (CallbackMessageHandler callback : mCallbacks) {
+ callback.postOnUserConfigChanged(launcherUserInfo);
+ }
+ }
+ }
};
/**
@@ -2224,6 +2254,7 @@ public class LauncherApps {
private static final int MSG_UNSUSPENDED = 7;
private static final int MSG_SHORTCUT_CHANGED = 8;
private static final int MSG_LOADING_PROGRESS_CHANGED = 9;
+ private static final int MSG_USER_CONFIG_CHANGED = 10;
private final LauncherApps.Callback mCallback;
@@ -2278,6 +2309,14 @@ public class LauncherApps {
mCallback.onPackageLoadingProgressChanged(info.packageName, info.user,
info.mLoadingProgress);
break;
+ case MSG_USER_CONFIG_CHANGED:
+ if (Flags.allowPrivateProfile()
+ && android.multiuser.Flags.addLauncherUserConfig()) {
+ mCallback.onUserConfigChanged(Objects.requireNonNull(
+ info.launcherExtras.getParcelable(LAUNCHER_USER_INFO_EXTRA_KEY,
+ LauncherUserInfo.class)));
+ }
+ break;
}
}
@@ -2353,6 +2392,13 @@ public class LauncherApps {
info.mLoadingProgress = progress;
obtainMessage(MSG_LOADING_PROGRESS_CHANGED, info).sendToTarget();
}
+
+ public void postOnUserConfigChanged(LauncherUserInfo launcherUserInfo) {
+ CallbackInfo info = new CallbackInfo();
+ info.launcherExtras = new Bundle();
+ info.launcherExtras.putParcelable(LAUNCHER_USER_INFO_EXTRA_KEY, launcherUserInfo);
+ obtainMessage(MSG_USER_CONFIG_CHANGED, info).sendToTarget();
+ }
}
/**
diff --git a/core/java/android/content/pm/LauncherUserInfo.java b/core/java/android/content/pm/LauncherUserInfo.java
index 8426f54d4754..574af5902e89 100644
--- a/core/java/android/content/pm/LauncherUserInfo.java
+++ b/core/java/android/content/pm/LauncherUserInfo.java
@@ -18,6 +18,7 @@ package android.content.pm;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,11 +32,25 @@ import android.os.UserManager;
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public final class LauncherUserInfo implements Parcelable {
+ /**
+ * A boolean extra indicating whether the private space entrypoint should be hidden when locked.
+ *
+ * @see #getUserConfig
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ public static final String PRIVATE_SPACE_ENTRYPOINT_HIDDEN =
+ "private_space_entrypoint_hidden";
+
private final String mUserType;
// Serial number for the user, should be same as in the {@link UserInfo} object.
private final int mUserSerialNumber;
+ // Additional configs for the user, e.g., whether to hide the private space entrypoint when
+ // locked.
+ private final Bundle mUserConfig;
+
+
/**
* Returns type of the user as defined in {@link UserManager}. e.g.,
* {@link UserManager.USER_TYPE_PROFILE_MANAGED} or {@link UserManager.USER_TYPE_PROFILE_ClONE}
@@ -50,6 +65,17 @@ public final class LauncherUserInfo implements Parcelable {
}
/**
+ * Returns additional configs for this launcher user
+ *
+ * @see #PRIVATE_SPACE_ENTRYPOINT_HIDDEN
+ */
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ @NonNull
+ public Bundle getUserConfig() {
+ return mUserConfig;
+ }
+
+ /**
* Returns serial number of user as returned by
* {@link UserManager#getSerialNumberForUser(UserHandle)}
*
@@ -63,6 +89,7 @@ public final class LauncherUserInfo implements Parcelable {
private LauncherUserInfo(@NonNull Parcel in) {
mUserType = in.readString16NoHelper();
mUserSerialNumber = in.readInt();
+ mUserConfig = in.readBundle(Bundle.class.getClassLoader());
}
@Override
@@ -70,6 +97,7 @@ public final class LauncherUserInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString16NoHelper(mUserType);
dest.writeInt(mUserSerialNumber);
+ dest.writeBundle(mUserConfig);
}
@Override
@@ -99,23 +127,36 @@ public final class LauncherUserInfo implements Parcelable {
private final String mUserType;
private final int mUserSerialNumber;
+ private final Bundle mUserConfig;
+
+
+ @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+ public Builder(@NonNull String userType, int userSerialNumber, @NonNull Bundle config) {
+ this.mUserType = userType;
+ this.mUserSerialNumber = userSerialNumber;
+ this.mUserConfig = config;
+ }
public Builder(@NonNull String userType, int userSerialNumber) {
this.mUserType = userType;
this.mUserSerialNumber = userSerialNumber;
+ this.mUserConfig = new Bundle();
}
/**
* Builds the LauncherUserInfo object
*/
- @NonNull public LauncherUserInfo build() {
- return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber);
+ @NonNull
+ public LauncherUserInfo build() {
+ return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber, this.mUserConfig);
}
} // End builder
- private LauncherUserInfo(@NonNull String userType, int userSerialNumber) {
+ private LauncherUserInfo(@NonNull String userType, int userSerialNumber,
+ @NonNull Bundle config) {
this.mUserType = userType;
this.mUserSerialNumber = userSerialNumber;
+ this.mUserConfig = config;
}
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 54c5596623a2..63279af8480d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2936,6 +2936,8 @@ public class PackageInstaller {
public @Nullable String dexoptCompilerFilter = null;
/** {@hide} */
public boolean forceVerification;
+ /** {@hide} */
+ public boolean isAutoInstallDependenciesEnabled = true;
private final ArrayMap<String, Integer> mPermissionStates;
@@ -2991,6 +2993,7 @@ public class PackageInstaller {
unarchiveId = source.readInt();
dexoptCompilerFilter = source.readString();
forceVerification = source.readBoolean();
+ isAutoInstallDependenciesEnabled = source.readBoolean();
}
/** {@hide} */
@@ -3028,6 +3031,7 @@ public class PackageInstaller {
ret.unarchiveId = unarchiveId;
ret.dexoptCompilerFilter = dexoptCompilerFilter;
ret.forceVerification = forceVerification;
+ ret.isAutoInstallDependenciesEnabled = isAutoInstallDependenciesEnabled;
return ret;
}
@@ -3744,6 +3748,23 @@ public class PackageInstaller {
this.forceVerification = true;
}
+ /**
+ * Optionally indicate whether missing SDK or static shared library dependencies should be
+ * automatically fetched and installed when installing an app that wants to use these
+ * dependencies.
+ *
+ * <p> This feature is enabled by default.
+ *
+ * @param enableAutoInstallDependencies {@code true} to enable auto-installation of missing
+ * SDK or static shared library dependencies,
+ * {@code false} to disable and fail immediately if
+ * dependencies aren't already installed.
+ */
+ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public void setEnableAutoInstallDependencies(boolean enableAutoInstallDependencies) {
+ isAutoInstallDependenciesEnabled = enableAutoInstallDependencies;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -3780,6 +3801,7 @@ public class PackageInstaller {
pw.printPair("unarchiveId", unarchiveId);
pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter);
pw.printPair("forceVerification", forceVerification);
+ pw.printPair("isAutoInstallDependenciesEnabled", isAutoInstallDependenciesEnabled);
pw.println();
}
@@ -3827,6 +3849,7 @@ public class PackageInstaller {
dest.writeInt(unarchiveId);
dest.writeString(dexoptCompilerFilter);
dest.writeBoolean(forceVerification);
+ dest.writeBoolean(isAutoInstallDependenciesEnabled);
}
public static final Parcelable.Creator<SessionParams>
@@ -4005,6 +4028,9 @@ public class PackageInstaller {
private String mSessionErrorMessage;
/** {@hide} */
+ public boolean isAutoInstallingDependenciesEnabled;
+
+ /** {@hide} */
public boolean isCommitted;
/** {@hide} */
@@ -4097,6 +4123,7 @@ public class PackageInstaller {
packageSource = source.readInt();
applicationEnabledSettingPersistent = source.readBoolean();
pendingUserActionReason = source.readInt();
+ isAutoInstallingDependenciesEnabled = source.readBoolean();
}
/**
@@ -4681,6 +4708,16 @@ public class PackageInstaller {
return (installFlags & PackageManager.INSTALL_UNARCHIVE) != 0;
}
+ /**
+ * Check whether missing SDK or static shared library dependencies should be automatically
+ * fetched and installed when installing an app that wants to use these dependencies.
+ *
+ * @return true if the dependencies will be auto-installed, false otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public boolean isAutoInstallDependenciesEnabled() {
+ return isAutoInstallingDependenciesEnabled;
+ }
@Override
public int describeContents() {
@@ -4735,6 +4772,7 @@ public class PackageInstaller {
dest.writeInt(packageSource);
dest.writeBoolean(applicationEnabledSettingPersistent);
dest.writeInt(pendingUserActionReason);
+ dest.writeBoolean(isAutoInstallingDependenciesEnabled);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 5b0cee75e591..4285b0a2b91a 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -251,6 +251,7 @@ public class ServiceInfo extends ComponentInfo
* {@link android.Manifest.permission#NFC},
* {@link android.Manifest.permission#TRANSMIT_IR},
* {@link android.Manifest.permission#UWB_RANGING},
+ * {@link android.Manifest.permission#RANGING},
* or has been granted the access to one of the attached USB devices/accessories.
*/
@RequiresPermission(
@@ -267,6 +268,7 @@ public class ServiceInfo extends ComponentInfo
Manifest.permission.NFC,
Manifest.permission.TRANSMIT_IR,
Manifest.permission.UWB_RANGING,
+ Manifest.permission.RANGING,
},
conditional = true
)
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 6f70586881be..9ba5a352358b 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -321,6 +321,7 @@ flag {
flag {
name: "sdk_dependency_installer"
+ is_exported: true
namespace: "package_manager_service"
description: "Feature flag to enable installation of missing sdk dependency of app"
bug: "370822870"
@@ -349,3 +350,19 @@ flag {
bug: "364760703"
is_fixed_read_only: true
}
+
+flag {
+ name: "cloud_compilation_pm"
+ is_exported: true
+ namespace: "art_mainline"
+ description: "Feature flag to enable the Cloud Compilation support on the package manager side."
+ bug: "377474232"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "support_minor_versions_in_minsdkversion"
+ namespace: "package_manager_service"
+ description: "Block app installations that specify an incompatible minor SDK version"
+ bug: "377474232"
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 528bde80cd3d..813208d7ff38 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -330,6 +330,17 @@ flag {
is_fixed_read_only: true
}
+flag {
+ name: "cache_user_info_read_only"
+ namespace: "multiuser"
+ description: "Cache UserInfo to avoid unnecessary binder calls"
+ bug: "161915546"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_fixed_read_only: true
+}
+
# This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
flag {
name: "enable_private_space_features"
@@ -543,3 +554,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "add_launcher_user_config"
+ namespace: "profile_experiences"
+ description: "Add support for LauncherUserInfo configs"
+ bug: "346553745"
+}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 34c3f5798bc5..e9e8578af787 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -38,6 +38,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.EmptyArray;
import android.util.Pair;
import android.util.Slog;
@@ -565,10 +566,7 @@ public class ApkLiteParseUtils {
usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor,
/*allowDuplicates=*/ true);
- // We allow ":" delimiters in the SHA declaration as this is the format
- // emitted by the certtool making it easy for developers to copy/paste.
- // TODO(372862145): Add test for this replacement
- usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase();
+ usesSdkCertDigest = normalizeCertDigest(usesSdkCertDigest);
if ("".equals(usesSdkCertDigest)) {
// Test-only uses-sdk-library empty certificate digest override.
@@ -618,18 +616,23 @@ public class ApkLiteParseUtils {
usesStaticLibrariesVersions, usesStaticLibVersion,
/*allowDuplicates=*/ true);
- // We allow ":" delimiters in the SHA declaration as this is the format
- // emitted by the certtool making it easy for developers to copy/paste.
- // TODO(372862145): Add test for this replacement
- usesStaticLibCertDigest =
- usesStaticLibCertDigest.replace(":", "").toLowerCase();
+ usesStaticLibCertDigest = normalizeCertDigest(usesStaticLibCertDigest);
+
+ ParseResult<String[]> certResult =
+ parseAdditionalCertificates(input, parser);
+ if (certResult.isError()) {
+ return input.error(certResult);
+ }
+ String[] additionalCertSha256Digests = certResult.getResult();
+ String[] certSha256Digests =
+ new String[additionalCertSha256Digests.length + 1];
+ certSha256Digests[0] = usesStaticLibCertDigest;
+ System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+ 1, additionalCertSha256Digests.length);
- // TODO(372862145): Add support for multiple signer for app targeting
- // O-MR1
usesStaticLibrariesCertDigests = ArrayUtils.appendElement(
String[].class, usesStaticLibrariesCertDigests,
- new String[]{usesStaticLibCertDigest},
- /*allowDuplicates=*/ true);
+ certSha256Digests, /*allowDuplicates=*/ true);
break;
case TAG_SDK_LIBRARY:
isSdkLibrary = true;
@@ -809,6 +812,43 @@ public class ApkLiteParseUtils {
declaredLibraries));
}
+ private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
+ XmlResourceParser parser) throws XmlPullParserException, IOException {
+ String[] certSha256Digests = EmptyArray.STRING;
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final String nodeName = parser.getName();
+ if (nodeName.equals("additional-certificate")) {
+ String certSha256Digest = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "certDigest");
+ if (TextUtils.isEmpty(certSha256Digest)) {
+ return input.error("Bad additional-certificate declaration with empty"
+ + " certDigest:" + certSha256Digest);
+ }
+
+ certSha256Digest = normalizeCertDigest(certSha256Digest);
+ certSha256Digests = ArrayUtils.appendElement(String.class,
+ certSha256Digests, certSha256Digest);
+ }
+ }
+
+ return input.success(certSha256Digests);
+ }
+
+ /**
+ * We allow ":" delimiters in the SHA declaration as this is the format emitted by the
+ * certtool making it easy for developers to copy/paste.
+ */
+ private static String normalizeCertDigest(String certDigest) {
+ return certDigest.replace(":", "").toLowerCase();
+ }
+
private static boolean isDeviceAdminReceiver(
XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)
throws XmlPullParserException, IOException {
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 68b5d782bfbf..908999b64961 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -124,11 +124,13 @@ public final class ApkAssets {
@Nullable
@GuardedBy("this")
- private final StringBlock mStringBlock; // null or closed if mNativePtr = 0.
+ private StringBlock mStringBlock; // null or closed if mNativePtr = 0.
@PropertyFlags
private final int mFlags;
+ private final boolean mIsOverlay;
+
@Nullable
private final AssetsProvider mAssets;
@@ -302,40 +304,43 @@ public final class ApkAssets {
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(path, "path");
- mFlags = flags;
mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mFlags = flags;
mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mFlags = flags;
mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- mAssets = assets;
}
private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
- mFlags = flags;
+ this(FORMAT_APK, flags, assets);
mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
+ }
+
+ private ApkAssets(@FormatType int format, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) {
+ mFlags = flags;
mAssets = assets;
+ mIsOverlay = format == FORMAT_IDMAP;
}
@UnsupportedAppUsage
@@ -425,6 +430,18 @@ public final class ApkAssets {
}
}
+ public boolean isSystem() {
+ return (mFlags & PROPERTY_SYSTEM) != 0;
+ }
+
+ public boolean isSharedLib() {
+ return (mFlags & PROPERTY_DYNAMIC) != 0;
+ }
+
+ public boolean isOverlay() {
+ return mIsOverlay;
+ }
+
@Override
public String toString() {
return "ApkAssets{path=" + getDebugName() + "}";
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index e6b93427f413..bcaceb24d767 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -203,9 +203,25 @@ public class ResourcesImpl {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
- mAssets = assets;
- mAppliedSharedLibsHash =
- ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
+ // Don't reuse assets by default as we have no control over whether they're already
+ // inside some other ResourcesImpl.
+ this(assets, metrics, config, displayAdjustments, false);
+ }
+
+ public ResourcesImpl(@NonNull ResourcesImpl orig) {
+ // We know for sure that the other assets are in use, so can't reuse the object here.
+ this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(),
+ orig.getDisplayAdjustments(), false);
+ }
+
+ public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
+ @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments,
+ boolean reuseAssets) {
+ final var assetsAndHash =
+ ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets,
+ reuseAssets);
+ mAssets = assetsAndHash.first;
+ mAppliedSharedLibsHash = assetsAndHash.second;
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
index c2febaeec73a..e8893e47ddc8 100644
--- a/core/java/android/content/res/TEST_MAPPING
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -5,6 +5,9 @@
},
{
"path": "frameworks/base/core/tests/coretests/src/com/android/internal/content/res"
+ },
+ {
+ "path": "platform_testing/libraries/screenshot"
}
],
"presubmit": [
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index e98fc0c9d02c..f23c193e2da0 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -83,3 +83,25 @@ flag {
bug: "364035303"
}
+flag {
+ name: "system_context_handle_app_info_changed"
+ is_exported: true
+ namespace: "resource_manager"
+ description: "Feature flag for allowing system context to handle application info changes"
+ bug: "362420029"
+ # This flag is read at boot time.
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "layout_readwrite_flags"
+ is_exported: true
+ namespace: "resource_manager"
+ description: "Feature flag for allowing read/write flags in layout files"
+ bug: "377974898"
+ # This flag is used to control aapt2 behavior.
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index d2435757756c..6c35d106bfb7 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -3,6 +3,16 @@ container: "system"
flag {
namespace: "credential_manager"
+ name: "ttl_fix_enabled"
+ description: "Enable fix for transaction too large issue"
+ bug: "371052524"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "credential_manager"
name: "settings_activity_enabled"
is_exported: true
description: "Enable the Credential Manager Settings Activity APIs"
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 41585b3571d6..7d6e7ad857d1 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -215,7 +215,7 @@ final class BulkCursorProxy implements IBulkCursor {
// If close() is being called from the finalizer thread, do not wait for a reply from
// the remote side.
final boolean fromFinalizer =
- android.database.sqlite.Flags.onewayFinalizerClose()
+ android.database.sqlite.Flags.onewayFinalizerCloseFixed()
&& "FinalizerDaemon".equals(Thread.currentThread().getName());
mRemote.transact(CLOSE_TRANSACTION, data, reply,
fromFinalizer ? IBinder.FLAG_ONEWAY: 0);
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index ef59e0af3a27..93ef5c365730 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -45,7 +45,7 @@ import dalvik.system.CloseGuard;
* </p>
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("CursorWindow_host")
+@RavenwoodRedirectionClass("CursorWindow_ravenwood")
public class CursorWindow extends SQLiteClosable implements Parcelable {
private static final String STATS_TAG = "CursorWindowStats";
diff --git a/ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java b/core/java/android/database/CursorWindow_ravenwood.java
index e21a9cd71a2d..990ec5e9d59e 100644
--- a/ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java
+++ b/core/java/android/database/CursorWindow_ravenwood.java
@@ -17,6 +17,7 @@ package android.database;
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.util.Base64;
import java.text.DecimalFormat;
@@ -26,9 +27,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-public class CursorWindow_host {
+@RavenwoodKeepWholeClass
+class CursorWindow_ravenwood {
- private static final HashMap<Long, CursorWindow_host> sInstances = new HashMap<>();
+ private static final HashMap<Long, CursorWindow_ravenwood> sInstances = new HashMap<>();
private static long sNextId = 1;
private String mName;
@@ -41,7 +43,7 @@ public class CursorWindow_host {
private final List<Row> mRows = new ArrayList<>();
public static long nativeCreate(String name, int cursorWindowSize) {
- CursorWindow_host instance = new CursorWindow_host();
+ CursorWindow_ravenwood instance = new CursorWindow_ravenwood();
instance.mName = name;
long instanceId = sNextId++;
sInstances.put(instanceId, instance);
@@ -66,7 +68,7 @@ public class CursorWindow_host {
}
public static boolean nativeAllocRow(long windowPtr) {
- CursorWindow_host instance = sInstances.get(windowPtr);
+ CursorWindow_ravenwood instance = sInstances.get(windowPtr);
Row row = new Row();
row.mFields = new String[instance.mColumnNum];
row.mTypes = new int[instance.mColumnNum];
@@ -76,7 +78,7 @@ public class CursorWindow_host {
}
private static boolean put(long windowPtr, String value, int type, int row, int column) {
- CursorWindow_host instance = sInstances.get(windowPtr);
+ CursorWindow_ravenwood instance = sInstances.get(windowPtr);
if (row >= instance.mRows.size() || column >= instance.mColumnNum) {
return false;
}
@@ -87,7 +89,7 @@ public class CursorWindow_host {
}
public static int nativeGetType(long windowPtr, int row, int column) {
- CursorWindow_host instance = sInstances.get(windowPtr);
+ CursorWindow_ravenwood instance = sInstances.get(windowPtr);
if (row >= instance.mRows.size() || column >= instance.mColumnNum) {
return Cursor.FIELD_TYPE_NULL;
}
@@ -101,7 +103,7 @@ public class CursorWindow_host {
}
public static String nativeGetString(long windowPtr, int row, int column) {
- CursorWindow_host instance = sInstances.get(windowPtr);
+ CursorWindow_ravenwood instance = sInstances.get(windowPtr);
if (row >= instance.mRows.size() || column >= instance.mColumnNum) {
return null;
}
@@ -164,7 +166,7 @@ public class CursorWindow_host {
}
public static void nativeWriteToParcel(long windowPtr, Parcel parcel) {
- CursorWindow_host window = sInstances.get(windowPtr);
+ CursorWindow_ravenwood window = sInstances.get(windowPtr);
parcel.writeString(window.mName);
parcel.writeInt(window.mColumnNum);
parcel.writeInt(window.mRows.size());
@@ -176,7 +178,7 @@ public class CursorWindow_host {
public static long nativeCreateFromParcel(Parcel parcel) {
long windowPtr = nativeCreate(null, 0);
- CursorWindow_host window = sInstances.get(windowPtr);
+ CursorWindow_ravenwood window = sInstances.get(windowPtr);
window.mName = parcel.readString();
window.mColumnNum = parcel.readInt();
int rowCount = parcel.readInt();
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index 826b908e6775..d43a66904af8 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -2,8 +2,9 @@ package: "android.database.sqlite"
container: "system"
flag {
- name: "oneway_finalizer_close"
+ name: "oneway_finalizer_close_fixed"
namespace: "system_performance"
+ is_fixed_read_only: true
description: "Make BuildCursorNative.close oneway if in the the finalizer"
bug: "368221351"
}
diff --git a/core/java/android/hardware/DataSpace.java b/core/java/android/hardware/DataSpace.java
index 611738435f7e..1cd9244333c5 100644
--- a/core/java/android/hardware/DataSpace.java
+++ b/core/java/android/hardware/DataSpace.java
@@ -420,18 +420,38 @@ public final class DataSpace {
public static final int DATASPACE_HEIF = 4100;
/**
- * ISO/IEC TBD
+ * Ultra HDR
*
- * JPEG image with embedded recovery map following the Jpeg/R specification.
+ * JPEG image with embedded HDR gain map following the Ultra HDR specification and
+ * starting with Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM V}
+ * ISO/CD 21496‐1
*
- * <p>This value must always remain aligned with the public ImageFormat Jpeg/R definition and is
- * valid with formats:
- * HAL_PIXEL_FORMAT_BLOB: JPEG image encoded by Jpeg/R encoder according to ISO/IEC TBD.
- * The image contains a standard SDR JPEG and a recovery map. Jpeg/R decoders can use the
- * map to recover the input image.</p>
+ * <p>This value is valid with formats:</p>
+ * <ul>
+ * <li>HAL_PIXEL_FORMAT_BLOB: JPEG image encoded by Jpeg/R encoder according to
+ * ISO/CD 21496‐1</li>
+ * </ul>
+ * <p>
+ * The image contains a standard SDR JPEG and a gain map. Ultra HDR decoders can use the
+ * gain map to boost the brightness of the rendered image.</p>
*/
public static final int DATASPACE_JPEG_R = 4101;
+ /**
+ * ISO/IEC 23008-12:2024
+ *
+ * High Efficiency Image File Format (HEIF) with embedded HDR gain map
+ *
+ * <p>This value is valid with formats:</p>
+ * <ul>
+ * <li>HAL_PIXEL_FORMAT_BLOB: A HEIC image encoded by HEVC encoder
+ * according to ISO/IEC 23008-12:2024 that includes an HDR gain map and
+ * metadata according to ISO/CD 21496‐1.</li>
+ * </ul>
+ */
+ @FlaggedApi(com.android.internal.camera.flags.Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final int DATASPACE_HEIF_ULTRAHDR = 4102;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
@@ -660,6 +680,7 @@ public final class DataSpace {
DATASPACE_DEPTH,
DATASPACE_DYNAMIC_DEPTH,
DATASPACE_HEIF,
+ DATASPACE_HEIF_ULTRAHDR,
DATASPACE_JPEG_R,
DATASPACE_UNKNOWN,
DATASPACE_SCRGB_LINEAR,
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 16d82caa6c1a..58e524e741b3 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -560,7 +560,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* on a particular SessionConfiguration.</p>
*
* @return List of CameraCharacteristic keys containing characterisitics specific to a session
- * configuration. If {@link #INFO_SESSION_CONFIGURATION_QUERY_VERSION} is
+ * configuration. If {@link #INFO_SESSION_CONFIGURATION_QUERY_VERSION} is at least
* {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, then this list will only contain
* CONTROL_ZOOM_RATIO_RANGE and SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
*
@@ -1436,6 +1436,24 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.util.Range<Float>>("android.control.lowLightBoostInfoLuminanceRange", new TypeReference<android.util.Range<Float>>() {{ }});
/**
+ * <p>List of auto-exposure priority modes for {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode}
+ * that are supported by this camera device.</p>
+ * <p>This entry lists the valid modes for
+ * {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} for this camera device.
+ * If no AE priority modes are available for a device, this will only list OFF.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_AE_PRIORITY)
+ public static final Key<int[]> CONTROL_AE_AVAILABLE_PRIORITY_MODES =
+ new Key<int[]>("android.control.aeAvailablePriorityModes", int[].class);
+
+ /**
* <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera
* device.</p>
* <p>Full-capability camera devices must always support OFF; camera devices that support
@@ -5241,6 +5259,32 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>All of the above configurations can be set up with a SessionConfiguration. The list of
* OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and
* the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p>
+ * <p>When set to BAKLAVA, the additional stream combinations below are verified
+ * by the compliance tests:</p>
+ * <table>
+ * <thead>
+ * <tr>
+ * <th style="text-align: center;">Target 1</th>
+ * <th style="text-align: center;">Size</th>
+ * <th style="text-align: center;">Target 2</th>
+ * <th style="text-align: center;">Size</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">S1080P</td>
+ * </tr>
+ * <tr>
+ * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">S1440P</td>
+ * </tr>
+ * </tbody>
+ * </table>
* <p>This key is available on all devices.</p>
*/
@PublicKey
@@ -5775,6 +5819,122 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
/**
+ * <p>The available HEIC (ISO/IEC 23008-12/24) UltraHDR stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ * <p>The configurations are listed as <code>(format, width, height, input?)</code> tuples.</p>
+ * <p>All the static, control, and dynamic metadata tags related to JPEG apply to HEIC formats.
+ * Configuring JPEG and HEIC streams at the same time is not supported.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicUltraHdrStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC UltraHDR output formats.</p>
+ * <p>This should correspond to the frame duration when only that
+ * stream is active, with all processing (typically in android.*.mode)
+ * set to either OFF or FAST.</p>
+ * <p>When multiple streams are used in a request, the minimum frame
+ * duration will be max(individual stream min durations).</p>
+ * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+ * android.scaler.availableStallDurations for more details about
+ * calculating the max frame rate.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC UltraHDR streams.</p>
+ * <p>A stall duration is how much extra time would get added
+ * to the normal minimum frame duration for a repeating request
+ * that has streams with non-zero stall.</p>
+ * <p>This functions similarly to
+ * android.scaler.availableStallDurations for HEIC UltraHDR
+ * streams.</p>
+ * <p>All HEIC output stream formats may have a nonzero stall
+ * duration.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>The available HEIC (ISO/IEC 23008-12/24) UltraHDR stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream) for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicStreamConfigurations for details.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicUltraHdrStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC UltraHDR output formats for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicMinFrameDurations for details.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC UltraHDR streams for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicStallDurations for details.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicUltraHdrStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
* <p>The direction of the camera faces relative to the vehicle body frame and the
* passenger seats.</p>
* <p>This enum defines the lens facing characteristic of the cameras on the automotive
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b7856303fc05..75e20582b7b4 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -994,7 +994,7 @@ public final class CameraManager {
AttributionSourceState contextAttributionSourceState =
contextAttributionSource.asState();
- if (Flags.useContextAttributionSource() && useContextAttributionSource) {
+ if (Flags.dataDeliveryPermissionChecks() && useContextAttributionSource) {
return contextAttributionSourceState;
} else {
AttributionSourceState clientAttribution =
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 86bbd4a57a63..8d36fbdc8b10 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -3371,6 +3371,74 @@ public abstract class CameraMetadata<TKey> {
public static final int CONTROL_AUTOFRAMING_AUTO = 2;
//
+ // Enumeration values for CaptureRequest#CONTROL_ZOOM_METHOD
+ //
+
+ /**
+ * <p>The camera device automatically detects whether the application does zoom with
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and in turn decides which
+ * metadata tag reflects the effective zoom level.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CaptureRequest#CONTROL_ZOOM_METHOD
+ */
+ @FlaggedApi(Flags.FLAG_ZOOM_METHOD)
+ public static final int CONTROL_ZOOM_METHOD_AUTO = 0;
+
+ /**
+ * <p>The application intends to control zoom via {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and
+ * the effective zoom level is reflected by {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} in capture results.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see CaptureRequest#CONTROL_ZOOM_METHOD
+ */
+ @FlaggedApi(Flags.FLAG_ZOOM_METHOD)
+ public static final int CONTROL_ZOOM_METHOD_ZOOM_RATIO = 1;
+
+ //
+ // Enumeration values for CaptureRequest#CONTROL_AE_PRIORITY_MODE
+ //
+
+ /**
+ * <p>Disable AE priority mode. This is the default value.</p>
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
+ */
+ @FlaggedApi(Flags.FLAG_AE_PRIORITY)
+ public static final int CONTROL_AE_PRIORITY_MODE_OFF = 0;
+
+ /**
+ * <p>The camera device's auto-exposure routine is active and
+ * prioritizes the application-selected ISO ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}).</p>
+ * <p>The application has control over {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} while
+ * the application's values for {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
+ * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored.</p>
+ *
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
+ */
+ @FlaggedApi(Flags.FLAG_AE_PRIORITY)
+ public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY = 1;
+
+ /**
+ * <p>The camera device's auto-exposure routine is active and
+ * prioritizes the application-selected exposure time
+ * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}).</p>
+ * <p>The application has control over {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} while
+ * the application's values for {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and
+ * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored.</p>
+ *
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
+ */
+ @FlaggedApi(Flags.FLAG_AE_PRIORITY)
+ public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY = 2;
+
+ //
// Enumeration values for CaptureRequest#EDGE_MODE
//
@@ -4289,6 +4357,39 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int SYNC_FRAME_NUMBER_UNKNOWN = -2;
+ //
+ // Enumeration values for CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR
+ //
+
+ /**
+ * <p>The camera can't accurately assess the scene's lighting to determine if a Night Mode
+ * Camera Extension capture would improve the photo. This can happen when the current
+ * camera configuration doesn't support night mode indicator detection, such as when
+ * the auto exposure mode is ON_AUTO_FLASH, ON_ALWAYS_FLASH, ON_AUTO_FLASH_REDEYE, or
+ * ON_EXTERNAL_FLASH.</p>
+ * @see CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR
+ */
+ @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR)
+ public static final int EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN = 0;
+
+ /**
+ * <p>The camera has detected lighting conditions that are sufficiently bright. Night
+ * Mode Camera Extensions is available but may not be able to optimize the camera
+ * settings to take a higher quality photo.</p>
+ * @see CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR
+ */
+ @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR)
+ public static final int EXTENSION_NIGHT_MODE_INDICATOR_OFF = 1;
+
+ /**
+ * <p>The camera has detected low-light conditions. It is recommended to use Night Mode
+ * Camera Extension to optimize the camera settings to take a high-quality photo in
+ * the dark.</p>
+ * @see CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR
+ */
+ @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR)
+ public static final int EXTENSION_NIGHT_MODE_INDICATOR_ON = 2;
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 8142bbe9b838..496d316eb028 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -21,7 +21,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.OutputConfiguration;
@@ -1407,7 +1406,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* application's selected exposure time, sensor sensitivity,
* and frame duration ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime},
* {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and
- * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If one of the FLASH modes
+ * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is
+ * enabled, the relevant priority CaptureRequest settings will not be overridden.
+ * See {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} for more details. If one of the FLASH modes
* is selected, the camera device's flash unit controls are
* also overridden.</p>
* <p>The FLASH modes are only available if the camera device
@@ -1441,6 +1442,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
* @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES
* @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#FLASH_INFO_AVAILABLE
* @see CaptureRequest#FLASH_MODE
@@ -2668,6 +2670,85 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<Integer>("android.control.autoframing", int.class);
/**
+ * <p>Whether the application uses {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
+ * to control zoom levels.</p>
+ * <p>If set to AUTO, the camera device detects which capture request key the application uses
+ * to do zoom, {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. If
+ * the application doesn't set android.scaler.zoomRatio or sets it to 1.0 in the capture
+ * request, the effective zoom level is reflected in {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} in capture
+ * results. If {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is set to values other than 1.0, the effective
+ * zoom level is reflected in {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. AUTO is the default value
+ * for this control, and also the behavior of the OS before Android version
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA BAKLAVA}.</p>
+ * <p>If set to ZOOM_RATIO, the application explicitly specifies zoom level be controlled
+ * by {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and the effective zoom level is reflected in
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} in capture results. This addresses an ambiguity with AUTO,
+ * with which the camera device cannot know if the application is using cropRegion or
+ * zoomRatio at 1.0x.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #CONTROL_ZOOM_METHOD_AUTO AUTO}</li>
+ * <li>{@link #CONTROL_ZOOM_METHOD_ZOOM_RATIO ZOOM_RATIO}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see #CONTROL_ZOOM_METHOD_AUTO
+ * @see #CONTROL_ZOOM_METHOD_ZOOM_RATIO
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_ZOOM_METHOD)
+ public static final Key<Integer> CONTROL_ZOOM_METHOD =
+ new Key<Integer>("android.control.zoomMethod", int.class);
+
+ /**
+ * <p>Turn on AE priority mode.</p>
+ * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is
+ * AUTO and {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to one of its
+ * ON modes, with the exception of ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+ * <p>When a priority mode is enabled, the camera device's
+ * auto-exposure routine will maintain the application's
+ * selected parameters relevant to the priority mode while overriding
+ * the remaining exposure parameters
+ * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and
+ * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). For example, if
+ * SENSOR_SENSITIVITY_PRIORITY mode is enabled, the camera device will
+ * maintain the application-selected {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}
+ * while adjusting {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}
+ * and {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}. The overridden fields for a
+ * given capture will be available in its CaptureResult.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #CONTROL_AE_PRIORITY_MODE_OFF OFF}</li>
+ * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY SENSOR_SENSITIVITY_PRIORITY}</li>
+ * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY SENSOR_EXPOSURE_TIME_PRIORITY}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ * @see #CONTROL_AE_PRIORITY_MODE_OFF
+ * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY
+ * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_AE_PRIORITY)
+ public static final Key<Integer> CONTROL_AE_PRIORITY_MODE =
+ new Key<Integer>("android.control.aePriorityMode", int.class);
+
+ /**
* <p>Operation mode for edge
* enhancement.</p>
* <p>Edge enhancement improves sharpness and details in the captured image. OFF means
@@ -3489,7 +3570,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* duration exposed to the nearest possible value (rather than expose longer).
* The final exposure time used will be available in the output capture result.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
- * OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * OFF; otherwise the auto-exposure algorithm will override this value. However, in the
+ * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_EXPOSURE_TIME_PRIORITY, this
+ * control will be effective and not controlled by the auto-exposure algorithm.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</p>
@@ -3499,6 +3582,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
@@ -3607,7 +3691,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* value. The final sensitivity used will be available in the
* output capture result.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
- * OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * OFF; otherwise the auto-exposure algorithm will override this value. However, in the
+ * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_SENSITIVITY_PRIORITY, this
+ * control will be effective and not controlled by the auto-exposure algorithm.</p>
* <p>Note that for devices supporting postRawSensitivityBoost, the total sensitivity applied
* to the final processed image is the combination of {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and
* {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}. In case the application uses the sensor
@@ -3623,6 +3709,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ae72ca40fc5a..a52be973f564 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
-import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.utils.TypeReference;
@@ -808,7 +807,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* application's selected exposure time, sensor sensitivity,
* and frame duration ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime},
* {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and
- * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If one of the FLASH modes
+ * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is
+ * enabled, the relevant priority CaptureRequest settings will not be overridden.
+ * See {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} for more details. If one of the FLASH modes
* is selected, the camera device's flash unit controls are
* also overridden.</p>
* <p>The FLASH modes are only available if the camera device
@@ -842,6 +843,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
*
* @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES
* @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#FLASH_INFO_AVAILABLE
* @see CaptureRequest#FLASH_MODE
@@ -2915,6 +2917,85 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.control.lowLightBoostState", int.class);
/**
+ * <p>Whether the application uses {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
+ * to control zoom levels.</p>
+ * <p>If set to AUTO, the camera device detects which capture request key the application uses
+ * to do zoom, {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. If
+ * the application doesn't set android.scaler.zoomRatio or sets it to 1.0 in the capture
+ * request, the effective zoom level is reflected in {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} in capture
+ * results. If {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is set to values other than 1.0, the effective
+ * zoom level is reflected in {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. AUTO is the default value
+ * for this control, and also the behavior of the OS before Android version
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA BAKLAVA}.</p>
+ * <p>If set to ZOOM_RATIO, the application explicitly specifies zoom level be controlled
+ * by {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and the effective zoom level is reflected in
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} in capture results. This addresses an ambiguity with AUTO,
+ * with which the camera device cannot know if the application is using cropRegion or
+ * zoomRatio at 1.0x.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #CONTROL_ZOOM_METHOD_AUTO AUTO}</li>
+ * <li>{@link #CONTROL_ZOOM_METHOD_ZOOM_RATIO ZOOM_RATIO}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see #CONTROL_ZOOM_METHOD_AUTO
+ * @see #CONTROL_ZOOM_METHOD_ZOOM_RATIO
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_ZOOM_METHOD)
+ public static final Key<Integer> CONTROL_ZOOM_METHOD =
+ new Key<Integer>("android.control.zoomMethod", int.class);
+
+ /**
+ * <p>Turn on AE priority mode.</p>
+ * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is
+ * AUTO and {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to one of its
+ * ON modes, with the exception of ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+ * <p>When a priority mode is enabled, the camera device's
+ * auto-exposure routine will maintain the application's
+ * selected parameters relevant to the priority mode while overriding
+ * the remaining exposure parameters
+ * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and
+ * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). For example, if
+ * SENSOR_SENSITIVITY_PRIORITY mode is enabled, the camera device will
+ * maintain the application-selected {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}
+ * while adjusting {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}
+ * and {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}. The overridden fields for a
+ * given capture will be available in its CaptureResult.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #CONTROL_AE_PRIORITY_MODE_OFF OFF}</li>
+ * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY SENSOR_SENSITIVITY_PRIORITY}</li>
+ * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY SENSOR_EXPOSURE_TIME_PRIORITY}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ * @see #CONTROL_AE_PRIORITY_MODE_OFF
+ * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY
+ * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_AE_PRIORITY)
+ public static final Key<Integer> CONTROL_AE_PRIORITY_MODE =
+ new Key<Integer>("android.control.aePriorityMode", int.class);
+
+ /**
* <p>Operation mode for edge
* enhancement.</p>
* <p>Edge enhancement improves sharpness and details in the captured image. OFF means
@@ -4199,7 +4280,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* duration exposed to the nearest possible value (rather than expose longer).
* The final exposure time used will be available in the output capture result.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
- * OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * OFF; otherwise the auto-exposure algorithm will override this value. However, in the
+ * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_EXPOSURE_TIME_PRIORITY, this
+ * control will be effective and not controlled by the auto-exposure algorithm.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</p>
@@ -4209,6 +4292,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
@@ -4317,7 +4401,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* value. The final sensitivity used will be available in the
* output capture result.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
- * OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * OFF; otherwise the auto-exposure algorithm will override this value. However, in the
+ * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_SENSITIVITY_PRIORITY, this
+ * control will be effective and not controlled by the auto-exposure algorithm.</p>
* <p>Note that for devices supporting postRawSensitivityBoost, the total sensitivity applied
* to the final processed image is the combination of {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and
* {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}. In case the application uses the sensor
@@ -4333,6 +4419,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
@@ -6016,6 +6103,38 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
public static final Key<Integer> EXTENSION_STRENGTH =
new Key<Integer>("android.extension.strength", int.class);
+ /**
+ * <p>Indicates when to activate Night Mode Camera Extension for high-quality
+ * still captures in low-light conditions.</p>
+ * <p>Provides awareness to the application when the current scene can benefit from using a
+ * Night Mode Camera Extension to take a high-quality photo.</p>
+ * <p>Support for this capture result can be queried via
+ * {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
+ * <p>If the device supports this capability then it will also support
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_NIGHT NIGHT}
+ * and will be available in both
+ * {@link android.hardware.camera2.CameraCaptureSession sessions} and
+ * {@link android.hardware.camera2.CameraExtensionSession sessions}.</p>
+ * <p>The value will be {@code UNKNOWN} in the following auto exposure modes: ON_AUTO_FLASH,
+ * ON_ALWAYS_FLASH, ON_AUTO_FLASH_REDEYE, or ON_EXTERNAL_FLASH.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN UNKNOWN}</li>
+ * <li>{@link #EXTENSION_NIGHT_MODE_INDICATOR_OFF OFF}</li>
+ * <li>{@link #EXTENSION_NIGHT_MODE_INDICATOR_ON ON}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN
+ * @see #EXTENSION_NIGHT_MODE_INDICATOR_OFF
+ * @see #EXTENSION_NIGHT_MODE_INDICATOR_ON
+ */
+ @PublicKey
+ @NonNull
+ @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR)
+ public static final Key<Integer> EXTENSION_NIGHT_MODE_INDICATOR =
+ new Key<Integer>("android.extension.nightModeIndicator", int.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ef7f3f8ab58b..1cc085658bfa 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -437,7 +437,7 @@ public class CameraMetadataNative implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public synchronized void writeToParcel(Parcel dest, int flags) {
nativeWriteToParcel(dest, mMetadataPtr);
}
@@ -479,7 +479,7 @@ public class CameraMetadataNative implements Parcelable {
return getBase(key);
}
- public void readFromParcel(Parcel in) {
+ public synchronized void readFromParcel(Parcel in) {
nativeReadFromParcel(in, mMetadataPtr);
updateNativeAllocation();
}
@@ -592,28 +592,33 @@ public class CameraMetadataNative implements Parcelable {
}
private <T> T getBase(Key<T> key) {
- int tag;
- if (key.hasTag()) {
- tag = key.getTag();
- } else {
- tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName());
- key.cacheTag(tag);
- }
- byte[] values = readValues(tag);
- if (values == null) {
- // If the key returns null, use the fallback key if exists.
- // This is to support old key names for the newly published keys.
- if (key.mFallbackName == null) {
- return null;
+ int tag, nativeType;
+ byte[] values = null;
+ synchronized (this) {
+ if (key.hasTag()) {
+ tag = key.getTag();
+ } else {
+ tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName());
+ key.cacheTag(tag);
}
- tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName);
values = readValues(tag);
if (values == null) {
- return null;
+ // If the key returns null, use the fallback key if exists.
+ // This is to support old key names for the newly published keys.
+ if (key.mFallbackName == null) {
+ return null;
+ }
+ tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName);
+ values = readValues(tag);
+ if (values == null) {
+ return null;
+ }
}
- }
- int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
+ nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
+ }
+ // This block of code doesn't need to be synchronized since we aren't writing or reading
+ // from the metadata buffer for this instance of CameraMetadataNative.
Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
return marshaler.unmarshal(buffer);
@@ -1385,6 +1390,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
/*highspeedvideoconfigurations*/ null,
/*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
break;
@@ -1402,6 +1410,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
highSpeedVideoConfigurations,
/*inputoutputformatsmap*/ null, listHighResolution, supportsPrivate[i]);
break;
@@ -1419,6 +1430,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRcconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
/*highSpeedVideoConfigurations*/ null,
inputOutputFormatsMap, listHighResolution, supportsPrivate[i]);
break;
@@ -1436,6 +1450,9 @@ public class CameraMetadataNative implements Parcelable {
/*jpegRconfiguration*/ null,
/*jpegRminduration*/ null,
/*jpegRstallduration*/ null,
+ /*heicUltraHDRcconfiguration*/ null,
+ /*heicUltraHDRminduration*/ null,
+ /*heicUltraHDRstallduration*/ null,
/*highSpeedVideoConfigurations*/ null,
/*inputOutputFormatsMap*/ null, listHighResolution, supportsPrivate[i]);
}
@@ -1607,6 +1624,17 @@ public class CameraMetadataNative implements Parcelable {
CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS);
StreamConfigurationDuration[] heicStallDurations = getBase(
CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS);
+ StreamConfiguration[] heicUltraHDRConfigurations = null;
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations = null;
+ StreamConfigurationDuration[] heicUltraHDRStallDurations = null;
+ if (Flags.cameraHeifGainmap()) {
+ heicUltraHDRConfigurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS);
+ heicUltraHDRMinFrameDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS);
+ heicUltraHDRStallDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS);
+ }
StreamConfiguration[] jpegRConfigurations = getBase(
CameraCharacteristics.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS);
StreamConfigurationDuration[] jpegRMinFrameDurations = getBase(
@@ -1625,7 +1653,8 @@ public class CameraMetadataNative implements Parcelable {
dynamicDepthStallDurations, heicConfigurations,
heicMinFrameDurations, heicStallDurations,
jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap,
+ heicUltraHDRConfigurations, heicUltraHDRMinFrameDurations,
+ heicUltraHDRStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
listHighResolution);
}
@@ -1662,6 +1691,17 @@ public class CameraMetadataNative implements Parcelable {
CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
StreamConfigurationDuration[] heicStallDurations = getBase(
CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfiguration[] heicUltraHDRConfigurations = null;
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations = null;
+ StreamConfigurationDuration[] heicUltraHDRStallDurations = null;
+ if (Flags.cameraHeifGainmap()) {
+ heicUltraHDRConfigurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ heicUltraHDRMinFrameDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+ heicUltraHDRStallDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_ULTRA_HDR_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ }
StreamConfiguration[] jpegRConfigurations = getBase(
CameraCharacteristics.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
StreamConfigurationDuration[] jpegRMinFrameDurations = getBase(
@@ -1681,7 +1721,8 @@ public class CameraMetadataNative implements Parcelable {
dynamicDepthStallDurations, heicConfigurations,
heicMinFrameDurations, heicStallDurations,
jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap,
+ heicUltraHDRConfigurations, heicUltraHDRMinFrameDurations,
+ heicUltraHDRStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
listHighResolution, false);
}
@@ -1909,8 +1950,12 @@ public class CameraMetadataNative implements Parcelable {
setBase(key.getNativeKey(), value);
}
- private <T> void setBase(Key<T> key, T value) {
- int tag;
+ // The whole method needs to be synchronized since we're making
+ // multiple calls to the native layer. From one call to the other (within setBase)
+ // we expect the metadata's properties such as vendor id etc to
+ // stay the same and as a result the whole method should be synchronized for safety.
+ private synchronized <T> void setBase(Key<T> key, T value) {
+ int tag, nativeType;
if (key.hasTag()) {
tag = key.getTag();
} else {
@@ -1923,7 +1968,7 @@ public class CameraMetadataNative implements Parcelable {
return;
} // else update the entry to a new value
- int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
+ nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
int size = marshaler.calculateMarshalSize(value);
@@ -2126,7 +2171,7 @@ public class CameraMetadataNative implements Parcelable {
return true;
}
- private void updateNativeAllocation() {
+ private synchronized void updateNativeAllocation() {
long currentBufferSize = nativeGetBufferSize(mMetadataPtr);
if (currentBufferSize != mBufferSize) {
@@ -2209,6 +2254,11 @@ public class CameraMetadataNative implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
+ // FastNative doesn't work with synchronized methods and we can do synchronization
+ // wherever needed in the java layer (caller). At some places in java such as
+ // setBase() / getBase(), we do need to synchronize the whole method, so leaving
+ // synchronized out for these native methods.
+
@FastNative
private static native long nativeAllocate();
@FastNative
@@ -2218,28 +2268,41 @@ public class CameraMetadataNative implements Parcelable {
@FastNative
private static native void nativeUpdate(long dst, long src);
- private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
- private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
- private static synchronized native void nativeSwap(long ptr, long otherPtr)
+ @FastNative
+ private static native void nativeWriteToParcel(Parcel dest, long ptr);
+ @FastNative
+ private static native void nativeReadFromParcel(Parcel source, long ptr);
+ @FastNative
+ private static native void nativeSwap(long ptr, long otherPtr)
throws NullPointerException;
@FastNative
private static native void nativeSetVendorId(long ptr, long vendorId);
- private static synchronized native void nativeClose(long ptr);
- private static synchronized native boolean nativeIsEmpty(long ptr);
- private static synchronized native int nativeGetEntryCount(long ptr);
- private static synchronized native long nativeGetBufferSize(long ptr);
+ @FastNative
+ private static native void nativeClose(long ptr);
+ @FastNative
+ private static native boolean nativeIsEmpty(long ptr);
+ @FastNative
+ private static native int nativeGetEntryCount(long ptr);
+ @FastNative
+ private static native long nativeGetBufferSize(long ptr);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static synchronized native byte[] nativeReadValues(int tag, long ptr);
- private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr);
- private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD
+ @FastNative
+ private static native byte[] nativeReadValues(int tag, long ptr);
+ @FastNative
+ private static native void nativeWriteValues(int tag, byte[] src, long ptr);
+ @FastNative
+ private static native void nativeDump(long ptr) throws IOException; // dump to LOGD
- private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
+ @FastNative
+ private static native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName)
+ @FastNative
+ private static native int nativeGetTagFromKeyLocal(long ptr, String keyName)
throws IllegalArgumentException;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag)
+ @FastNative
+ private static native int nativeGetTypeFromTagLocal(long ptr, int tag)
throws IllegalArgumentException;
@FastNative
private static native int nativeGetTagFromKey(String keyName, long vendorId)
@@ -2257,7 +2320,7 @@ public class CameraMetadataNative implements Parcelable {
* @throws NullPointerException if other was null
* @hide
*/
- public void swap(CameraMetadataNative other) {
+ public synchronized void swap(CameraMetadataNative other) {
nativeSwap(mMetadataPtr, other.mMetadataPtr);
mCameraId = other.mCameraId;
mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
@@ -2272,14 +2335,14 @@ public class CameraMetadataNative implements Parcelable {
*
* @hide
*/
- public void setVendorId(long vendorId) {
+ public synchronized void setVendorId(long vendorId) {
nativeSetVendorId(mMetadataPtr, vendorId);
}
/**
* @hide
*/
- public int getEntryCount() {
+ public synchronized int getEntryCount() {
return nativeGetEntryCount(mMetadataPtr);
}
@@ -2288,7 +2351,7 @@ public class CameraMetadataNative implements Parcelable {
*
* @hide
*/
- public boolean isEmpty() {
+ public synchronized boolean isEmpty() {
return nativeIsEmpty(mMetadataPtr);
}
@@ -2307,7 +2370,7 @@ public class CameraMetadataNative implements Parcelable {
*
* @hide
*/
- public <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
+ public synchronized <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
if (keyClass == null) {
throw new NullPointerException();
}
@@ -2362,7 +2425,7 @@ public class CameraMetadataNative implements Parcelable {
*
* @hide
*/
- public void writeValues(int tag, byte[] src) {
+ public synchronized void writeValues(int tag, byte[] src) {
nativeWriteValues(tag, src, mMetadataPtr);
}
@@ -2377,7 +2440,7 @@ public class CameraMetadataNative implements Parcelable {
* @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
* @hide
*/
- public byte[] readValues(int tag) {
+ public synchronized byte[] readValues(int tag) {
// TODO: Optimization. Native code returns a ByteBuffer instead.
return nativeReadValues(tag, mMetadataPtr);
}
@@ -2390,7 +2453,7 @@ public class CameraMetadataNative implements Parcelable {
*
* @hide
*/
- public void dumpToLog() {
+ public synchronized void dumpToLog() {
try {
nativeDump(mMetadataPtr);
} catch (IOException e) {
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index e3dbb2bbbf90..ec028bf6dcbb 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArrayElementsNotNull;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
+import android.hardware.DataSpace;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
@@ -31,6 +32,8 @@ import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
@@ -100,6 +103,12 @@ public final class StreamConfigurationMap {
* {@link StreamConfigurationDuration}
* @param jpegRStallDurations a non-{@code null} array of Jpeg/R
* {@link StreamConfigurationDuration}
+ * @param heicUltraHDRConfigurations a non-{@code null} array of Heic UltraHDR
+ * {@link StreamConfiguration}
+ * @param heicUltraHDRMinFrameDurations a non-{@code null} array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}
+ * @param heicUltraHDRStallDurations a non-{@code null} array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}
* @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if
* camera device does not support high speed video recording
* @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE
@@ -125,6 +134,9 @@ public final class StreamConfigurationMap {
StreamConfiguration[] jpegRConfigurations,
StreamConfigurationDuration[] jpegRMinFrameDurations,
StreamConfigurationDuration[] jpegRStallDurations,
+ StreamConfiguration[] heicUltraHDRConfigurations,
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations,
+ StreamConfigurationDuration[] heicUltraHDRStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution) {
@@ -134,8 +146,9 @@ public final class StreamConfigurationMap {
dynamicDepthStallDurations,
heicConfigurations, heicMinFrameDurations, heicStallDurations,
jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations,
- highSpeedVideoConfigurations, inputOutputFormatsMap, listHighResolution,
- /*enforceImplementationDefined*/ true);
+ heicUltraHDRConfigurations, heicUltraHDRMinFrameDurations,
+ heicUltraHDRStallDurations, highSpeedVideoConfigurations, inputOutputFormatsMap,
+ listHighResolution, /*enforceImplementationDefined*/ true);
}
/**
@@ -168,6 +181,12 @@ public final class StreamConfigurationMap {
* {@link StreamConfigurationDuration}
* @param jpegRStallDurations a non-{@code null} array of Jpeg/R
* {@link StreamConfigurationDuration}
+ * @param heicUltraHDRConfigurations an array of Heic UltraHDR
+ * {@link StreamConfiguration}, {@code null} if camera doesn't support the format
+ * @param heicUltraHDRMinFrameDurations an array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}, {@code null} if camera doesn't support the format
+ * @param heicUltraHDRStallDurations an array of Heic UltraHDR
+ * {@link StreamConfigurationDuration}, {@code null} if camera doesn't support the format
* @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if
* camera device does not support high speed video recording
* @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE
@@ -195,6 +214,9 @@ public final class StreamConfigurationMap {
StreamConfiguration[] jpegRConfigurations,
StreamConfigurationDuration[] jpegRMinFrameDurations,
StreamConfigurationDuration[] jpegRStallDurations,
+ StreamConfiguration[] heicUltraHDRConfigurations,
+ StreamConfigurationDuration[] heicUltraHDRMinFrameDurations,
+ StreamConfigurationDuration[] heicUltraHDRStallDurations,
HighSpeedVideoConfiguration[] highSpeedVideoConfigurations,
ReprocessFormatsMap inputOutputFormatsMap,
boolean listHighResolution,
@@ -259,6 +281,18 @@ public final class StreamConfigurationMap {
"heicStallDurations");
}
+ if (heicUltraHDRConfigurations == null || (!Flags.cameraHeifGainmap())) {
+ mHeicUltraHDRConfigurations = new StreamConfiguration[0];
+ mHeicUltraHDRMinFrameDurations = new StreamConfigurationDuration[0];
+ mHeicUltraHDRStallDurations = new StreamConfigurationDuration[0];
+ } else {
+ mHeicUltraHDRConfigurations = checkArrayElementsNotNull(heicUltraHDRConfigurations,
+ "heicUltraHDRConfigurations");
+ mHeicUltraHDRMinFrameDurations = checkArrayElementsNotNull(
+ heicUltraHDRMinFrameDurations, "heicUltraHDRMinFrameDurations");
+ mHeicUltraHDRStallDurations = checkArrayElementsNotNull(heicUltraHDRStallDurations,
+ "heicUltraHDRStallDurations");
+ }
if (jpegRConfigurations == null) {
mJpegRConfigurations = new StreamConfiguration[0];
@@ -336,6 +370,19 @@ public final class StreamConfigurationMap {
mHeicOutputFormats.get(config.getFormat()) + 1);
}
+ if (Flags.cameraHeifGainmap()) {
+ // For each Heic UlrtaHDR format, track how many sizes there are available to configure
+ for (StreamConfiguration config : mHeicUltraHDRConfigurations) {
+ if (!config.isOutput()) {
+ // Ignoring input Heic UltraHDR configs
+ continue;
+ }
+
+ mHeicUltraHDROutputFormats.put(config.getFormat(),
+ mHeicUltraHDROutputFormats.get(config.getFormat()) + 1);
+ }
+ }
+
// For each Jpeg/R format, track how many sizes there are available to configure
for (StreamConfiguration config : mJpegRConfigurations) {
if (!config.isOutput()) {
@@ -483,6 +530,11 @@ public final class StreamConfigurationMap {
int internalFormat = imageFormatToInternal(format);
int dataspace = imageFormatToDataspace(format);
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ return mHeicUltraHDROutputFormats.indexOfKey(internalFormat) >= 0;
+ }
+ }
if (dataspace == HAL_DATASPACE_DEPTH) {
return mDepthOutputFormats.indexOfKey(internalFormat) >= 0;
} else if (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) {
@@ -607,6 +659,11 @@ public final class StreamConfigurationMap {
surfaceDataspace == HAL_DATASPACE_HEIF ? mHeicConfigurations :
surfaceDataspace == HAL_DATASPACE_JPEG_R ? mJpegRConfigurations :
mConfigurations;
+ if (Flags.cameraHeifGainmap()) {
+ if (surfaceDataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ configs = mHeicUltraHDRConfigurations;
+ }
+ }
for (StreamConfiguration config : configs) {
if (config.getFormat() == surfaceFormat && config.isOutput()) {
// Matching format, either need exact size match, or a flexible consumer
@@ -646,6 +703,11 @@ public final class StreamConfigurationMap {
dataspace == HAL_DATASPACE_HEIF ? mHeicConfigurations :
dataspace == HAL_DATASPACE_JPEG_R ? mJpegRConfigurations :
mConfigurations;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR ) {
+ configs = mHeicUltraHDRConfigurations;
+ }
+ }
for (StreamConfiguration config : configs) {
if ((config.getFormat() == internalFormat) && config.isOutput() &&
config.getSize().equals(size)) {
@@ -1176,6 +1238,10 @@ public final class StreamConfigurationMap {
Arrays.equals(mHeicConfigurations, other.mHeicConfigurations) &&
Arrays.equals(mHeicMinFrameDurations, other.mHeicMinFrameDurations) &&
Arrays.equals(mHeicStallDurations, other.mHeicStallDurations) &&
+ Arrays.equals(mHeicUltraHDRConfigurations, other.mHeicUltraHDRConfigurations) &&
+ Arrays.equals(mHeicUltraHDRMinFrameDurations,
+ other.mHeicUltraHDRMinFrameDurations) &&
+ Arrays.equals(mHeicUltraHDRStallDurations, other.mHeicUltraHDRStallDurations) &&
Arrays.equals(mJpegRConfigurations, other.mJpegRConfigurations) &&
Arrays.equals(mJpegRMinFrameDurations, other.mJpegRMinFrameDurations) &&
Arrays.equals(mJpegRStallDurations, other.mJpegRStallDurations) &&
@@ -1197,8 +1263,9 @@ public final class StreamConfigurationMap {
mDynamicDepthConfigurations, mDynamicDepthMinFrameDurations,
mDynamicDepthStallDurations, mHeicConfigurations,
mHeicMinFrameDurations, mHeicStallDurations,
- mJpegRConfigurations, mJpegRMinFrameDurations, mJpegRStallDurations,
- mHighSpeedVideoConfigurations);
+ mHeicUltraHDRConfigurations, mHeicUltraHDRMinFrameDurations,
+ mHeicUltraHDRStallDurations, mJpegRConfigurations, mJpegRMinFrameDurations,
+ mJpegRStallDurations, mHighSpeedVideoConfigurations);
}
// Check that the argument is supported by #getOutputFormats or #getInputFormats
@@ -1209,6 +1276,13 @@ public final class StreamConfigurationMap {
int internalDataspace = imageFormatToDataspace(format);
if (output) {
+ if (Flags.cameraHeifGainmap()) {
+ if (internalDataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ if (mHeicUltraHDROutputFormats.indexOfKey(internalFormat) >= 0) {
+ return format;
+ }
+ }
+ }
if (internalDataspace == HAL_DATASPACE_DEPTH) {
if (mDepthOutputFormats.indexOfKey(internalFormat) >= 0) {
return format;
@@ -1429,6 +1503,7 @@ public final class StreamConfigurationMap {
* <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.DEPTH_JPEG => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.HEIC => HAL_PIXEL_FORMAT_BLOB
+ * <li>ImageFormat.HEIC_ULTRAHDR => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.JPEG_R => HAL_PIXEL_FORMAT_BLOB
* <li>ImageFormat.DEPTH16 => HAL_PIXEL_FORMAT_Y16
* </ul>
@@ -1451,6 +1526,11 @@ public final class StreamConfigurationMap {
* if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
*/
static int imageFormatToInternal(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return HAL_PIXEL_FORMAT_BLOB;
+ }
+ }
switch (format) {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
@@ -1480,6 +1560,7 @@ public final class StreamConfigurationMap {
* <li>ImageFormat.DEPTH16 => HAL_DATASPACE_DEPTH
* <li>ImageFormat.DEPTH_JPEG => HAL_DATASPACE_DYNAMIC_DEPTH
* <li>ImageFormat.HEIC => HAL_DATASPACE_HEIF
+ * <li>ImageFormat.HEIC_ULTRAHDR => DATASPACE_HEIF_ULTRAHDR
* <li>ImageFormat.JPEG_R => HAL_DATASPACE_JPEG_R
* <li>ImageFormat.YUV_420_888 => HAL_DATASPACE_JFIF
* <li>ImageFormat.RAW_SENSOR => HAL_DATASPACE_ARBITRARY
@@ -1508,6 +1589,11 @@ public final class StreamConfigurationMap {
* if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
*/
static int imageFormatToDataspace(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return DataSpace.DATASPACE_HEIF_ULTRAHDR;
+ }
+ }
switch (format) {
case ImageFormat.JPEG:
return HAL_DATASPACE_V0_JFIF;
@@ -1584,13 +1670,21 @@ public final class StreamConfigurationMap {
dataspace == HAL_DATASPACE_JPEG_R ? mJpegROutputFormats :
highRes ? mHighResOutputFormats :
mOutputFormats;
-
+ boolean isDataSpaceHeifUltraHDR = false;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ formatsMap = mHeicUltraHDROutputFormats;
+ isDataSpaceHeifUltraHDR = true;
+ }
+ }
int sizesCount = formatsMap.get(format);
if ( ((!output || (dataspace == HAL_DATASPACE_DEPTH || dataspace == HAL_DATASPACE_JPEG_R ||
dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ||
- dataspace == HAL_DATASPACE_HEIF)) && sizesCount == 0) ||
+ dataspace == HAL_DATASPACE_HEIF ||
+ isDataSpaceHeifUltraHDR)) && sizesCount == 0) ||
(output && (dataspace != HAL_DATASPACE_DEPTH && dataspace != HAL_DATASPACE_JPEG_R &&
dataspace != HAL_DATASPACE_DYNAMIC_DEPTH &&
+ !isDataSpaceHeifUltraHDR &&
dataspace != HAL_DATASPACE_HEIF) &&
mAllOutputFormats.get(format) == 0)) {
return null;
@@ -1604,12 +1698,14 @@ public final class StreamConfigurationMap {
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicConfigurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRConfigurations :
+ (isDataSpaceHeifUltraHDR) ? mHeicUltraHDRConfigurations :
mConfigurations;
StreamConfigurationDuration[] minFrameDurations =
(dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicMinFrameDurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRMinFrameDurations :
+ (isDataSpaceHeifUltraHDR) ? mHeicUltraHDRMinFrameDurations :
mMinFrameDurations;
for (StreamConfiguration config : configurations) {
@@ -1639,7 +1735,8 @@ public final class StreamConfigurationMap {
// Dynamic depth streams can have both fast and also high res modes.
if ((sizeIndex != sizesCount) && (dataspace == HAL_DATASPACE_DYNAMIC_DEPTH ||
- dataspace == HAL_DATASPACE_HEIF) || (dataspace == HAL_DATASPACE_JPEG_R)) {
+ dataspace == HAL_DATASPACE_HEIF) || (dataspace == HAL_DATASPACE_JPEG_R) ||
+ isDataSpaceHeifUltraHDR) {
if (sizeIndex > sizesCount) {
throw new AssertionError(
@@ -1682,6 +1779,11 @@ public final class StreamConfigurationMap {
if (mHeicOutputFormats.size() > 0) {
formats[i++] = ImageFormat.HEIC;
}
+ if (Flags.cameraHeifGainmap()) {
+ if (mHeicUltraHDROutputFormats.size() > 0) {
+ formats[i++] = ImageFormat.HEIC_ULTRAHDR;
+ }
+ }
if (mJpegROutputFormats.size() > 0) {
formats[i++] = ImageFormat.JPEG_R;
}
@@ -1725,12 +1827,19 @@ public final class StreamConfigurationMap {
* @see #DURATION_STALL
* */
private StreamConfigurationDuration[] getDurations(int duration, int dataspace) {
+ boolean isDataSpaceHeifUltraHDR = false;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ isDataSpaceHeifUltraHDR = true;
+ }
+ }
switch (duration) {
case DURATION_MIN_FRAME:
return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ?
mDynamicDepthMinFrameDurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicMinFrameDurations :
+ isDataSpaceHeifUltraHDR ? mHeicUltraHDRMinFrameDurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRMinFrameDurations :
mMinFrameDurations;
@@ -1738,6 +1847,7 @@ public final class StreamConfigurationMap {
return (dataspace == HAL_DATASPACE_DEPTH) ? mDepthStallDurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthStallDurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicStallDurations :
+ isDataSpaceHeifUltraHDR ? mHeicUltraHDRStallDurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRStallDurations :
mStallDurations;
default:
@@ -1754,6 +1864,7 @@ public final class StreamConfigurationMap {
size += mDynamicDepthOutputFormats.size();
size += mHeicOutputFormats.size();
size += mJpegROutputFormats.size();
+ size += mHeicUltraHDROutputFormats.size();
}
return size;
@@ -1774,11 +1885,18 @@ public final class StreamConfigurationMap {
}
private boolean isSupportedInternalConfiguration(int format, int dataspace, Size size) {
+ boolean isDataSpaceHeifUltraHDR = false;
+ if (Flags.cameraHeifGainmap()) {
+ if (dataspace == DataSpace.DATASPACE_HEIF_ULTRAHDR) {
+ isDataSpaceHeifUltraHDR = true;
+ }
+ }
StreamConfiguration[] configurations =
(dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations :
(dataspace == HAL_DATASPACE_DYNAMIC_DEPTH) ? mDynamicDepthConfigurations :
(dataspace == HAL_DATASPACE_HEIF) ? mHeicConfigurations :
(dataspace == HAL_DATASPACE_JPEG_R) ? mJpegRConfigurations :
+ isDataSpaceHeifUltraHDR ? mHeicUltraHDRConfigurations :
mConfigurations;
for (int i = 0; i < configurations.length; i++) {
@@ -1954,6 +2072,11 @@ public final class StreamConfigurationMap {
* @hide
*/
public static String formatToString(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return "HEIC_ULTRAHDR";
+ }
+ }
switch (format) {
case ImageFormat.YV12:
return "YV12";
@@ -2078,6 +2201,10 @@ public final class StreamConfigurationMap {
private final StreamConfigurationDuration[] mHeicMinFrameDurations;
private final StreamConfigurationDuration[] mHeicStallDurations;
+ private final StreamConfiguration[] mHeicUltraHDRConfigurations;
+ private final StreamConfigurationDuration[] mHeicUltraHDRMinFrameDurations;
+ private final StreamConfigurationDuration[] mHeicUltraHDRStallDurations;
+
private final StreamConfiguration[] mJpegRConfigurations;
private final StreamConfigurationDuration[] mJpegRMinFrameDurations;
private final StreamConfigurationDuration[] mJpegRStallDurations;
@@ -2103,6 +2230,8 @@ public final class StreamConfigurationMap {
private final SparseIntArray mDynamicDepthOutputFormats = new SparseIntArray();
/** internal format -> num heic output sizes mapping, for HAL_DATASPACE_HEIF */
private final SparseIntArray mHeicOutputFormats = new SparseIntArray();
+ /** internal format -> num heic output sizes mapping, for DATASPACE_HEIF_GAINMAP */
+ private final SparseIntArray mHeicUltraHDROutputFormats = new SparseIntArray();
/** internal format -> num Jpeg/R output sizes mapping, for HAL_DATASPACE_JPEG_R */
private final SparseIntArray mJpegROutputFormats = new SparseIntArray();
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index e583627c0960..8b4d0da147bc 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -172,6 +172,23 @@ public final class DeviceState {
*/
public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17;
+ /**
+ * Property that indicates that this state corresponds to the device state for rear display
+ * mode, where both the inner and outer displays are on. In this state, the outer display
+ * is the default display where the app is shown, and the inner display is used by the system to
+ * show a UI affordance for exiting the mode.
+ *
+ * Note that this value should generally not be used, and may be removed in the future (e.g.
+ * if or when it becomes the only type of rear display mode when
+ * {@link android.hardware.devicestate.feature.flags.Flags#deviceStateRdmV2} is removed).
+ *
+ * As such, clients should strongly consider relying on {@link #PROPERTY_FEATURE_REAR_DISPLAY}
+ * instead.
+ *
+ * @hide
+ */
+ public static final int PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT = 1001;
+
/** @hide */
@IntDef(prefix = {"PROPERTY_"}, flag = false, value = {
PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
@@ -190,7 +207,8 @@ public final class DeviceState {
PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY,
PROPERTY_FEATURE_REAR_DISPLAY,
- PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+ PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT
})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 98ba9192044d..6230f4dbf6f4 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -29,4 +29,13 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "device_state_rdm_v2"
+ is_exported: true
+ namespace: "windowing_sdk"
+ description: "Enables Rear Display Mode V2, where the inner display shows the user a UI affordance for exiting the state"
+ bug: "372486634"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 6a96a54d93ba..529ee91cbfdf 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -113,16 +113,24 @@ public final class BrightnessInfo implements Parcelable {
*/
public final int brightnessMaxReason;
+ /**
+ * Whether the current brightness value is overridden by the application window via
+ * {@link android.view.WindowManager.LayoutParams#screenBrightness}.
+ */
+ public final boolean isBrightnessOverrideByWindow;
+
public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
@HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint,
@BrightnessMaxReason int brightnessMaxReason) {
this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode,
- highBrightnessTransitionPoint, brightnessMaxReason);
+ highBrightnessTransitionPoint, brightnessMaxReason,
+ false /* isBrightnessOverrideByWindow */);
}
public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum,
float brightnessMaximum, @HighBrightnessMode int highBrightnessMode,
- float highBrightnessTransitionPoint, @BrightnessMaxReason int brightnessMaxReason) {
+ float highBrightnessTransitionPoint, @BrightnessMaxReason int brightnessMaxReason,
+ boolean isBrightnessOverrideByWindow) {
this.brightness = brightness;
this.adjustedBrightness = adjustedBrightness;
this.brightnessMinimum = brightnessMinimum;
@@ -130,6 +138,7 @@ public final class BrightnessInfo implements Parcelable {
this.highBrightnessMode = highBrightnessMode;
this.highBrightnessTransitionPoint = highBrightnessTransitionPoint;
this.brightnessMaxReason = brightnessMaxReason;
+ this.isBrightnessOverrideByWindow = isBrightnessOverrideByWindow;
}
/**
@@ -178,6 +187,7 @@ public final class BrightnessInfo implements Parcelable {
dest.writeInt(highBrightnessMode);
dest.writeFloat(highBrightnessTransitionPoint);
dest.writeInt(brightnessMaxReason);
+ dest.writeBoolean(isBrightnessOverrideByWindow);
}
public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR =
@@ -201,6 +211,7 @@ public final class BrightnessInfo implements Parcelable {
highBrightnessMode = source.readInt();
highBrightnessTransitionPoint = source.readFloat();
brightnessMaxReason = source.readInt();
+ isBrightnessOverrideByWindow = source.readBoolean();
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a81bcbcce9c9..28da644dd837 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.HdrCapabilities.HdrType;
import static android.view.Display.INVALID_DISPLAY;
@@ -575,14 +576,22 @@ public final class DisplayManager {
EVENT_FLAG_DISPLAY_ADDED,
EVENT_FLAG_DISPLAY_CHANGED,
EVENT_FLAG_DISPLAY_REMOVED,
- EVENT_FLAG_DISPLAY_BRIGHTNESS,
- EVENT_FLAG_HDR_SDR_RATIO_CHANGED,
- EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventFlag {}
/**
+ * @hide
+ */
+ @LongDef(flag = true, prefix = {"PRIVATE_EVENT_FLAG_"}, value = {
+ PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS,
+ PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED,
+ PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PrivateEventFlag {}
+
+ /**
* Event type for when a new display is added.
*
* @see #registerDisplayListener(DisplayListener, Handler, long)
@@ -618,7 +627,7 @@ public final class DisplayManager {
*
* @hide
*/
- public static final long EVENT_FLAG_DISPLAY_BRIGHTNESS = 1L << 3;
+ public static final long PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS = 1L << 0;
/**
* Event flag to register for a display's hdr/sdr ratio changes. This notification is sent
@@ -631,14 +640,16 @@ public final class DisplayManager {
*
* @hide
*/
- public static final long EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 4;
+ public static final long PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 1;
/**
* Event flag to register for a display's connection changed.
*
+ * @see #registerDisplayListener(DisplayListener, Handler, long)
* @hide
*/
- public static final long EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 5;
+ public static final long PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 2;
+
/** @hide */
public DisplayManager(Context context) {
@@ -774,20 +785,49 @@ public final class DisplayManager {
* @param listener The listener to register.
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
- * @param eventFlagsMask A bitmask of the event types for which this listener is subscribed.
+ * @param eventFlags A bitmask of the event types for which this listener is subscribed.
+ *
+ * @see #EVENT_FLAG_DISPLAY_ADDED
+ * @see #EVENT_FLAG_DISPLAY_CHANGED
+ * @see #EVENT_FLAG_DISPLAY_REMOVED
+ * @see #registerDisplayListener(DisplayListener, Handler)
+ * @see #unregisterDisplayListener
+ *
+ * @hide
+ */
+ public void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @EventFlag long eventFlags) {
+ mGlobal.registerDisplayListener(listener, handler,
+ mGlobal.mapFlagsToInternalEventFlag(eventFlags, 0),
+ ActivityThread.currentPackageName());
+ }
+
+ /**
+ * Registers a display listener to receive notifications about given display event types.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @param eventFlags A bitmask of the event types for which this listener is subscribed.
+ * @param privateEventFlags A bitmask of the private event types for which this listener
+ * is subscribed.
*
* @see #EVENT_FLAG_DISPLAY_ADDED
* @see #EVENT_FLAG_DISPLAY_CHANGED
* @see #EVENT_FLAG_DISPLAY_REMOVED
- * @see #EVENT_FLAG_DISPLAY_BRIGHTNESS
+ * @see #PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS
+ * @see #PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED
+ * @see #PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED
* @see #registerDisplayListener(DisplayListener, Handler)
* @see #unregisterDisplayListener
*
* @hide
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @Nullable Handler handler, @EventFlag long eventFlagsMask) {
- mGlobal.registerDisplayListener(listener, handler, eventFlagsMask,
+ @Nullable Handler handler, @EventFlag long eventFlags,
+ @PrivateEventFlag long privateEventFlags) {
+ mGlobal.registerDisplayListener(listener, handler,
+ mGlobal.mapFlagsToInternalEventFlag(eventFlags, privateEventFlags),
ActivityThread.currentPackageName());
}
@@ -1725,6 +1765,29 @@ public final class DisplayManager {
}
/**
+ * @return The current display topology that represents the relative positions of extended
+ * displays.
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_DISPLAYS)
+ @Nullable
+ public DisplayTopology getDisplayTopology() {
+ return mGlobal.getDisplayTopology();
+ }
+
+ /**
+ * Set the relative positions between extended displays (display topology).
+ * @param topology The display topology to be set
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_DISPLAYS)
+ public void setDisplayTopology(DisplayTopology topology) {
+ mGlobal.setDisplayTopology(topology);
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3c6841cd8aeb..03b44f63e3b7 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,12 +18,14 @@ package android.hardware.display;
import static android.hardware.display.DisplayManager.EventFlag;
+import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.view.Display.HdrCapabilities.HdrType;
import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -118,6 +120,24 @@ public final class DisplayManagerGlobal {
public static final int EVENT_DISPLAY_CONNECTED = 6;
public static final int EVENT_DISPLAY_DISCONNECTED = 7;
+ @LongDef(prefix = {"INTERNAL_EVENT_DISPLAY"}, flag = true, value = {
+ INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
+ INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
+ INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
+ INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
+ INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InternalEventFlag {}
+
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_ADDED = 1L << 0;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_CHANGED = 1L << 1;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_REMOVED = 1L << 2;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED = 1L << 3;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED = 1L << 4;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 5;
+
@UnsupportedAppUsage
private static DisplayManagerGlobal sInstance;
@@ -130,7 +150,7 @@ public final class DisplayManagerGlobal {
private final IDisplayManager mDm;
private DisplayManagerCallback mCallback;
- private @EventFlag long mRegisteredEventFlagsMask = 0;
+ private @InternalEventFlag long mRegisteredInternalEventFlag = 0;
private final CopyOnWriteArrayList<DisplayListenerDelegate> mDisplayListeners =
new CopyOnWriteArrayList<>();
@@ -346,11 +366,11 @@ public final class DisplayManagerGlobal {
* @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @Nullable Handler handler, @EventFlag long eventFlagsMask,
+ @Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
String packageName) {
Looper looper = getLooperForHandler(handler);
Handler springBoard = new Handler(looper);
- registerDisplayListener(listener, new HandlerExecutor(springBoard), eventFlagsMask,
+ registerDisplayListener(listener, new HandlerExecutor(springBoard), internalEventFlagsMask,
packageName);
}
@@ -359,32 +379,34 @@ public final class DisplayManagerGlobal {
*
* @param listener The listener that will be called when display changes occur.
* @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
- * @param eventFlagsMask Flag of events to be listened to.
+ * @param internalEventFlagsMask Mask of events to be listened to.
* @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @NonNull Executor executor, @EventFlag long eventFlagsMask, String packageName) {
+ @NonNull Executor executor, @InternalEventFlag long internalEventFlagsMask,
+ String packageName) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
- if (eventFlagsMask == 0) {
+ if (internalEventFlagsMask == 0) {
throw new IllegalArgumentException("The set of events to listen to must not be empty.");
}
if (extraLogging()) {
Slog.i(TAG, "Registering Display Listener: "
- + Long.toBinaryString(eventFlagsMask) + ", packageName: " + packageName);
+ + Long.toBinaryString(internalEventFlagsMask)
+ + ", packageName: " + packageName);
}
synchronized (mLock) {
int index = findDisplayListenerLocked(listener);
if (index < 0) {
mDisplayListeners.add(new DisplayListenerDelegate(listener, executor,
- eventFlagsMask, packageName));
+ internalEventFlagsMask, packageName));
registerCallbackIfNeededLocked();
} else {
- mDisplayListeners.get(index).setEventFlagsMask(eventFlagsMask);
+ mDisplayListeners.get(index).setEventsMask(internalEventFlagsMask);
}
updateCallbackIfNeededLocked();
maybeLogAllDisplayListeners();
@@ -456,17 +478,17 @@ public final class DisplayManagerGlobal {
return -1;
}
- @EventFlag
- private int calculateEventFlagsMaskLocked() {
- int mask = 0;
+ @InternalEventFlag
+ private long calculateEventsMaskLocked() {
+ long mask = 0;
final int numListeners = mDisplayListeners.size();
for (int i = 0; i < numListeners; i++) {
- mask |= mDisplayListeners.get(i).mEventFlagsMask;
+ mask |= mDisplayListeners.get(i).mInternalEventFlagsMask;
}
if (mDispatchNativeCallbacks) {
- mask |= DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ mask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
}
return mask;
}
@@ -479,14 +501,14 @@ public final class DisplayManagerGlobal {
}
private void updateCallbackIfNeededLocked() {
- int mask = calculateEventFlagsMaskLocked();
+ long mask = calculateEventsMaskLocked();
if (DEBUG) {
- Log.d(TAG, "Flag for listener: " + mask);
+ Log.d(TAG, "Mask for listener: " + mask);
}
- if (mask != mRegisteredEventFlagsMask) {
+ if (mask != mRegisteredInternalEventFlag) {
try {
mDm.registerCallbackWithEventMask(mCallback, mask);
- mRegisteredEventFlagsMask = mask;
+ mRegisteredInternalEventFlag = mask;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -1264,12 +1286,37 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * @see DisplayManager#getDisplayTopology
+ */
+ @RequiresPermission(MANAGE_DISPLAYS)
+ @Nullable
+ public DisplayTopology getDisplayTopology() {
+ try {
+ return mDm.getDisplayTopology();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @see DisplayManager#setDisplayTopology
+ */
+ @RequiresPermission(MANAGE_DISPLAYS)
+ public void setDisplayTopology(DisplayTopology topology) {
+ try {
+ mDm.setDisplayTopology(topology);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, @DisplayEvent int event) {
if (DEBUG) {
- Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + eventToString(
- event));
+ Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event="
+ + eventToString(event));
}
handleDisplayEvent(displayId, event, false /* forceUpdate */);
}
@@ -1277,7 +1324,7 @@ public final class DisplayManagerGlobal {
private static final class DisplayListenerDelegate {
public final DisplayListener mListener;
- public volatile long mEventFlagsMask;
+ public volatile long mInternalEventFlagsMask;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Executor mExecutor;
@@ -1285,10 +1332,10 @@ public final class DisplayManagerGlobal {
private final String mPackageName;
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
- @EventFlag long eventFlag, String packageName) {
+ @InternalEventFlag long internalEventFlag, String packageName) {
mExecutor = executor;
mListener = listener;
- mEventFlagsMask = eventFlag;
+ mInternalEventFlagsMask = internalEventFlag;
mPackageName = packageName;
}
@@ -1310,16 +1357,16 @@ public final class DisplayManagerGlobal {
mGenerationId.incrementAndGet();
}
- void setEventFlagsMask(@EventFlag long newEventsFlag) {
- mEventFlagsMask = newEventsFlag;
+ void setEventsMask(@InternalEventFlag long newInternalEventFlagsMask) {
+ mInternalEventFlagsMask = newInternalEventFlagsMask;
}
- private void handleDisplayEventInner(int displayId, @DisplayEvent int eventFlagsMask,
+ private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
@Nullable DisplayInfo info, boolean forceUpdate) {
if (extraLogging()) {
- Slog.i(TAG, "DLD(" + eventToString(eventFlagsMask)
+ Slog.i(TAG, "DLD(" + eventToString(event)
+ ", display=" + displayId
- + ", mEventsFlagMask=" + Long.toBinaryString(mEventFlagsMask)
+ + ", mEventsMask=" + Long.toBinaryString(mInternalEventFlagsMask)
+ ", mPackageName=" + mPackageName
+ ", displayInfo=" + info
+ ", listener=" + mListener.getClass() + ")");
@@ -1327,18 +1374,19 @@ public final class DisplayManagerGlobal {
if (DEBUG) {
Trace.beginSection(
TextUtils.trimToSize(
- "DLD(" + eventToString(eventFlagsMask)
+ "DLD(" + eventToString(event)
+ ", display=" + displayId
+ ", listener=" + mListener.getClass() + ")", 127));
}
- switch (eventFlagsMask) {
+ switch (event) {
case EVENT_DISPLAY_ADDED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
+ if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_ADDED) != 0) {
mListener.onDisplayAdded(displayId);
}
break;
case EVENT_DISPLAY_CHANGED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
+ if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_CHANGED)
+ != 0) {
if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
if (extraLogging()) {
Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
@@ -1350,29 +1398,32 @@ public final class DisplayManagerGlobal {
}
break;
case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED) != 0) {
mListener.onDisplayChanged(displayId);
}
break;
case EVENT_DISPLAY_REMOVED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
+ if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_REMOVED)
+ != 0) {
mListener.onDisplayRemoved(displayId);
}
break;
case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED) != 0) {
mListener.onDisplayChanged(displayId);
}
break;
case EVENT_DISPLAY_CONNECTED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
- != 0) {
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
mListener.onDisplayConnected(displayId);
}
break;
case EVENT_DISPLAY_DISCONNECTED:
- if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
- != 0) {
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
mListener.onDisplayDisconnected(displayId);
}
break;
@@ -1384,7 +1435,7 @@ public final class DisplayManagerGlobal {
@Override
public String toString() {
- return "mEventFlagsMask: {" + mEventFlagsMask + "}, for " + mListener.getClass();
+ return "flag: {" + mInternalEventFlagsMask + "}, for " + mListener.getClass();
}
}
@@ -1432,6 +1483,13 @@ public final class DisplayManagerGlobal {
mExecutor.execute(mCallback::onStopped);
}
}
+
+ @Override // Binder call
+ public void onRequestedBrightnessChanged(float brightness) {
+ if (mCallback != null) {
+ mExecutor.execute(() -> mCallback.onRequestedBrightnessChanged(brightness));
+ }
+ }
}
/**
@@ -1525,4 +1583,53 @@ public final class DisplayManagerGlobal {
private static boolean extraLogging() {
return sExtraDisplayListenerLogging;
}
+
+
+ /**
+ * Maps the supplied public and private event flags to a unified InternalEventFlag
+ * @param eventFlags A bitmask of the event types for which this listener is subscribed.
+ * @param privateEventFlags A bitmask of the private event types for which this listener
+ * is subscribed.
+ * @return returns the bitmask of both public and private event flags unified to
+ * InternalEventFlag
+ */
+ public @InternalEventFlag long mapFlagsToInternalEventFlag(@EventFlag long eventFlags,
+ @DisplayManager.PrivateEventFlag long privateEventFlags) {
+ return mapPrivateEventFlags(privateEventFlags) | mapPublicEventFlags(eventFlags);
+ }
+
+ private long mapPrivateEventFlags(@DisplayManager.PrivateEventFlag long privateEventFlags) {
+ long baseEventMask = 0;
+ if ((privateEventFlags & DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED;
+ }
+
+ if ((privateEventFlags & DisplayManager.PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED;
+ }
+
+ if ((privateEventFlags
+ & DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED;
+ }
+ return baseEventMask;
+ }
+
+ private long mapPublicEventFlags(@EventFlag long eventFlags) {
+ long baseEventMask = 0;
+ if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED;
+ }
+
+ if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_CHANGED;
+ }
+
+ if ((eventFlags
+ & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
+ }
+
+ return baseEventMask;
+ }
}
diff --git a/core/java/android/hardware/display/DisplayTopology.aidl b/core/java/android/hardware/display/DisplayTopology.aidl
new file mode 100644
index 000000000000..e69b777a30de
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayTopology.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable DisplayTopology;
diff --git a/services/core/java/com/android/server/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index fdadafeb98c9..e349b81614bc 100644
--- a/services/core/java/com/android/server/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -14,25 +14,34 @@
* limitations under the License.
*/
-package com.android.server.display;
+package android.hardware.display;
-import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM;
-import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_LEFT;
-import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP;
-import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT;
+import static android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM;
+import static android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT;
+import static android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT;
+import static android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.view.Display;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -42,24 +51,59 @@ import java.util.Queue;
/**
* Represents the relative placement of extended displays.
* Does not support concurrent calls, so a lock should be held when calling into this class.
+ *
+ * @hide
*/
-class DisplayTopology {
+public final class DisplayTopology implements Parcelable {
private static final String TAG = "DisplayTopology";
private static final float EPSILON = 0.0001f;
+ @android.annotation.NonNull
+ public static final Creator<DisplayTopology> CREATOR =
+ new Creator<>() {
+ @Override
+ public DisplayTopology createFromParcel(Parcel source) {
+ return new DisplayTopology(source);
+ }
+
+ @Override
+ public DisplayTopology[] newArray(int size) {
+ return new DisplayTopology[size];
+ }
+ };
+
/**
* The topology tree
*/
@Nullable
- @VisibleForTesting
- TreeNode mRoot;
+ private TreeNode mRoot;
/**
* The logical display ID of the primary display that will show certain UI elements.
* This is not necessarily the same as the default display.
*/
+ private int mPrimaryDisplayId = Display.INVALID_DISPLAY;
+
+ public DisplayTopology() {}
+
@VisibleForTesting
- int mPrimaryDisplayId = Display.INVALID_DISPLAY;
+ public DisplayTopology(TreeNode root, int primaryDisplayId) {
+ mRoot = root;
+ mPrimaryDisplayId = primaryDisplayId;
+ }
+
+ public DisplayTopology(Parcel source) {
+ this(source.readTypedObject(TreeNode.CREATOR), source.readInt());
+ }
+
+ @Nullable
+ public TreeNode getRoot() {
+ return mRoot;
+ }
+
+ public int getPrimaryDisplayId() {
+ return mPrimaryDisplayId;
+ }
/**
* Add a display to the topology.
@@ -69,7 +113,7 @@ class DisplayTopology {
* @param width The width of the display
* @param height The height of the display
*/
- void addDisplay(int displayId, float width, float height) {
+ public void addDisplay(int displayId, float width, float height) {
addDisplay(displayId, width, height, /* shouldLog= */ true);
}
@@ -79,7 +123,7 @@ class DisplayTopology {
* one by one.
* @param displayId The logical display ID
*/
- void removeDisplay(int displayId) {
+ public void removeDisplay(int displayId) {
if (findDisplay(displayId, mRoot) == null) {
return;
}
@@ -106,11 +150,22 @@ class DisplayTopology {
}
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mRoot, flags);
+ dest.writeInt(mPrimaryDisplayId);
+ }
+
/**
* Print the object's state and debug information into the given stream.
* @param pw The stream to dump information to.
*/
- void dump(PrintWriter pw) {
+ public void dump(PrintWriter pw) {
pw.println("DisplayTopology:");
pw.println("--------------------");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
@@ -126,13 +181,21 @@ class DisplayTopology {
}
}
+ @Override
+ public String toString() {
+ StringWriter out = new StringWriter();
+ PrintWriter writer = new PrintWriter(out);
+ dump(writer);
+ return out.toString();
+ }
+
private void addDisplay(int displayId, float width, float height, boolean shouldLog) {
if (findDisplay(displayId, mRoot) != null) {
throw new IllegalArgumentException(
"DisplayTopology: attempting to add a display that already exists");
}
if (mRoot == null) {
- mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0);
+ mRoot = new TreeNode(displayId, width, height, /* position= */ 0, /* offset= */ 0);
mPrimaryDisplayId = displayId;
if (shouldLog) {
Slog.i(TAG, "First display added: " + mRoot);
@@ -241,7 +304,7 @@ class DisplayTopology {
* Update the topology to remove any overlaps between displays.
*/
@VisibleForTesting
- void normalize() {
+ public void normalize() {
if (mRoot == null) {
return;
}
@@ -341,6 +404,8 @@ class DisplayTopology {
case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left);
case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom);
case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top);
+ default -> throw new IllegalStateException(
+ "Unexpected value: " + targetDisplay.mPosition);
};
// Check that the offset is within bounds
areTouching &= switch (targetDisplay.mPosition) {
@@ -350,6 +415,8 @@ class DisplayTopology {
case POSITION_TOP, POSITION_BOTTOM ->
childBounds.right + EPSILON >= parentBounds.left
&& childBounds.left <= parentBounds.right + EPSILON;
+ default -> throw new IllegalStateException(
+ "Unexpected value: " + targetDisplay.mPosition);
};
if (!areTouching) {
@@ -379,36 +446,56 @@ class DisplayTopology {
* @param b second float to compare
* @return whether the two values are within a small enough tolerance value
*/
- public static boolean floatEquals(float a, float b) {
- return a == b || Float.isNaN(a) && Float.isNaN(b) || Math.abs(a - b) < EPSILON;
+ private static boolean floatEquals(float a, float b) {
+ return a == b || (Float.isNaN(a) && Float.isNaN(b)) || Math.abs(a - b) < EPSILON;
}
- @VisibleForTesting
- static class TreeNode {
+ public static final class TreeNode implements Parcelable {
+ public static final int POSITION_LEFT = 0;
+ public static final int POSITION_TOP = 1;
+ public static final int POSITION_RIGHT = 2;
+ public static final int POSITION_BOTTOM = 3;
+
+ @IntDef(prefix = { "POSITION_" }, value = {
+ POSITION_LEFT, POSITION_TOP, POSITION_RIGHT, POSITION_BOTTOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Position{}
+
+ @android.annotation.NonNull
+ public static final Creator<TreeNode> CREATOR =
+ new Creator<>() {
+ @Override
+ public TreeNode createFromParcel(Parcel source) {
+ return new TreeNode(source);
+ }
+
+ @Override
+ public TreeNode[] newArray(int size) {
+ return new TreeNode[size];
+ }
+ };
/**
* The logical display ID
*/
- @VisibleForTesting
- final int mDisplayId;
+ private final int mDisplayId;
/**
* The width of the display in density-independent pixels (dp).
*/
- @VisibleForTesting
- float mWidth;
+ private final float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
- @VisibleForTesting
- float mHeight;
+ private final float mHeight;
/**
* The position of this display relative to its parent.
*/
- @VisibleForTesting
- Position mPosition;
+ @Position
+ private int mPosition;
/**
* The distance from the top edge of the parent display to the top edge of this display (in
@@ -416,13 +503,13 @@ class DisplayTopology {
* to the left edge of this display (in case of POSITION_TOP or POSITION_BOTTOM). The unit
* used is density-independent pixels (dp).
*/
- @VisibleForTesting
- float mOffset;
+ private float mOffset;
- @VisibleForTesting
- final List<TreeNode> mChildren = new ArrayList<>();
+ private final List<TreeNode> mChildren = new ArrayList<>();
- TreeNode(int displayId, float width, float height, Position position, float offset) {
+ @VisibleForTesting
+ public TreeNode(int displayId, float width, float height, @Position int position,
+ float offset) {
mDisplayId = displayId;
mWidth = width;
mHeight = height;
@@ -430,11 +517,76 @@ class DisplayTopology {
mOffset = offset;
}
+ public TreeNode(Parcel source) {
+ this(source.readInt(), source.readFloat(), source.readFloat(), source.readInt(),
+ source.readFloat());
+ source.readTypedList(mChildren, CREATOR);
+ }
+
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ public float getWidth() {
+ return mWidth;
+ }
+
+ public float getHeight() {
+ return mHeight;
+ }
+
+ public int getPosition() {
+ return mPosition;
+ }
+
+ public float getOffset() {
+ return mOffset;
+ }
+
+ public List<TreeNode> getChildren() {
+ return Collections.unmodifiableList(mChildren);
+ }
+
+ @Override
+ public String toString() {
+ return "Display {id=" + mDisplayId + ", width=" + mWidth + ", height=" + mHeight
+ + ", position=" + positionToString(mPosition) + ", offset=" + mOffset + "}";
+ }
+
+ /**
+ * @param position The position
+ * @return The string representation
+ */
+ public static String positionToString(@Position int position) {
+ return switch (position) {
+ case POSITION_LEFT -> "left";
+ case POSITION_TOP -> "top";
+ case POSITION_RIGHT -> "right";
+ case POSITION_BOTTOM -> "bottom";
+ default -> throw new IllegalStateException("Unexpected value: " + position);
+ };
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mDisplayId);
+ dest.writeFloat(mWidth);
+ dest.writeFloat(mHeight);
+ dest.writeInt(mPosition);
+ dest.writeFloat(mOffset);
+ dest.writeTypedList(mChildren);
+ }
+
/**
* Print the object's state and debug information into the given stream.
* @param ipw The stream to dump information to.
*/
- void dump(IndentingPrintWriter ipw) {
+ public void dump(IndentingPrintWriter ipw) {
ipw.println(this);
ipw.increaseIndent();
for (TreeNode child : mChildren) {
@@ -443,15 +595,12 @@ class DisplayTopology {
ipw.decreaseIndent();
}
- @Override
- public String toString() {
- return "Display {id=" + mDisplayId + ", width=" + mWidth + ", height=" + mHeight
- + ", position=" + mPosition + ", offset=" + mOffset + "}";
- }
-
+ /**
+ * @param child The child to add
+ */
@VisibleForTesting
- enum Position {
- POSITION_LEFT, POSITION_TOP, POSITION_RIGHT, POSITION_BOTTOM
+ public void addChild(TreeNode child) {
+ mChildren.add(child);
}
}
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b612bca5671e..4fbdf7f5afc8 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -23,6 +23,7 @@ import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.HdrConversionMode;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -254,4 +255,13 @@ interface IDisplayManager {
// Get the default doze brightness
@EnforcePermission("CONTROL_DISPLAY_BRIGHTNESS")
float getDefaultDozeBrightness(int displayId);
+
+ // Get the display topology
+ @EnforcePermission("MANAGE_DISPLAYS")
+ @nullable
+ DisplayTopology getDisplayTopology();
+
+ // Set the display topology
+ @EnforcePermission("MANAGE_DISPLAYS")
+ void setDisplayTopology(in DisplayTopology topology);
}
diff --git a/core/java/android/hardware/display/IVirtualDisplayCallback.aidl b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
index c3490d177be2..9cc0364f2729 100644
--- a/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
+++ b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
@@ -38,4 +38,9 @@ oneway interface IVirtualDisplayCallback {
* of the application to release() the virtual display.
*/
void onStopped();
+
+ /**
+ * Called when the virtual display's requested brightness has changed.
+ */
+ void onRequestedBrightnessChanged(float brightness);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 32b640583734..3b573ea98c27 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -16,6 +16,8 @@
package android.hardware.display;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.SystemApi;
import android.view.Display;
import android.view.Surface;
@@ -164,5 +166,25 @@ public final class VirtualDisplay {
* of the application to release() the virtual display.
*/
public void onStopped() { }
+
+ /**
+ * Called when the requested brightness of the display has changed.
+ *
+ * <p>The system may adjust the display's brightness based on user or app activity. This
+ * callback will only be invoked if the display has an explicitly specified default
+ * brightness value.</p>
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
+ * {@code 1.0} indicates the maximum supported brightness.</p>
+ *
+ * @see android.view.View#setKeepScreenOn(boolean)
+ * @see android.view.WindowManager.LayoutParams#screenBrightness
+ * @see VirtualDisplayConfig.Builder#setDefaultBrightness(float)
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SystemApi
+ public void onRequestedBrightnessChanged(
+ @FloatRange(from = 0.0f, to = 1.0f) float brightness) {}
}
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 49944c76eb99..57d9d28a9d47 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -29,6 +29,7 @@ import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PowerManager;
import android.util.ArraySet;
import android.view.Display;
import android.view.DisplayCutout;
@@ -61,6 +62,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private final boolean mIsHomeSupported;
private final DisplayCutout mDisplayCutout;
private final boolean mIgnoreActivitySizeRestrictions;
+ private final float mDefaultBrightness;
private VirtualDisplayConfig(
@NonNull String name,
@@ -76,7 +78,8 @@ public final class VirtualDisplayConfig implements Parcelable {
float requestedRefreshRate,
boolean isHomeSupported,
@Nullable DisplayCutout displayCutout,
- boolean ignoreActivitySizeRestrictions) {
+ boolean ignoreActivitySizeRestrictions,
+ @FloatRange(from = 0.0f, to = 1.0f) float defaultBrightness) {
mName = name;
mWidth = width;
mHeight = height;
@@ -91,6 +94,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mIsHomeSupported = isHomeSupported;
mDisplayCutout = displayCutout;
mIgnoreActivitySizeRestrictions = ignoreActivitySizeRestrictions;
+ mDefaultBrightness = defaultBrightness;
}
/**
@@ -157,6 +161,22 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Returns the default brightness of the display.
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of {@code 1.0}
+ * indicates the maximum supported brightness.</p>
+ *
+ * @see Builder#setDefaultBrightness(float)
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SystemApi
+ public @FloatRange(from = 0.0f, to = 1.0f) float getDefaultBrightness() {
+ return mDefaultBrightness;
+ }
+
+
+ /**
* Returns the unique identifier for the display. Shouldn't be displayed to the user.
* @hide
*/
@@ -245,6 +265,7 @@ public final class VirtualDisplayConfig implements Parcelable {
dest.writeBoolean(mIsHomeSupported);
DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
dest.writeBoolean(mIgnoreActivitySizeRestrictions);
+ dest.writeFloat(mDefaultBrightness);
}
@Override
@@ -272,7 +293,8 @@ public final class VirtualDisplayConfig implements Parcelable {
&& mRequestedRefreshRate == that.mRequestedRefreshRate
&& mIsHomeSupported == that.mIsHomeSupported
&& mIgnoreActivitySizeRestrictions == that.mIgnoreActivitySizeRestrictions
- && Objects.equals(mDisplayCutout, that.mDisplayCutout);
+ && Objects.equals(mDisplayCutout, that.mDisplayCutout)
+ && mDefaultBrightness == that.mDefaultBrightness;
}
@Override
@@ -281,7 +303,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout,
- mIgnoreActivitySizeRestrictions);
+ mIgnoreActivitySizeRestrictions, mDefaultBrightness);
return hashCode;
}
@@ -303,6 +325,7 @@ public final class VirtualDisplayConfig implements Parcelable {
+ " mIsHomeSupported=" + mIsHomeSupported
+ " mDisplayCutout=" + mDisplayCutout
+ " mIgnoreActivitySizeRestrictions=" + mIgnoreActivitySizeRestrictions
+ + " mDefaultBrightness=" + mDefaultBrightness
+ ")";
}
@@ -321,6 +344,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mIsHomeSupported = in.readBoolean();
mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
mIgnoreActivitySizeRestrictions = in.readBoolean();
+ mDefaultBrightness = in.readFloat();
}
@NonNull
@@ -355,6 +379,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private boolean mIsHomeSupported = false;
private DisplayCutout mDisplayCutout = null;
private boolean mIgnoreActivitySizeRestrictions = false;
+ private float mDefaultBrightness = 0.0f;
/**
* Creates a new Builder.
@@ -547,6 +572,35 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Sets the default brightness of the display.
+ *
+ * <p>The system will use this brightness value whenever the display should be bright, i.e.
+ * it is powered on and not dimmed due to user activity or app activity.</p>
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
+ * {@code 1.0} indicates the maximum supported brightness.</p>
+ *
+ * <p>If unset, defaults to {@code 0.0}</p>
+ *
+ * @see android.view.View#setKeepScreenOn(boolean)
+ * @see Builder#setDefaultBrightness(float)
+ * @see VirtualDisplay.Callback#onRequestedBrightnessChanged(float)
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SystemApi
+ @NonNull
+ public Builder setDefaultBrightness(@FloatRange(from = 0.0f, to = 1.0f) float brightness) {
+ if (brightness < PowerManager.BRIGHTNESS_MIN
+ || brightness > PowerManager.BRIGHTNESS_MAX) {
+ throw new IllegalArgumentException(
+ "Virtual display default brightness must be in range [0.0, 1.0]");
+ }
+ mDefaultBrightness = brightness;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -565,7 +619,8 @@ public final class VirtualDisplayConfig implements Parcelable {
mRequestedRefreshRate,
mIsHomeSupported,
mDisplayCutout,
- mIgnoreActivitySizeRestrictions);
+ mIgnoreActivitySizeRestrictions,
+ mDefaultBrightness);
}
}
}
diff --git a/core/java/android/hardware/input/AidlInputGestureData.aidl b/core/java/android/hardware/input/AidlInputGestureData.aidl
index 137f672bf59c..e33ec53dd208 100644
--- a/core/java/android/hardware/input/AidlInputGestureData.aidl
+++ b/core/java/android/hardware/input/AidlInputGestureData.aidl
@@ -19,13 +19,26 @@ package android.hardware.input;
/** @hide */
@JavaDerive(equals=true)
parcelable AidlInputGestureData {
- int keycode;
- int modifierState;
- int gestureType;
+ Trigger trigger;
- // App launch parameters: Only set if gestureType is KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+ int gestureType;
+ // App launch parameters (Only set if gestureType is LAUNCH_APPLICATION)
String appLaunchCategory;
String appLaunchRole;
String appLaunchPackageName;
String appLaunchClassName;
+
+ parcelable KeyTrigger {
+ int keycode;
+ int modifierState;
+ }
+
+ parcelable TouchpadGestureTrigger {
+ int gestureType;
+ }
+
+ union Trigger {
+ KeyTrigger key;
+ TouchpadGestureTrigger touchpadGesture;
+ }
}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index bce95187515a..3284761eb273 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -266,17 +266,19 @@ interface IInputManager {
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- int addCustomInputGesture(in AidlInputGestureData data);
+ int addCustomInputGesture(int userId, in AidlInputGestureData data);
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- int removeCustomInputGesture(in AidlInputGestureData data);
+ int removeCustomInputGesture(int userId, in AidlInputGestureData data);
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- void removeAllCustomInputGestures();
+ void removeAllCustomInputGestures(int userId, int tag);
- AidlInputGestureData[] getCustomInputGestures();
+ AidlInputGestureData[] getCustomInputGestures(int userId, int tag);
+
+ AidlInputGestureData[] getAppLaunchBookmarks();
}
diff --git a/core/java/android/hardware/input/InputGestureData.java b/core/java/android/hardware/input/InputGestureData.java
index 5ab73cee9641..f41550f6061e 100644
--- a/core/java/android/hardware/input/InputGestureData.java
+++ b/core/java/android/hardware/input/InputGestureData.java
@@ -35,20 +35,40 @@ import java.util.Objects;
*/
public final class InputGestureData {
+ public static final int TOUCHPAD_GESTURE_TYPE_UNKNOWN = 0;
+ public static final int TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP = 1;
+
@NonNull
private final AidlInputGestureData mInputGestureData;
- public InputGestureData(AidlInputGestureData inputGestureData) {
+ public InputGestureData(@NonNull AidlInputGestureData inputGestureData) {
this.mInputGestureData = inputGestureData;
validate();
}
/** Returns the trigger information for this input gesture */
public Trigger getTrigger() {
- if (mInputGestureData.keycode != KeyEvent.KEYCODE_UNKNOWN) {
- return new KeyTrigger(mInputGestureData.keycode, mInputGestureData.modifierState);
+ switch (mInputGestureData.trigger.getTag()) {
+ case AidlInputGestureData.Trigger.Tag.key: {
+ AidlInputGestureData.KeyTrigger trigger = mInputGestureData.trigger.getKey();
+ if (trigger == null) {
+ throw new RuntimeException("InputGestureData is corrupted, null key trigger!");
+ }
+ return createKeyTrigger(trigger.keycode, trigger.modifierState);
+ }
+ case AidlInputGestureData.Trigger.Tag.touchpadGesture: {
+ AidlInputGestureData.TouchpadGestureTrigger trigger =
+ mInputGestureData.trigger.getTouchpadGesture();
+ if (trigger == null) {
+ throw new RuntimeException(
+ "InputGestureData is corrupted, null touchpad trigger!");
+ }
+ return createTouchpadTrigger(trigger.gestureType);
+ }
+ default:
+ throw new RuntimeException("InputGestureData is corrupted, invalid trigger type!");
+
}
- throw new RuntimeException("InputGestureData is corrupted, invalid trigger type!");
}
/** Returns the action to perform for this input gesture */
@@ -127,9 +147,15 @@ public final class InputGestureData {
"No app launch data for system action launch application");
}
AidlInputGestureData data = new AidlInputGestureData();
+ data.trigger = new AidlInputGestureData.Trigger();
if (mTrigger instanceof KeyTrigger keyTrigger) {
- data.keycode = keyTrigger.getKeycode();
- data.modifierState = keyTrigger.getModifierState();
+ data.trigger.setKey(new AidlInputGestureData.KeyTrigger());
+ data.trigger.getKey().keycode = keyTrigger.getKeycode();
+ data.trigger.getKey().modifierState = keyTrigger.getModifierState();
+ } else if (mTrigger instanceof TouchpadTrigger touchpadTrigger) {
+ data.trigger.setTouchpadGesture(new AidlInputGestureData.TouchpadGestureTrigger());
+ data.trigger.getTouchpadGesture().gestureType =
+ touchpadTrigger.getTouchpadGestureType();
} else {
throw new IllegalArgumentException("Invalid trigger type!");
}
@@ -163,30 +189,12 @@ public final class InputGestureData {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InputGestureData that = (InputGestureData) o;
- return mInputGestureData.keycode == that.mInputGestureData.keycode
- && mInputGestureData.modifierState == that.mInputGestureData.modifierState
- && mInputGestureData.gestureType == that.mInputGestureData.gestureType
- && Objects.equals(mInputGestureData.appLaunchCategory, that.mInputGestureData.appLaunchCategory)
- && Objects.equals(mInputGestureData.appLaunchRole, that.mInputGestureData.appLaunchRole)
- && Objects.equals(mInputGestureData.appLaunchPackageName, that.mInputGestureData.appLaunchPackageName)
- && Objects.equals(mInputGestureData.appLaunchPackageName, that.mInputGestureData.appLaunchPackageName);
+ return Objects.equals(mInputGestureData, that.mInputGestureData);
}
@Override
public int hashCode() {
- int _hash = 1;
- _hash = 31 * _hash + mInputGestureData.keycode;
- _hash = 31 * _hash + mInputGestureData.modifierState;
- _hash = 31 * _hash + mInputGestureData.gestureType;
- _hash = 31 * _hash + (mInputGestureData.appLaunchCategory != null
- ? mInputGestureData.appLaunchCategory.hashCode() : 0);
- _hash = 31 * _hash + (mInputGestureData.appLaunchRole != null
- ? mInputGestureData.appLaunchRole.hashCode() : 0);
- _hash = 31 * _hash + (mInputGestureData.appLaunchPackageName != null
- ? mInputGestureData.appLaunchPackageName.hashCode() : 0);
- _hash = 31 * _hash + (mInputGestureData.appLaunchPackageName != null
- ? mInputGestureData.appLaunchPackageName.hashCode() : 0);
- return _hash;
+ return mInputGestureData.hashCode();
}
public interface Trigger {
@@ -197,6 +205,11 @@ public final class InputGestureData {
return new KeyTrigger(keycode, modifierState);
}
+ /** Creates a input gesture trigger based on a touchpad gesture */
+ public static Trigger createTouchpadTrigger(int touchpadGestureType) {
+ return new TouchpadTrigger(touchpadGestureType);
+ }
+
/** Key based input gesture trigger */
public static class KeyTrigger implements Trigger {
private static final int SHORTCUT_META_MASK =
@@ -242,8 +255,76 @@ public final class InputGestureData {
}
}
+ /** Touchpad based input gesture trigger */
+ public static class TouchpadTrigger implements Trigger {
+ private final int mTouchpadGestureType;
+
+ private TouchpadTrigger(int touchpadGestureType) {
+ if (touchpadGestureType != TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP) {
+ throw new IllegalArgumentException(
+ "Invalid touchpadGestureType = " + touchpadGestureType);
+ }
+ mTouchpadGestureType = touchpadGestureType;
+ }
+
+ public int getTouchpadGestureType() {
+ return mTouchpadGestureType;
+ }
+
+ @Override
+ public String toString() {
+ return "TouchpadTrigger{" +
+ "mTouchpadGestureType=" + mTouchpadGestureType +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TouchpadTrigger that = (TouchpadTrigger) o;
+ return mTouchpadGestureType == that.mTouchpadGestureType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mTouchpadGestureType);
+ }
+ }
+
/** Data for action to perform when input gesture is triggered */
public record Action(@KeyGestureEvent.KeyGestureType int keyGestureType,
@Nullable AppLaunchData appLaunchData) {
}
+
+ /** Filter definition for InputGestureData */
+ public enum Filter {
+ KEY(AidlInputGestureData.Trigger.Tag.key),
+ TOUCHPAD(AidlInputGestureData.Trigger.Tag.touchpadGesture);
+
+ @AidlInputGestureData.Trigger.Tag
+ private final int mTag;
+
+ Filter(@AidlInputGestureData.Trigger.Tag int tag) {
+ mTag = tag;
+ }
+
+ @Nullable
+ public static Filter of(@AidlInputGestureData.Trigger.Tag int tag) {
+ return switch (tag) {
+ case AidlInputGestureData.Trigger.Tag.key -> KEY;
+ case AidlInputGestureData.Trigger.Tag.touchpadGesture -> TOUCHPAD;
+ default -> null;
+ };
+ }
+
+ @AidlInputGestureData.Trigger.Tag
+ public int getTag() {
+ return mTag;
+ }
+
+ public boolean matches(@NonNull InputGestureData inputGestureData) {
+ return mTag == inputGestureData.mInputGestureData.trigger.getTag();
+ }
+ }
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 876ba1021917..f8241925dff0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -34,6 +34,7 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.compat.annotation.ChangeId;
@@ -1487,16 +1488,16 @@ public final class InputManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
@CustomInputGestureResult
+ @UserHandleAware
public int addCustomInputGesture(@NonNull InputGestureData inputGestureData) {
if (!enableCustomizableInputGestures()) {
return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
}
try {
- return mIm.addCustomInputGesture(inputGestureData.getAidlData());
+ return mIm.addCustomInputGesture(mContext.getUserId(), inputGestureData.getAidlData());
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
- return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
}
/** Removes an existing custom gesture
@@ -1510,54 +1511,85 @@ public final class InputManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
@CustomInputGestureResult
+ @UserHandleAware
public int removeCustomInputGesture(@NonNull InputGestureData inputGestureData) {
if (!enableCustomizableInputGestures()) {
return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
}
try {
- return mIm.removeCustomInputGesture(inputGestureData.getAidlData());
+ return mIm.removeCustomInputGesture(mContext.getUserId(),
+ inputGestureData.getAidlData());
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
- return CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTHER;
}
/** Removes all custom input gestures
*
+ * @param filter for removing all gestures of a category. If {@code null}, all custom input
+ * gestures will be removed
+ *
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
- public void removeAllCustomInputGestures() {
+ @UserHandleAware
+ public void removeAllCustomInputGestures(@Nullable InputGestureData.Filter filter) {
if (!enableCustomizableInputGestures()) {
return;
}
try {
- mIm.removeAllCustomInputGestures();
+ mIm.removeAllCustomInputGestures(mContext.getUserId(),
+ filter == null ? -1 : filter.getTag());
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
/** Get all custom input gestures
*
+ * @param filter for fetching all gestures of a category. If {@code null}, then will return
+ * all custom input gestures
+ *
* @hide
*/
- public List<InputGestureData> getCustomInputGestures() {
+ @UserHandleAware
+ public List<InputGestureData> getCustomInputGestures(@Nullable InputGestureData.Filter filter) {
List<InputGestureData> result = new ArrayList<>();
if (!enableCustomizableInputGestures()) {
return result;
}
try {
- for (AidlInputGestureData data : mIm.getCustomInputGestures()) {
+ for (AidlInputGestureData data : mIm.getCustomInputGestures(mContext.getUserId(),
+ filter == null ? -1 : filter.getTag())) {
result.add(new InputGestureData(data));
}
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
return result;
}
/**
+ * Return the set of application launch bookmarks handled by the input framework.
+ *
+ * @return list of {@link InputGestureData} containing the application launch shortcuts parsed
+ * at boot time from {@code bookmarks.xml}.
+ *
+ * @hide
+ */
+ public List<InputGestureData> getAppLaunchBookmarks() {
+ try {
+ List<InputGestureData> result = new ArrayList<>();
+ for (AidlInputGestureData data : mIm.getAppLaunchBookmarks()) {
+ result.add(new InputGestureData(data));
+ }
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* A callback used to be notified about battery state changes for an input device. The
* {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
* listener is successfully registered to provide the initial battery state of the device.
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 4a9efe0a675b..71b60cff9367 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -20,16 +20,20 @@ import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FL
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG;
+import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
+import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
-import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling;
import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
import static com.android.hardware.input.Flags.touchpadTapDragging;
+import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
import static com.android.hardware.input.Flags.touchpadVisualizer;
-import static com.android.input.flags.Flags.enableInputFilterRustImpl;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
+import static com.android.input.flags.Flags.enableInputFilterRustImpl;
import static com.android.input.flags.Flags.keyboardRepeatKeys;
import android.Manifest;
@@ -379,6 +383,15 @@ public class InputSettings {
}
/**
+ * Returns true if the feature flag for the touchpad three-finger tap shortcut is enabled.
+ *
+ * @hide
+ */
+ public static boolean isTouchpadThreeFingerTapShortcutFeatureFlagEnabled() {
+ return isCustomizableInputGesturesFeatureFlagEnabled() && touchpadThreeFingerTapShortcut();
+ }
+
+ /**
* Returns true if the feature flag for mouse reverse vertical scrolling is enabled.
* @hide
*/
@@ -498,6 +511,22 @@ public class InputSettings {
}
/**
+ * Returns true if three-finger taps on the touchpad should trigger a customizable shortcut
+ * rather than a middle click.
+ *
+ * The returned value only applies to gesture-compatible touchpads.
+ *
+ * @param context The application context.
+ * @return Whether three-finger taps should trigger the shortcut.
+ *
+ * @hide
+ */
+ public static boolean useTouchpadThreeFingerTapShortcut(@NonNull Context context) {
+ // TODO(b/365063048): determine whether to enable the shortcut based on the settings.
+ return isTouchpadThreeFingerTapShortcutFeatureFlagEnabled();
+ }
+
+ /**
* Whether a pointer icon will be shown over the location of a stylus pointer.
*
* @hide
@@ -1105,4 +1134,27 @@ public class InputSettings {
Settings.Secure.KEY_REPEAT_DELAY_MS, delayTimeMillis,
UserHandle.USER_CURRENT);
}
+
+ /**
+ * Whether "Customizable key gestures" feature flag is enabled.
+ *
+ * <p>
+ * ‘Customizable key gestures’ is a feature which allows users to customize key based
+ * shortcuts on the physical keyboard.
+ * </p>
+ *
+ * @hide
+ */
+ public static boolean isCustomizableInputGesturesFeatureFlagEnabled() {
+ return enableCustomizableInputGestures() && useKeyGestureEventHandler();
+ }
+
+ /**
+ * Whether multi-key gestures are supported using {@code KeyGestureEventHandler}
+ *
+ * @hide
+ */
+ public static boolean doesKeyGestureEventHandlerSupportMultiKeyGestures() {
+ return useKeyGestureEventHandler() && useKeyGestureEventHandlerMultiPressGestures();
+ }
}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 9d42b67bf5a8..24951c4d516e 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -117,6 +117,10 @@ public final class KeyGestureEvent {
public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69;
public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 70;
public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN = 72;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT = 73;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74;
+ public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
public static final int FLAG_CANCELLED = 1;
@@ -203,6 +207,10 @@ public final class KeyGestureEvent {
KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+ KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
+ KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
+ KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
+ KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -773,6 +781,14 @@ public final class KeyGestureEvent {
return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
return "KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE";
+ case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN";
+ case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT";
+ case KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION:
+ return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION";
+ case KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK:
+ return "KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK";
default:
return Integer.toHexString(value);
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 71c91e913376..4b2f2c218e5a 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -30,14 +30,6 @@ flag {
flag {
namespace: "input_native"
- name: "emoji_and_screenshot_keycodes_available"
- is_exported: true
- description: "Add new KeyEvent keycodes for opening Emoji Picker and Taking Screenshots"
- bug: "315307777"
-}
-
-flag {
- namespace: "input_native"
name: "keyboard_a11y_slow_keys_flag"
description: "Controls if the slow keys accessibility feature for physical keyboard is available to the user"
bug: "294546335"
@@ -141,7 +133,7 @@ flag {
flag {
name: "keyboard_a11y_shortcut_control"
namespace: "input"
- description: "Adds shortcuts to toggle and control a11y features"
+ description: "Adds shortcuts to toggle and control a11y keyboard features"
bug: "373458181"
}
@@ -153,8 +145,29 @@ flag {
}
flag {
+ name: "enable_new_25q2_keycodes"
+ namespace: "input"
+ description: "Enables new 25Q2 keycodes"
+ bug: "365920375"
+}
+
+flag {
name: "override_power_key_behavior_in_focused_window"
- namespace: "input_native"
- description: "Allows privileged focused windows to capture power key events."
+ namespace: "wallet_integration"
+ description: "Allows privileged focused windows to override the power key double tap behavior."
bug: "357144512"
}
+
+flag {
+ name: "touchpad_three_finger_tap_shortcut"
+ namespace: "input"
+ description: "Turns three-finger touchpad taps into a customizable shortcut."
+ bug: "365063048"
+}
+
+flag {
+ name: "enable_talkback_and_magnifier_key_gestures"
+ namespace: "input"
+ description: "Adds key gestures for talkback and magnifier"
+ bug: "375277034"
+} \ No newline at end of file
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index 163f9fa83fe2..38f34e961ec0 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -71,6 +71,12 @@ public final class Light implements Parcelable {
public static final int LIGHT_TYPE_KEYBOARD_MIC_MUTE = 10004;
/**
+ * Type for keyboard volume mute light.
+ * @hide
+ */
+ public static final int LIGHT_TYPE_KEYBOARD_VOLUME_MUTE = 10005;
+
+ /**
* Capability for lights that could adjust its LED brightness. If the capability is not present
* the LED can only be turned either on or off.
*/
@@ -99,6 +105,7 @@ public final class Light implements Parcelable {
LIGHT_TYPE_PLAYER_ID,
LIGHT_TYPE_KEYBOARD_BACKLIGHT,
LIGHT_TYPE_KEYBOARD_MIC_MUTE,
+ LIGHT_TYPE_KEYBOARD_VOLUME_MUTE,
})
public @interface LightType {}
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 858ec23ebed8..af715e485b73 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -15,7 +15,6 @@
*/
package android.hardware.location;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 6284e7061b88..494bfc926384 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -18,6 +18,7 @@ package android.hardware.location;
import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,7 +32,6 @@ import android.app.ActivityThread;
import android.app.PendingIntent;
import android.chre.flags.Flags;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.contexthub.ErrorCode;
import android.os.Handler;
@@ -484,15 +484,33 @@ public final class ContextHubManager {
}
}
- /**
- * Helper function to generate a stub for a query transaction callback.
- *
- * @param transaction the transaction to unblock when complete
- *
- * @return the callback
- *
- * @hide
- */
+ /**
+ * Returns the list of HubInfo objects describing the available hubs (including ContextHub and
+ * VendorHub). This method is primarily used for debugging purposes as most clients care about
+ * endpoints and services more than hubs.
+ *
+ * @return the list of HubInfo objects
+ * @see HubInfo
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull
+ @FlaggedApi(Flags.FLAG_OFFLOAD_API)
+ public List<HubInfo> getHubs() {
+ try {
+ return mService.getHubs();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Helper function to generate a stub for a query transaction callback.
+ *
+ * @param transaction the transaction to unblock when complete
+ * @return the callback
+ * @hide
+ */
private IContextHubTransactionCallback createQueryCallback(
ContextHubTransaction<List<NanoAppState>> transaction) {
return new IContextHubTransactionCallback.Stub() {
diff --git a/core/java/android/hardware/location/HubInfo.aidl b/core/java/android/hardware/location/HubInfo.aidl
new file mode 100644
index 000000000000..25b5b0aa1222
--- /dev/null
+++ b/core/java/android/hardware/location/HubInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+/** @hide */
+parcelable HubInfo;
diff --git a/core/java/android/hardware/location/HubInfo.java b/core/java/android/hardware/location/HubInfo.java
new file mode 100644
index 000000000000..f7de1279672c
--- /dev/null
+++ b/core/java/android/hardware/location/HubInfo.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.chre.flags.Flags;
+import android.os.BadParcelableException;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Union type for {@link ContextHubInfo} and {@link VendorHubInfo}
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public final class HubInfo implements Parcelable {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {TYPE_CONTEXT_HUB, TYPE_VENDOR_HUB})
+ private @interface HubType {}
+
+ public static final int TYPE_CONTEXT_HUB = 0;
+ public static final int TYPE_VENDOR_HUB = 1;
+
+ private final long mId;
+ @HubType private final int mType;
+ @Nullable private final ContextHubInfo mContextHubInfo;
+ @Nullable private final VendorHubInfo mVendorHubInfo;
+
+ /** @hide */
+ public HubInfo(long id, @NonNull ContextHubInfo contextHubInfo) {
+ mId = id;
+ mType = TYPE_CONTEXT_HUB;
+ mContextHubInfo = contextHubInfo;
+ mVendorHubInfo = null;
+ }
+
+ /** @hide */
+ public HubInfo(long id, @NonNull VendorHubInfo vendorHubInfo) {
+ mId = id;
+ mType = TYPE_VENDOR_HUB;
+ mContextHubInfo = null;
+ mVendorHubInfo = vendorHubInfo;
+ }
+
+ private HubInfo(Parcel in) {
+ mId = in.readLong();
+ mType = in.readInt();
+
+ switch (mType) {
+ case TYPE_CONTEXT_HUB:
+ mContextHubInfo = ContextHubInfo.CREATOR.createFromParcel(in);
+ mVendorHubInfo = null;
+ break;
+ case TYPE_VENDOR_HUB:
+ mVendorHubInfo = VendorHubInfo.CREATOR.createFromParcel(in);
+ mContextHubInfo = null;
+ break;
+ default:
+ throw new BadParcelableException("Parcelable has invalid type");
+ }
+ }
+
+ /** Get the hub unique identifier */
+ public long getId() {
+ return mId;
+ }
+
+ /** Get the hub type. The type can be {@link TYPE_CONTEXT_HUB} or {@link TYPE_VENDOR_HUB} */
+ public int getType() {
+ return mType;
+ }
+
+ /** Get the {@link ContextHubInfo} object, null if type is not {@link TYPE_CONTEXT_HUB} */
+ @Nullable
+ public ContextHubInfo getContextHubInfo() {
+ return mContextHubInfo;
+ }
+
+ /** Parcel implementation details */
+ public int describeContents() {
+ if (mType == TYPE_CONTEXT_HUB && mContextHubInfo != null) {
+ return mContextHubInfo.describeContents();
+ }
+ if (mType == TYPE_VENDOR_HUB && mVendorHubInfo != null) {
+ return mVendorHubInfo.describeContents();
+ }
+ return 0;
+ }
+
+ /** Parcel implementation details */
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeLong(mId);
+ out.writeInt(mType);
+
+ if (mType == TYPE_CONTEXT_HUB && mContextHubInfo != null) {
+ mContextHubInfo.writeToParcel(out, flags);
+ }
+
+ if (mType == TYPE_VENDOR_HUB && mVendorHubInfo != null) {
+ mVendorHubInfo.writeToParcel(out, flags);
+ }
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("HubInfo ID: 0x");
+ out.append(Long.toHexString(mId));
+ out.append("\n");
+ if (mType == TYPE_CONTEXT_HUB && mContextHubInfo != null) {
+ out.append(" ContextHubDetails: ");
+ out.append(mContextHubInfo);
+ }
+ if (mType == TYPE_VENDOR_HUB && mVendorHubInfo != null) {
+ out.append(" VendorHubDetails: ");
+ out.append(mVendorHubInfo);
+ }
+ return out.toString();
+ }
+
+ public static final @NonNull Creator<HubInfo> CREATOR =
+ new Creator<>() {
+ public HubInfo createFromParcel(Parcel in) {
+ return new HubInfo(in);
+ }
+
+ public HubInfo[] newArray(int size) {
+ return new HubInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 11f3046150d3..b0cc763dc8fd 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -18,6 +18,7 @@ package android.hardware.location;
// Declare any non-default types here with import statements
import android.app.PendingIntent;
+import android.hardware.location.HubInfo;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
import android.hardware.location.NanoApp;
@@ -82,6 +83,10 @@ interface IContextHubService {
@EnforcePermission("ACCESS_CONTEXT_HUB")
List<ContextHubInfo> getContextHubs();
+ // Returns a list of HubInfo objects of available hubs (including ContextHub and VendorHub)
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ List<HubInfo> getHubs();
+
// Loads a nanoapp at the specified hub (new API)
@EnforcePermission("ACCESS_CONTEXT_HUB")
void loadNanoAppOnHub(
diff --git a/core/java/android/hardware/location/OWNERS b/core/java/android/hardware/location/OWNERS
index 747f90947b9c..340d6f2eb08c 100644
--- a/core/java/android/hardware/location/OWNERS
+++ b/core/java/android/hardware/location/OWNERS
@@ -9,4 +9,4 @@ wyattriley@google.com
yuhany@google.com
# ContextHub team
-per-file *ContextHub*,*NanoApp* = file:platform/system/chre:/OWNERS
+per-file Android.bp,*Hub*,*NanoApp* = file:platform/system/chre:/OWNERS
diff --git a/core/java/android/hardware/location/VendorHubInfo.aidl b/core/java/android/hardware/location/VendorHubInfo.aidl
new file mode 100644
index 000000000000..a7936acbb654
--- /dev/null
+++ b/core/java/android/hardware/location/VendorHubInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+/** @hide */
+parcelable VendorHubInfo; \ No newline at end of file
diff --git a/core/java/android/hardware/location/VendorHubInfo.java b/core/java/android/hardware/location/VendorHubInfo.java
new file mode 100644
index 000000000000..26772b18176f
--- /dev/null
+++ b/core/java/android/hardware/location/VendorHubInfo.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.location;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.chre.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelableHolder;
+
+/**
+ * Information about a VendorHub. VendorHub is similar to ContextHub, but it does not run the
+ * Context Hub Runtime Environment (or nano apps). It provides a unified endpoint messaging API
+ * through the ContextHub V4 HAL.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_OFFLOAD_API)
+public final class VendorHubInfo implements Parcelable {
+ private final String mName;
+ private final int mVersion;
+ private final ParcelableHolder mExtendedInfo;
+
+ /** @hide */
+ public VendorHubInfo(android.hardware.contexthub.VendorHubInfo halHubInfo) {
+ mName = halHubInfo.name;
+ mVersion = halHubInfo.version;
+ mExtendedInfo = halHubInfo.extendedInfo;
+ }
+
+ private VendorHubInfo(Parcel in) {
+ mName = in.readString();
+ mVersion = in.readInt();
+ mExtendedInfo = ParcelableHolder.CREATOR.createFromParcel(in);
+ }
+
+ /** Get the hub name */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /** Get the hub version */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /** Parcel implementation details */
+ public int describeContents() {
+ return mExtendedInfo.describeContents();
+ }
+
+ /** Parcel implementation details */
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mName);
+ out.writeInt(mVersion);
+ mExtendedInfo.writeToParcel(out, flags);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("VendorHub Name : ");
+ out.append(mName);
+ out.append(", Version : ");
+ out.append(mVersion);
+ return out.toString();
+ }
+
+ public static final @NonNull Creator<VendorHubInfo> CREATOR =
+ new Creator<>() {
+ public VendorHubInfo createFromParcel(Parcel in) {
+ return new VendorHubInfo(in);
+ }
+
+ public VendorHubInfo[] newArray(int size) {
+ return new VendorHubInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index c6fd0ee3c80d..7745b036bcbe 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1521,8 +1521,7 @@ public class SoundTrigger {
private final boolean mAllowMultipleTriggers;
private final KeyphraseRecognitionExtra mKeyphrases[];
private final byte[] mData;
- @ModuleProperties.AudioCapabilities
- private final int mAudioCapabilities;
+ private final @ModuleProperties.AudioCapabilities int mAudioCapabilities;
/**
* Constructor for {@link RecognitionConfig} with {@code audioCapabilities} describes a
@@ -1535,11 +1534,12 @@ public class SoundTrigger {
* @param keyphrases List of keyphrases in the sound model.
* @param data Opaque data for use by system applications who know about voice engine
* internals, typically during enrollment.
- * @param audioCapabilities Bit field encoding of the AudioCapabilities.
+ * @param audioCapabilities Bit field encoding of the AudioCapabilities. See
+ * {@link ModuleProperties.AudioCapabilities} for details.
*/
private RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
@Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
- int audioCapabilities) {
+ @ModuleProperties.AudioCapabilities int audioCapabilities) {
this.mCaptureRequested = captureRequested;
this.mAllowMultipleTriggers = allowMultipleTriggers;
this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
@@ -1617,8 +1617,11 @@ public class SoundTrigger {
return mData;
}
- /** Bit field encoding of the AudioCapabilities supported by the firmware. */
- public int getAudioCapabilities() {
+ /**
+ * Bit field encoding of the AudioCapabilities supported by the firmware. See
+ * {@link ModuleProperties.AudioCapabilities} for details.
+ */
+ public @ModuleProperties.AudioCapabilities int getAudioCapabilities() {
return mAudioCapabilities;
}
@@ -1702,7 +1705,7 @@ public class SoundTrigger {
private boolean mAllowMultipleTriggers;
@Nullable private KeyphraseRecognitionExtra[] mKeyphrases;
@Nullable private byte[] mData;
- private int mAudioCapabilities;
+ private @ModuleProperties.AudioCapabilities int mAudioCapabilities;
/**
* Constructs a new Builder with the default values.
@@ -1750,18 +1753,20 @@ public class SoundTrigger {
* internals, typically during enrollment.
* @return the same Builder instance.
*/
- public @NonNull Builder setData(@Nullable byte[] data) {
- mData = data;
+ public @NonNull Builder setData(@NonNull byte[] data) {
+ mData = requireNonNull(data, "Data must not be null");
return this;
}
/**
* Sets the audio capabilities field.
* @param audioCapabilities The bit field encoding of the audio capabilities associated
- * with this recognition session.
+ * with this recognition session. See
+ * {@link ModuleProperties.AudioCapabilities} for details.
* @return the same Builder instance.
*/
- public @NonNull Builder setAudioCapabilities(int audioCapabilities) {
+ public @NonNull Builder setAudioCapabilities(
+ @ModuleProperties.AudioCapabilities int audioCapabilities) {
mAudioCapabilities = audioCapabilities;
return this;
}
diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS
index a753f9634d0d..37604bc2eb65 100644
--- a/core/java/android/hardware/usb/OWNERS
+++ b/core/java/android/hardware/usb/OWNERS
@@ -1,7 +1,7 @@
# Bug component: 175220
-aprasath@google.com
-kumarashishg@google.com
-sarup@google.com
anothermark@google.com
+febinthattil@google.com
+aprasath@google.com
badhri@google.com
+kumarashishg@google.com \ No newline at end of file
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 92608d048135..d2e232a94622 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -54,6 +54,11 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -823,6 +828,216 @@ public class UsbManager {
}
}
+ /**
+ * Opens the handle for accessory, marks it as input or output, and adds it to the map
+ * if it is the first time the accessory has had an I/O stream associated with it.
+ */
+ private AccessoryHandle openHandleForAccessory(UsbAccessory accessory,
+ boolean openingInputStream)
+ throws RemoteException {
+ synchronized (mAccessoryHandleMapLock) {
+ if (mAccessoryHandleMap == null) {
+ mAccessoryHandleMap = new ArrayMap<>();
+ }
+
+ // If accessory isn't available in map
+ if (!mAccessoryHandleMap.containsKey(accessory)) {
+ // open accessory and store associated AccessoryHandle in map
+ ParcelFileDescriptor pfd = mService.openAccessory(accessory);
+ AccessoryHandle newHandle = new AccessoryHandle(pfd, openingInputStream,
+ !openingInputStream);
+ mAccessoryHandleMap.put(accessory, newHandle);
+
+ return newHandle;
+ }
+
+ // if accessory is already in map, get modified handle
+ AccessoryHandle currentHandle = mAccessoryHandleMap.get(accessory);
+ if (currentHandle == null) {
+ throw new IllegalStateException("Accessory doesn't have an associated handle yet!");
+ }
+
+ AccessoryHandle modifiedHandle = getModifiedHandleForOpeningStream(
+ openingInputStream, currentHandle);
+
+ mAccessoryHandleMap.put(accessory, modifiedHandle);
+
+ return modifiedHandle;
+ }
+ }
+
+ private AccessoryHandle getModifiedHandleForOpeningStream(boolean openingInputStream,
+ @NonNull AccessoryHandle currentHandle) {
+ if (currentHandle.isInputStreamOpened() && openingInputStream) {
+ throw new IllegalStateException("Input stream already open for this accessory! "
+ + "Please close the existing input stream before opening a new one.");
+ }
+
+ if (currentHandle.isOutputStreamOpened() && !openingInputStream) {
+ throw new IllegalStateException("Output stream already open for this accessory! "
+ + "Please close the existing output stream before opening a new one.");
+ }
+
+ boolean isInputStreamOpened = openingInputStream || currentHandle.isInputStreamOpened();
+ boolean isOutputStreamOpened = !openingInputStream || currentHandle.isOutputStreamOpened();
+
+ return new AccessoryHandle(
+ currentHandle.getPfd(), isInputStreamOpened, isOutputStreamOpened);
+ }
+
+ /**
+ * Marks the handle for the given accessory closed for input or output, and closes the handle
+ * and removes it from the map if there are no more I/O streams associated with the handle.
+ */
+ private void closeHandleForAccessory(UsbAccessory accessory, boolean closingInputStream)
+ throws IOException {
+ synchronized (mAccessoryHandleMapLock) {
+ AccessoryHandle currentHandle = mAccessoryHandleMap.get(accessory);
+
+ if (currentHandle == null) {
+ throw new IllegalStateException(
+ "No handle has been initialised for this accessory!");
+ }
+
+ AccessoryHandle modifiedHandle = getModifiedHandleForClosingStream(
+ closingInputStream, currentHandle);
+ if (!modifiedHandle.isOpen()) {
+ //close handle and remove accessory handle pair from map
+ modifiedHandle.getPfd().close();
+ mAccessoryHandleMap.remove(accessory);
+ } else {
+ mAccessoryHandleMap.put(accessory, modifiedHandle);
+ }
+ }
+ }
+
+ private AccessoryHandle getModifiedHandleForClosingStream(boolean closingInputStream,
+ @NonNull AccessoryHandle currentHandle) {
+ if (!currentHandle.isInputStreamOpened() && closingInputStream) {
+ throw new IllegalStateException(
+ "Attempting to close an input stream that has not been opened "
+ + "for this accessory!");
+ }
+
+ if (!currentHandle.isOutputStreamOpened() && !closingInputStream) {
+ throw new IllegalStateException(
+ "Attempting to close an output stream that has not been opened "
+ + "for this accessory!");
+ }
+
+ boolean isInputStreamOpened = !closingInputStream && currentHandle.isInputStreamOpened();
+ boolean isOutputStreamOpened = closingInputStream && currentHandle.isOutputStreamOpened();
+
+ return new AccessoryHandle(
+ currentHandle.getPfd(), isInputStreamOpened, isOutputStreamOpened);
+ }
+
+ /**
+ * An InputStream you can create on a UsbAccessory, which will
+ * take care of calling {@link ParcelFileDescriptor#close
+ * ParcelFileDescriptor.close()} for you when the stream is closed.
+ */
+ private class AccessoryAutoCloseInputStream extends FileInputStream {
+
+ private final ParcelFileDescriptor mPfd;
+ private final UsbAccessory mAccessory;
+
+ AccessoryAutoCloseInputStream(UsbAccessory accessory, ParcelFileDescriptor pfd) {
+ super(pfd.getFileDescriptor());
+ this.mAccessory = accessory;
+ this.mPfd = pfd;
+ }
+
+ @Override
+ public void close() throws IOException {
+ /* TODO(b/377850642) : Ensure the stream is closed even if client does not
+ explicitly close the stream to avoid corrupt FDs*/
+ super.close();
+ closeHandleForAccessory(mAccessory, true);
+ }
+
+
+ @Override
+ public int read() throws IOException {
+ final int result = super.read();
+ checkError(result);
+ return result;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ final int result = super.read(b);
+ checkError(result);
+ return result;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ final int result = super.read(b, off, len);
+ checkError(result);
+ return result;
+ }
+
+ private void checkError(int result) throws IOException {
+ if (result == -1 && mPfd.canDetectErrors()) {
+ mPfd.checkError();
+ }
+ }
+ }
+
+ /**
+ * An OutputStream you can create on a UsbAccessory, which will
+ * take care of calling {@link ParcelFileDescriptor#close
+ * ParcelFileDescriptor.close()} for you when the stream is closed.
+ */
+ private class AccessoryAutoCloseOutputStream extends FileOutputStream {
+ private final UsbAccessory mAccessory;
+
+ AccessoryAutoCloseOutputStream(UsbAccessory accessory, ParcelFileDescriptor pfd) {
+ super(pfd.getFileDescriptor());
+ mAccessory = accessory;
+ }
+
+ @Override
+ public void close() throws IOException {
+ /* TODO(b/377850642) : Ensure the stream is closed even if client does not
+ explicitly close the stream to avoid corrupt FDs*/
+ super.close();
+ closeHandleForAccessory(mAccessory, false);
+ }
+ }
+
+ /**
+ * Holds file descriptor and marks whether input and output streams have been opened for it.
+ */
+ private static class AccessoryHandle {
+ private final ParcelFileDescriptor mPfd;
+ private final boolean mInputStreamOpened;
+ private final boolean mOutputStreamOpened;
+ AccessoryHandle(ParcelFileDescriptor parcelFileDescriptor,
+ boolean inputStreamOpened, boolean outputStreamOpened) {
+ mPfd = parcelFileDescriptor;
+ mInputStreamOpened = inputStreamOpened;
+ mOutputStreamOpened = outputStreamOpened;
+ }
+
+ public ParcelFileDescriptor getPfd() {
+ return mPfd;
+ }
+
+ public boolean isInputStreamOpened() {
+ return mInputStreamOpened;
+ }
+
+ public boolean isOutputStreamOpened() {
+ return mOutputStreamOpened;
+ }
+
+ public boolean isOpen() {
+ return (mInputStreamOpened || mOutputStreamOpened);
+ }
+ }
+
private final Context mContext;
private final IUsbManager mService;
private final Object mDisplayPortListenersLock = new Object();
@@ -831,6 +1046,11 @@ public class UsbManager {
@GuardedBy("mDisplayPortListenersLock")
private DisplayPortAltModeInfoDispatchingListener mDisplayPortServiceListener;
+ private final Object mAccessoryHandleMapLock = new Object();
+ @GuardedBy("mAccessoryHandleMapLock")
+ private ArrayMap<UsbAccessory, AccessoryHandle> mAccessoryHandleMap;
+
+
/**
* @hide
*/
@@ -922,6 +1142,10 @@ public class UsbManager {
* data of a USB transfer should be read at once. If only a partial request is read the rest of
* the transfer is dropped.
*
+ * <p>It is strongly recommended to use newer methods instead of this method,
+ * since this method may provide sub-optimal performance on some devices.
+ * This method could potentially face interim performance degradation as well.
+ *
* @param accessory the USB accessory to open
* @return file descriptor, or null if the accessory could not be opened.
*/
@@ -935,6 +1159,49 @@ public class UsbManager {
}
/**
+ * Opens an input stream for reading from the USB accessory.
+ * If accessory is not open at this point, accessory will first be opened.
+ * <p>If data is read from the created {@link java.io.InputStream} all
+ * data of a USB transfer should be read at once. If only a partial request is read, the rest of
+ * the transfer is dropped.
+ * <p>The caller is responsible for ensuring that the returned stream is closed.
+ *
+ * @param accessory the USB accessory to open an input stream for
+ * @return input stream to read from given USB accessory
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API)
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
+ public @NonNull InputStream openAccessoryInputStream(@NonNull UsbAccessory accessory) {
+ try {
+ return new AccessoryAutoCloseInputStream(accessory,
+ openHandleForAccessory(accessory, true).getPfd());
+
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Opens an output stream for writing to the USB accessory.
+ * If accessory is not open at this point, accessory will first be opened.
+ * <p>The caller is responsible for ensuring that the returned stream is closed.
+ *
+ * @param accessory the USB accessory to open an output stream for
+ * @return output stream to write to given USB accessory
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API)
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
+ public @NonNull OutputStream openAccessoryOutputStream(@NonNull UsbAccessory accessory) {
+ try {
+ return new AccessoryAutoCloseOutputStream(accessory,
+ openHandleForAccessory(accessory, false).getPfd());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ }
+
+ /**
* Gets the functionfs control file descriptor for the given function, with
* the usb descriptors and strings already written. The file descriptor is used
* by the function implementation to handle events and control requests.
@@ -1293,7 +1560,7 @@ public class UsbManager {
* <p>
* This function returns the current USB bandwidth through USB Gadget HAL.
* It should be used when Android device is in USB peripheral mode and
- * connects to a USB host. If USB state is not configued, API will return
+ * connects to a USB host. If USB state is not configured, API will return
* {@value #USB_DATA_TRANSFER_RATE_UNKNOWN}. In addition, the unit of the
* return value is Mbps.
* </p>
diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
index 3b7a9e95c521..b719a7c6daac 100644
--- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
@@ -31,3 +31,11 @@ flag {
description: "Feature flag to enable exposing usb speed system api"
bug: "373653182"
}
+
+flag {
+ name: "enable_accessory_stream_api"
+ is_exported: true
+ namespace: "usb"
+ description: "Feature flag to enable stream APIs for Accessory mode"
+ bug: "369356693"
+}
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index 1adefe5a0b86..1b2c575917b2 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -18,13 +18,6 @@ flag {
}
flag {
- name: "safe_mode_timeout_config"
- namespace: "vcn"
- description: "Feature flag for adjustable safe mode timeout"
- bug: "317406085"
-}
-
-flag {
name: "fix_config_garbage_collection"
namespace: "vcn"
description: "Handle race condition in subscription change"
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index c7f8878f104e..f0e12ca644dd 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -85,7 +85,7 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {
throw new XmlPullParserException("Invalid XML parser state");
}
- consumerBuilder.setConsumedPower(
+ consumerBuilder.addConsumedPower(
parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER));
while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
@@ -132,11 +132,19 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {
}
/**
+ * Adds the total power included in this aggregate.
+ */
+ public Builder addConsumedPower(double consumedPowerMah) {
+ mData.putDouble(COLUMN_INDEX_CONSUMED_POWER,
+ mData.getDouble(COLUMN_INDEX_CONSUMED_POWER) + consumedPowerMah);
+ return this;
+ }
+
+ /**
* Adds power and usage duration from the supplied AggregateBatteryConsumer.
*/
public void add(AggregateBatteryConsumer aggregateBatteryConsumer) {
- setConsumedPower(mData.getDouble(COLUMN_INDEX_CONSUMED_POWER)
- + aggregateBatteryConsumer.getConsumedPower());
+ addConsumedPower(aggregateBatteryConsumer.getConsumedPower());
mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents);
}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index b0ecca790b80..14b67f64b6da 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -1064,7 +1064,9 @@ public abstract class BatteryConsumer {
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentPower Amount of consumed power in mAh.
+ * @deprecated use {@link #addConsumedPower}
*/
+ @Deprecated
@NonNull
public T setConsumedPower(@PowerComponentId int componentId, double componentPower) {
return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE);
@@ -1076,7 +1078,9 @@ public abstract class BatteryConsumer {
* @param componentId The ID of the power component, e.g.
* {@link BatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentPower Amount of consumed power in mAh.
+ * @deprecated use {@link #addConsumedPower}
*/
+ @Deprecated
@SuppressWarnings("unchecked")
@NonNull
public T setConsumedPower(@PowerComponentId int componentId, double componentPower,
@@ -1104,6 +1108,21 @@ public abstract class BatteryConsumer {
@SuppressWarnings("unchecked")
@NonNull
+ public T addConsumedPower(@PowerComponentId int componentId, double componentPower) {
+ mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentPower, POWER_MODEL_UNDEFINED);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
+ public T addConsumedPower(Key key, double componentPower) {
+ mPowerComponentsBuilder.addConsumedPower(key, componentPower, POWER_MODEL_UNDEFINED);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel);
return (T) this;
@@ -1115,7 +1134,9 @@ public abstract class BatteryConsumer {
* @param componentId The ID of the power component, e.g.
* {@link UidBatteryConsumer#POWER_COMPONENT_CPU}.
* @param componentUsageTimeMillis Amount of time in microseconds.
+ * @deprecated use {@link #addUsageDurationMillis}
*/
+ @Deprecated
@SuppressWarnings("unchecked")
@NonNull
public T setUsageDurationMillis(@PowerComponentId int componentId,
@@ -1126,6 +1147,7 @@ public abstract class BatteryConsumer {
return (T) this;
}
+ @Deprecated
@SuppressWarnings("unchecked")
@NonNull
public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) {
@@ -1133,6 +1155,14 @@ public abstract class BatteryConsumer {
return (T) this;
}
+ @NonNull
+ public T addUsageDurationMillis(@PowerComponentId int componentId,
+ long componentUsageTimeMillis) {
+ mPowerComponentsBuilder.addUsageDurationMillis(
+ getKey(componentId, PROCESS_STATE_UNSPECIFIED), componentUsageTimeMillis);
+ return (T) this;
+ }
+
@SuppressWarnings("unchecked")
@NonNull
public T addUsageDurationMillis(Key key, long componentUsageTimeMillis) {
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 6c3c2852c7e7..72e4cef2f6eb 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -209,7 +209,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
}
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(totalPowerMah);
+ .addConsumedPower(totalPowerMah);
mAggregateBatteryConsumers =
new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
@@ -1315,7 +1315,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
}
synchronized (BatteryUsageStats.class) {
- if (!sInstances.isEmpty()) {
+ if (sInstances != null && !sInstances.isEmpty()) {
Exception callSite = sInstances.entrySet().iterator().next().getValue();
int count = sInstances.size();
sInstances.clear();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 13d7e3c2fbfd..102bdd0b625c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -40,8 +40,6 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.View;
-import com.android.internal.ravenwood.RavenwoodEnvironment;
-
import dalvik.system.VMRuntime;
import java.lang.annotation.Retention;
@@ -57,10 +55,6 @@ import java.util.stream.Collectors;
*/
@RavenwoodKeepWholeClass
public class Build {
- static {
- // Set up the default system properties.
- RavenwoodEnvironment.ensureRavenwoodInitialized();
- }
private static final String TAG = "Build";
/** Value used for when a build property is unknown. */
@@ -1571,6 +1565,61 @@ public class Build {
/** A string that uniquely identifies this build. Do not attempt to parse this value. */
public static final String FINGERPRINT = deriveFingerprint();
+ /** The status of the known issue on this device is not known. */
+ @FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
+ public static final int BACKPORTED_FIX_STATUS_UNKNOWN = 0;
+ /** The known issue is fixed on this device. */
+ @FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
+ public static final int BACKPORTED_FIX_STATUS_FIXED = 1;
+ /**
+ * The known issue is not applicable to this device.
+ *
+ * <p>For example if the issue only affects a specific brand, devices
+ * from other brands would report not applicable.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
+ public static final int BACKPORTED_FIX_STATUS_NOT_APPLICABLE = 2;
+ /** The known issue is not fixed on this device. */
+ @FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
+ public static final int BACKPORTED_FIX_STATUS_NOT_FIXED = 3;
+
+ /**
+ * The status of the backported fix for a known issue on this device.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"BACKPORTED_FIX_STATUS_"},
+ value = {
+ BACKPORTED_FIX_STATUS_UNKNOWN,
+ BACKPORTED_FIX_STATUS_FIXED,
+ BACKPORTED_FIX_STATUS_NOT_APPLICABLE,
+ BACKPORTED_FIX_STATUS_NOT_FIXED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BackportedFixStatus {
+ }
+
+ /**
+ * The status of the backported fix for a known issue on this device.
+ *
+ * @param id The id of the known issue to check.
+ * @return {@link #BACKPORTED_FIX_STATUS_FIXED} if the known issue is
+ * fixed on this device,
+ * {@link #BACKPORTED_FIX_STATUS_NOT_FIXED} if the known issue is not
+ * fixed on this device,
+ * {@link #BACKPORTED_FIX_STATUS_NOT_APPLICABLE} if the known issue is
+ * is not applicable on this device,
+ * otherwise {@link #BACKPORTED_FIX_STATUS_UNKNOWN}.
+ */
+
+ @FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
+ public static @BackportedFixStatus int getBackportedFixStatus(long id) {
+ // TODO: b/308461809 - query aliases from system prop
+ // TODO: b/372518979 - use backported fix datastore.
+ return BACKPORTED_FIX_STATUS_UNKNOWN;
+ }
+
/**
* Some devices split the fingerprint components between multiple
* partitions, so we might derive the fingerprint at runtime.
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index c18fb0c38b82..99e7d166446e 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -281,7 +281,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
/** {@hide} */
- public void setIsIntentExtra() {
+ public void enableTokenVerification() {
mFlags |= FLAG_VERIFY_TOKENS_PRESENT;
}
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 66f4198ad31c..7529ab9ab894 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -31,6 +31,7 @@ import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import dalvik.annotation.optimization.NeverCompile;
+import dalvik.system.VMDebug;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -55,7 +56,7 @@ import java.util.concurrent.locks.ReentrantLock;
* {@link Looper#myQueue() Looper.myQueue()}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("MessageQueue_host")
+@RavenwoodRedirectionClass("MessageQueue_ravenwood")
public final class MessageQueue {
private static final String TAG_L = "LegacyMessageQueue";
private static final String TAG_C = "ConcurrentMessageQueue";
@@ -93,9 +94,7 @@ public final class MessageQueue {
* system processes and provides a higher level of concurrency and higher enqueue throughput
* than the legacy implementation.
*/
- private static boolean sUseConcurrent;
-
- private static boolean sUseConcurrentInitialized = false;
+ private boolean mUseConcurrent;
@RavenwoodRedirect
private native static long nativeInit();
@@ -112,10 +111,7 @@ public final class MessageQueue {
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
- if (!sUseConcurrentInitialized) {
- sUseConcurrent = UserHandle.isCore(Process.myUid());
- sUseConcurrentInitialized = true;
- }
+ mUseConcurrent = UserHandle.isCore(Process.myUid()) && !VMDebug.isDebuggingEnabled();
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
@@ -158,7 +154,7 @@ public final class MessageQueue {
* @return True if the looper is idle.
*/
public boolean isIdle() {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
final long now = SystemClock.uptimeMillis();
if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
@@ -208,7 +204,7 @@ public final class MessageQueue {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mIdleHandlersLock) {
mIdleHandlers.add(handler);
}
@@ -229,7 +225,7 @@ public final class MessageQueue {
* @param handler The IdleHandler to be removed.
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mIdleHandlersLock) {
mIdleHandlers.remove(handler);
}
@@ -252,7 +248,7 @@ public final class MessageQueue {
* @hide
*/
public boolean isPolling() {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
// If the loop is quitting then it must not be idling.
// We can assume mPtr != 0 when sQuitting is false.
return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
@@ -303,7 +299,7 @@ public final class MessageQueue {
throw new IllegalArgumentException("listener must not be null");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mFileDescriptorRecordsLock) {
updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
@@ -331,7 +327,7 @@ public final class MessageQueue {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mFileDescriptorRecordsLock) {
updateOnFileDescriptorEventListenerLocked(fd, 0, null);
}
@@ -388,7 +384,7 @@ public final class MessageQueue {
final int oldWatchedEvents;
final OnFileDescriptorEventListener listener;
final int seq;
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mFileDescriptorRecordsLock) {
record = mFileDescriptorRecords.get(fd);
if (record == null) {
@@ -431,13 +427,26 @@ public final class MessageQueue {
// Update the file descriptor record if the listener changed the set of
// events to watch and the listener itself hasn't been updated since.
if (newWatchedEvents != oldWatchedEvents) {
- synchronized (this) {
- int index = mFileDescriptorRecords.indexOfKey(fd);
- if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
- && record.mSeq == seq) {
- record.mEvents = newWatchedEvents;
- if (newWatchedEvents == 0) {
- mFileDescriptorRecords.removeAt(index);
+ if (mUseConcurrent) {
+ synchronized (mFileDescriptorRecordsLock) {
+ int index = mFileDescriptorRecords.indexOfKey(fd);
+ if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
+ && record.mSeq == seq) {
+ record.mEvents = newWatchedEvents;
+ if (newWatchedEvents == 0) {
+ mFileDescriptorRecords.removeAt(index);
+ }
+ }
+ }
+ } else {
+ synchronized (this) {
+ int index = mFileDescriptorRecords.indexOfKey(fd);
+ if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
+ && record.mSeq == seq) {
+ record.mEvents = newWatchedEvents;
+ if (newWatchedEvents == 0) {
+ mFileDescriptorRecords.removeAt(index);
+ }
}
}
}
@@ -708,7 +717,7 @@ public final class MessageQueue {
@UnsupportedAppUsage
Message next() {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return nextConcurrent();
}
@@ -834,7 +843,7 @@ public final class MessageQueue {
throw new IllegalStateException("Main thread not allowed to quit.");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
synchronized (mIdleHandlersLock) {
if (sQuitting.compareAndSet(this, false, true)) {
if (safe) {
@@ -898,7 +907,7 @@ public final class MessageQueue {
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
final int token = mNextBarrierTokenAtomic.getAndIncrement();
// b/376573804: apps and tests may expect to be able to use reflection
@@ -991,7 +1000,7 @@ public final class MessageQueue {
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
boolean removed;
MessageNode first;
final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
@@ -1058,7 +1067,7 @@ public final class MessageQueue {
throw new IllegalArgumentException("Message must have a target.");
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
@@ -1187,7 +1196,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
false);
}
@@ -1219,7 +1228,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
false);
@@ -1253,7 +1262,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
false);
}
@@ -1285,7 +1294,7 @@ public final class MessageQueue {
if (h == null) {
return false;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
}
synchronized (this) {
@@ -1304,7 +1313,7 @@ public final class MessageQueue {
if (h == null) {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
return;
}
@@ -1355,7 +1364,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
return;
}
@@ -1407,7 +1416,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
return;
}
@@ -1470,7 +1479,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
return;
}
@@ -1532,7 +1541,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
return;
}
@@ -1594,7 +1603,7 @@ public final class MessageQueue {
return;
}
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
return;
}
@@ -1742,7 +1751,7 @@ public final class MessageQueue {
@NeverCompile
void dump(Printer pw, String prefix, Handler h) {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
long now = SystemClock.uptimeMillis();
int n = 0;
@@ -1803,7 +1812,7 @@ public final class MessageQueue {
@NeverCompile
void dumpDebug(ProtoOutputStream proto, long fieldId) {
- if (sUseConcurrent) {
+ if (mUseConcurrent) {
final long messageQueueToken = proto.start(fieldId);
StackNode node = (StackNode) sState.getVolatile(this);
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 9db88d17614b..c2a47d767801 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -54,7 +54,7 @@ import java.util.concurrent.locks.ReentrantLock;
* {@link Looper#myQueue() Looper.myQueue()}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("MessageQueue_host")
+@RavenwoodRedirectionClass("MessageQueue_ravenwood")
public final class MessageQueue {
private static final String TAG = "ConcurrentMessageQueue";
private static final boolean DEBUG = false;
diff --git a/core/java/android/os/IThermalHeadroomListener.aidl b/core/java/android/os/IThermalHeadroomListener.aidl
new file mode 100644
index 000000000000..b2797d8805fa
--- /dev/null
+++ b/core/java/android/os/IThermalHeadroomListener.aidl
@@ -0,0 +1,31 @@
+/*
+** Copyright 2024, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * Listener for thermal headroom and threshold changes.
+ * This is mainly used by {@link android.os.PowerManager} to serve public thermal headoom related
+ * APIs.
+ * {@hide}
+ */
+oneway interface IThermalHeadroomListener {
+ /**
+ * Called when thermal headroom or thresholds changed.
+ */
+ void onHeadroomChange(in float headroom, in float forecastHeadroom,
+ in int forecastSeconds, in float[] thresholds);
+}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index bcffa45fbbd2..aa3bcfab6b66 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -18,6 +18,7 @@ package android.os;
import android.os.CoolingDevice;
import android.os.IThermalEventListener;
+import android.os.IThermalHeadroomListener;
import android.os.IThermalStatusListener;
import android.os.Temperature;
@@ -116,4 +117,20 @@ interface IThermalService {
* @return thermal headroom for each thermal status
*/
float[] getThermalHeadroomThresholds();
+
+ /**
+ * Register a listener for thermal headroom change.
+ * @param listener the {@link android.os.IThermalHeadroomListener} to be notified.
+ * @return true if registered successfully.
+ * {@hide}
+ */
+ boolean registerThermalHeadroomListener(in IThermalHeadroomListener listener);
+
+ /**
+ * Unregister a previously-registered listener for thermal headroom.
+ * @param listener the {@link android.os.IThermalHeadroomListener} to no longer be notified.
+ * @return true if unregistered successfully.
+ * {@hide}
+ */
+ boolean unregisterThermalHeadroomListener(in IThermalHeadroomListener listener);
}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 6aa9852314df..ecb5e6f1b29a 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -17,13 +17,17 @@
package android.os;
import android.os.CombinedVibration;
+import android.os.ICancellationSignal;
import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibratorInfo;
+import android.os.vibrator.IVibrationSession;
+import android.os.vibrator.IVibrationSessionCallback;
/** {@hide} */
interface IVibratorManagerService {
int[] getVibratorIds();
+ int getCapabilities();
VibratorInfo getVibratorInfo(int vibratorId);
@EnforcePermission("ACCESS_VIBRATOR_STATE")
boolean isVibrating(int vibratorId);
@@ -50,4 +54,9 @@ interface IVibratorManagerService {
oneway void performHapticFeedbackForInputDevice(int uid, int deviceId, String opPkg,
int constant, int inputDeviceId, int inputSource, String reason, int flags,
int privFlags);
+
+ @EnforcePermission(allOf={"VIBRATE", "VIBRATE_VENDOR_EFFECTS", "START_VIBRATION_SESSIONS"})
+ ICancellationSignal startVendorVibrationSession(int uid, int deviceId, String opPkg,
+ in int[] vibratorIds, in VibrationAttributes attributes, String reason,
+ in IVibrationSessionCallback callback);
}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 7875c23be038..8db1567336d3 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -23,6 +23,7 @@ import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.PropertyInvalidatedCache;
+import android.app.PropertyInvalidatedCache.Args;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -341,7 +342,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module,
@NonNull String api, @NonNull String cacheName,
@NonNull QueryHandler<Query, Result> computer) {
- super(maxEntries, module, api, cacheName, computer);
+ super(new Args(module).maxEntries(maxEntries).api(api), cacheName, computer);
}
/**
@@ -563,7 +564,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* @hide
*/
public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
- super(config.maxEntries(), config.module(), config.api(), config.name(), computer);
+ super(new Args(config.module()).maxEntries(config.maxEntries()).api(config.api()),
+ config.name(), computer);
}
/**
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index 9f7b0b71ae95..cae82d010132 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -45,7 +45,7 @@ import java.util.concurrent.atomic.AtomicLong;
* {@link Looper#myQueue() Looper.myQueue()}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("MessageQueue_host")
+@RavenwoodRedirectionClass("MessageQueue_ravenwood")
public final class MessageQueue {
private static final String TAG = "MessageQueue";
private static final boolean DEBUG = false;
diff --git a/core/java/android/os/LockedMessageQueue/MessageQueue.java b/core/java/android/os/LockedMessageQueue/MessageQueue.java
index f3eec13cb20f..2401f3d11bcf 100644
--- a/core/java/android/os/LockedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LockedMessageQueue/MessageQueue.java
@@ -48,7 +48,7 @@ import java.util.concurrent.atomic.AtomicLong;
* {@link Looper#myQueue() Looper.myQueue()}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("MessageQueue_host")
+@RavenwoodRedirectionClass("MessageQueue_ravenwood")
public final class MessageQueue {
private static final String TAG = "LockedMessageQueue";
private static final boolean DEBUG = false;
diff --git a/ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java b/core/java/android/os/MessageQueue_ravenwood.java
index 1b63adc4319f..4033707c3253 100644
--- a/ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java
+++ b/core/java/android/os/MessageQueue_ravenwood.java
@@ -16,13 +16,16 @@
package android.os;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
-public class MessageQueue_host {
+@RavenwoodKeepWholeClass
+class MessageQueue_ravenwood {
private static final AtomicLong sNextId = new AtomicLong(1);
- private static final Map<Long, MessageQueue_host> sInstances = new ConcurrentHashMap<>();
+ private static final Map<Long, MessageQueue_ravenwood> sInstances = new ConcurrentHashMap<>();
private boolean mDeleted = false;
@@ -37,8 +40,8 @@ public class MessageQueue_host {
}
}
- private static MessageQueue_host getInstance(long id) {
- MessageQueue_host q = sInstances.get(id);
+ private static MessageQueue_ravenwood getInstance(long id) {
+ MessageQueue_ravenwood q = sInstances.get(id);
if (q == null) {
throw new RuntimeException("MessageQueue doesn't exist with id=" + id);
}
@@ -48,7 +51,7 @@ public class MessageQueue_host {
public static long nativeInit() {
final long id = sNextId.getAndIncrement();
- final MessageQueue_host q = new MessageQueue_host();
+ final MessageQueue_ravenwood q = new MessageQueue_ravenwood();
sInstances.put(id, q);
return id;
}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index e80efd2a9380..60eeb2b8b0d5 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -41,7 +41,6 @@ import android.content.ContentResolver;
import android.net.Uri;
import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodReplace;
import android.ravenwood.annotation.RavenwoodThrow;
import android.system.ErrnoException;
import android.system.Os;
@@ -51,8 +50,6 @@ import android.util.CloseGuard;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.ravenwood.RavenwoodEnvironment;
-
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -1254,15 +1251,10 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
}
}
- @RavenwoodReplace
private static boolean isAtLeastQ() {
return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
}
- private static boolean isAtLeastQ$ravenwood() {
- return RavenwoodEnvironment.workaround().isTargetSdkAtLeastQ();
- }
-
private static int ifAtLeastQ(int value) {
return isAtLeastQ() ? value : 0;
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index f4e3f3b6e430..d116e0737c46 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -439,8 +439,8 @@ class PowerComponents {
}
final BatteryConsumer.Key key = builder.mData.layout.getKey(componentId,
processState, screenState, powerState);
- builder.setConsumedPower(key, powerMah, model);
- builder.setUsageDurationMillis(key, durationMs);
+ builder.addConsumedPower(key, powerMah, model);
+ builder.addUsageDurationMillis(key, durationMs);
break;
}
}
@@ -468,6 +468,10 @@ class PowerComponents {
}
}
+ /**
+ * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double, int)}
+ */
+ @Deprecated
@NonNull
public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
int powerModel) {
@@ -489,6 +493,10 @@ class PowerComponents {
return this;
}
+ /**
+ * @deprecated use {@link #addUsageDurationMillis(BatteryConsumer.Key, long)}
+ */
+ @Deprecated
@NonNull
public Builder setUsageDurationMillis(BatteryConsumer.Key key,
long componentUsageDurationMillis) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9d4ac2963b2d..07fded19c799 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -20,6 +20,7 @@ import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -40,6 +41,7 @@ import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import java.lang.annotation.ElementType;
@@ -1199,10 +1201,12 @@ public final class PowerManager {
/** We lazily initialize it.*/
private PowerExemptionManager mPowerExemptionManager;
+ @GuardedBy("mThermalStatusListenerMap")
private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
- mListenerMap = new ArrayMap<>();
- private final Object mThermalHeadroomThresholdsLock = new Object();
- private float[] mThermalHeadroomThresholds = null;
+ mThermalStatusListenerMap = new ArrayMap<>();
+ @GuardedBy("mThermalHeadroomListenerMap")
+ private final ArrayMap<OnThermalHeadroomChangedListener, IThermalHeadroomListener>
+ mThermalHeadroomListenerMap = new ArrayMap<>();
/**
* {@hide}
@@ -2689,15 +2693,59 @@ public final class PowerManager {
void onThermalStatusChanged(@ThermalStatus int status);
}
+ /**
+ * Listener passed to
+ * {@link PowerManager#addThermalHeadroomListener} and
+ * {@link PowerManager#removeThermalHeadroomListener}
+ * to notify caller of Thermal headroom or thresholds changes.
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK)
+ public interface OnThermalHeadroomChangedListener {
+
+ /**
+ * Called when overall thermal headroom or headroom thresholds have significantly
+ * changed that requires action.
+ * <p>
+ * This may not be used to fully replace the {@link #getThermalHeadroom(int)} API as it will
+ * only notify on one of the conditions below that will significantly change one or both
+ * values of current headroom and headroom thresholds since previous callback:
+ * 1. thermal throttling events: when the skin temperature has cross any of the thresholds
+ * and there isn't a previous callback in a short time ago with similar values.
+ * 2. skin temperature threshold change events: note that if the absolute °C threshold
+ * values change in a way that does not significantly change the current headroom nor
+ * headroom thresholds, it will not trigger any callback. The client should not
+ * need to take action in such case since the difference from temperature vs threshold
+ * hasn't changed.
+ * <p>
+ * By API version 36, it provides a forecast in the same call for developer's convenience
+ * based on a {@code forecastSeconds} defined by the device, which can be static or dynamic
+ * varied by OEM. Be aware that it will not notify on forecast temperature change but the
+ * events mentioned above. So periodically polling against {@link #getThermalHeadroom(int)}
+ * API should still be used to actively monitor temperature forecast in advance.
+ * <p>
+ * This serves as a more advanced option compared to thermal status listener, where the
+ * latter will only notify on thermal throttling events with status update.
+ *
+ * @param headroom current headroom
+ * @param forecastHeadroom forecasted headroom in future
+ * @param forecastSeconds how many seconds in the future used in forecast
+ * @param thresholds new headroom thresholds, see {@link #getThermalHeadroomThresholds()}
+ */
+ void onThermalHeadroomChanged(
+ @FloatRange(from = 0f) float headroom,
+ @FloatRange(from = 0f) float forecastHeadroom,
+ @IntRange(from = 0) int forecastSeconds,
+ @NonNull Map<@ThermalStatus Integer, Float> thresholds);
+ }
/**
- * This function adds a listener for thermal status change, listen call back will be
+ * This function adds a listener for thermal status change, listener callback will be
* enqueued tasks on the main thread
*
* @param listener listener to be added,
*/
public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
- Objects.requireNonNull(listener, "listener cannot be null");
+ Objects.requireNonNull(listener, "Thermal status listener cannot be null");
addThermalStatusListener(mContext.getMainExecutor(), listener);
}
@@ -2709,29 +2757,31 @@ public final class PowerManager {
*/
public void addThermalStatusListener(@NonNull @CallbackExecutor Executor executor,
@NonNull OnThermalStatusChangedListener listener) {
- Objects.requireNonNull(listener, "listener cannot be null");
- Objects.requireNonNull(executor, "executor cannot be null");
- Preconditions.checkArgument(!mListenerMap.containsKey(listener),
- "Listener already registered: %s", listener);
- IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
- @Override
- public void onStatusChange(int status) {
- final long token = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> listener.onThermalStatusChanged(status));
- } finally {
- Binder.restoreCallingIdentity(token);
+ Objects.requireNonNull(listener, "Thermal status listener cannot be null");
+ Objects.requireNonNull(executor, "Executor cannot be null");
+ synchronized (mThermalStatusListenerMap) {
+ Preconditions.checkArgument(!mThermalStatusListenerMap.containsKey(listener),
+ "Thermal status listener already registered: %s", listener);
+ IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
+ @Override
+ public void onStatusChange(int status) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onThermalStatusChanged(status));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
+ };
+ try {
+ if (mThermalService.registerThermalStatusListener(internalListener)) {
+ mThermalStatusListenerMap.put(listener, internalListener);
+ } else {
+ throw new RuntimeException("Thermal status listener failed to set");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- };
- try {
- if (mThermalService.registerThermalStatusListener(internalListener)) {
- mListenerMap.put(listener, internalListener);
- } else {
- throw new RuntimeException("Listener failed to set");
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
@@ -2741,20 +2791,101 @@ public final class PowerManager {
* @param listener listener to be removed
*/
public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
- Objects.requireNonNull(listener, "listener cannot be null");
- IThermalStatusListener internalListener = mListenerMap.get(listener);
- Preconditions.checkArgument(internalListener != null, "Listener was not added");
- try {
- if (mThermalService.unregisterThermalStatusListener(internalListener)) {
- mListenerMap.remove(listener);
- } else {
- throw new RuntimeException("Listener failed to remove");
+ Objects.requireNonNull(listener, "Thermal status listener cannot be null");
+ synchronized (mThermalStatusListenerMap) {
+ IThermalStatusListener internalListener = mThermalStatusListenerMap.get(listener);
+ Preconditions.checkArgument(internalListener != null,
+ "Thermal status listener was not added");
+ try {
+ if (mThermalService.unregisterThermalStatusListener(internalListener)) {
+ mThermalStatusListenerMap.remove(listener);
+ } else {
+ throw new RuntimeException("Failed to unregister thermal status listener");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
+ /**
+ * This function adds a listener for thermal headroom change, listener callback will be
+ * enqueued tasks on the main thread
+ *
+ * @param listener listener to be added,
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK)
+ public void addThermalHeadroomListener(@NonNull OnThermalHeadroomChangedListener listener) {
+ Objects.requireNonNull(listener, "Thermal headroom listener cannot be null");
+ addThermalHeadroomListener(mContext.getMainExecutor(), listener);
+ }
+
+ /**
+ * This function adds a listener for thermal headroom change.
+ *
+ * @param executor {@link Executor} to handle listener callback.
+ * @param listener listener to be added.
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK)
+ public void addThermalHeadroomListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnThermalHeadroomChangedListener listener) {
+ Objects.requireNonNull(listener, "Thermal headroom listener cannot be null");
+ Objects.requireNonNull(executor, "Executor cannot be null");
+ synchronized (mThermalHeadroomListenerMap) {
+ Preconditions.checkArgument(!mThermalHeadroomListenerMap.containsKey(listener),
+ "Thermal headroom listener already registered: %s", listener);
+ IThermalHeadroomListener internalListener = new IThermalHeadroomListener.Stub() {
+ @Override
+ public void onHeadroomChange(float headroom, float forecastHeadroom,
+ int forecastSeconds, float[] thresholds)
+ throws RemoteException {
+ final Map<Integer, Float> map = convertThresholdsToMap(thresholds);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onThermalHeadroomChanged(headroom,
+ forecastHeadroom, forecastSeconds, map));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+ try {
+ if (mThermalService.registerThermalHeadroomListener(internalListener)) {
+ mThermalHeadroomListenerMap.put(listener, internalListener);
+ } else {
+ throw new RuntimeException("Thermal headroom listener failed to set");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * This function removes a listener for Thermal headroom change
+ *
+ * @param listener listener to be removed
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK)
+ public void removeThermalHeadroomListener(@NonNull OnThermalHeadroomChangedListener listener) {
+ Objects.requireNonNull(listener, "Thermal headroom listener cannot be null");
+ synchronized (mThermalHeadroomListenerMap) {
+ IThermalHeadroomListener internalListener = mThermalHeadroomListenerMap.get(listener);
+ Preconditions.checkArgument(internalListener != null,
+ "Thermal headroom listener was not added");
+ try {
+ if (mThermalService.unregisterThermalHeadroomListener(internalListener)) {
+ mThermalHeadroomListenerMap.remove(listener);
+ } else {
+ throw new RuntimeException("Failed to unregister thermal status listener");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+
@CurrentTimeMillisLong
private final AtomicLong mLastHeadroomUpdate = new AtomicLong(0L);
private static final int MINIMUM_HEADROOM_TIME_MILLIS = 500;
@@ -2794,7 +2925,8 @@ public final class PowerManager {
* functionality or if this function is called significantly faster than once per
* second.
*/
- public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds) {
+ public @FloatRange(from = 0f) float getThermalHeadroom(
+ @IntRange(from = 0, to = 60) int forecastSeconds) {
// Rate-limit calls into the thermal service
long now = SystemClock.elapsedRealtime();
long timeSinceLastUpdate = now - mLastHeadroomUpdate.get();
@@ -2839,9 +2971,11 @@ public final class PowerManager {
* headroom of 0.75 will never come with {@link #THERMAL_STATUS_MODERATE} but lower, and 0.65
* will never come with {@link #THERMAL_STATUS_LIGHT} but {@link #THERMAL_STATUS_NONE}.
* <p>
- * The returned map of thresholds will not change between calls to this function, so it's
- * best to call this once on initialization. Modifying the result will not change the thresholds
- * cached by the system, and a new call to the API will get a new copy.
+ * Starting at {@link android.os.Build.VERSION_CODES#BAKLAVA} the returned map of thresholds can
+ * change between calls to this function, one could use the new
+ * {@link #addThermalHeadroomListener(Executor, OnThermalHeadroomChangedListener)} API to
+ * register a listener and get callback for changes to thresholds.
+ * <p>
*
* @return map from each thermal status to its thermal headroom
* @throws IllegalStateException if the thermal service is not ready
@@ -2850,24 +2984,22 @@ public final class PowerManager {
@FlaggedApi(Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS)
public @NonNull Map<@ThermalStatus Integer, Float> getThermalHeadroomThresholds() {
try {
- synchronized (mThermalHeadroomThresholdsLock) {
- if (mThermalHeadroomThresholds == null) {
- mThermalHeadroomThresholds = mThermalService.getThermalHeadroomThresholds();
- }
- final ArrayMap<Integer, Float> ret = new ArrayMap<>(THERMAL_STATUS_SHUTDOWN);
- for (int status = THERMAL_STATUS_LIGHT; status <= THERMAL_STATUS_SHUTDOWN;
- status++) {
- if (!Float.isNaN(mThermalHeadroomThresholds[status])) {
- ret.put(status, mThermalHeadroomThresholds[status]);
- }
- }
- return ret;
- }
+ return convertThresholdsToMap(mThermalService.getThermalHeadroomThresholds());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ private Map<@ThermalStatus Integer, Float> convertThresholdsToMap(final float[] thresholds) {
+ final ArrayMap<Integer, Float> ret = new ArrayMap<>(THERMAL_STATUS_SHUTDOWN);
+ for (int status = THERMAL_STATUS_LIGHT; status <= THERMAL_STATUS_SHUTDOWN; status++) {
+ if (!Float.isNaN(thresholds[status])) {
+ ret.put(status, thresholds[status]);
+ }
+ }
+ return ret;
+ }
+
/**
* If true, the doze component is not started until after the screen has been
* turned off and the screen off animation has been performed.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 71d29af6cf02..e7282435ad46 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -29,6 +29,11 @@ import android.annotation.TestApi;
import android.annotation.UptimeMillisLong;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build.VERSION_CODES;
+import android.ravenwood.annotation.RavenwoodKeep;
+import android.ravenwood.annotation.RavenwoodKeepPartialClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.sysprop.MemoryProperties;
import android.system.ErrnoException;
import android.system.Os;
@@ -37,8 +42,6 @@ import android.system.StructPollfd;
import android.util.Pair;
import android.webkit.WebViewZygote;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.Preconditions;
import com.android.sdksandbox.flags.Flags;
import dalvik.system.VMDebug;
@@ -55,6 +58,8 @@ import java.util.concurrent.TimeoutException;
/**
* Tools for managing OS processes.
*/
+@RavenwoodKeepPartialClass
+@RavenwoodRedirectionClass("Process_ravenwood")
public class Process {
private static final String LOG_TAG = "Process";
@@ -671,7 +676,6 @@ public class Process {
*/
public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
-
/**
* The process name set via {@link #setArgV0(String)}.
*/
@@ -845,47 +849,20 @@ public class Process {
/**
* Returns true if the current process is a 64-bit runtime.
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final boolean is64Bit() {
return VMRuntime.getRuntime().is64Bit();
}
- private static volatile ThreadLocal<SomeArgs> sIdentity$ravenwood;
-
- /** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
- public static void init$ravenwood(final int uid, final int pid) {
- sIdentity$ravenwood = ThreadLocal.withInitial(() -> {
- final SomeArgs args = SomeArgs.obtain();
- args.argi1 = uid;
- args.argi2 = pid;
- args.argi3 = Long.hashCode(Thread.currentThread().getId());
- args.argi4 = THREAD_PRIORITY_DEFAULT;
- args.arg1 = Boolean.TRUE; // backgroundOk
- return args;
- });
- }
-
- /** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
- public static void reset$ravenwood() {
- sIdentity$ravenwood = null;
- }
-
/**
* Returns the identifier of this process, which can be used with
* {@link #killProcess} and {@link #sendSignal}.
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodKeep
public static final int myPid() {
return Os.getpid();
}
- /** @hide */
- public static final int myPid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2;
- }
-
/**
* Returns the identifier of this process' parent.
* @hide
@@ -899,39 +876,29 @@ public class Process {
* Returns the identifier of the calling thread, which be used with
* {@link #setThreadPriority(int, int)}.
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodKeep
public static final int myTid() {
return Os.gettid();
}
- /** @hide */
- public static final int myTid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi3;
- }
-
/**
* Returns the identifier of this process's uid. This is the kernel uid
* that the process is running under, which is the identity of its
* app-specific sandbox. It is different from {@link #myUserHandle} in that
* a uid identifies a specific app sandbox in a specific user.
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodKeep
public static final int myUid() {
return Os.getuid();
}
- /** @hide */
- public static final int myUid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1;
- }
-
/**
* Returns this process's user handle. This is the
* user the process is running under. It is distinct from
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static UserHandle myUserHandle() {
return UserHandle.of(UserHandle.getUserId(myUid()));
}
@@ -940,7 +907,7 @@ public class Process {
* Returns whether the given uid belongs to a system core component or not.
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static boolean isCoreUid(int uid) {
return UserHandle.isCore(uid);
}
@@ -951,7 +918,7 @@ public class Process {
* @return Whether the uid corresponds to an application sandbox running in
* a specific user.
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static boolean isApplicationUid(int uid) {
return UserHandle.isApp(uid);
}
@@ -959,7 +926,7 @@ public class Process {
/**
* Returns whether the current process is in an isolated sandbox.
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final boolean isIsolated() {
return isIsolated(myUid());
}
@@ -971,7 +938,7 @@ public class Process {
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
publicAlternatives = "Use {@link #isIsolatedUid(int)} instead.")
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final boolean isIsolated(int uid) {
return isIsolatedUid(uid);
}
@@ -979,7 +946,7 @@ public class Process {
/**
* Returns whether the process with the given {@code uid} is an isolated sandbox.
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final boolean isIsolatedUid(int uid) {
uid = UserHandle.getAppId(uid);
return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
@@ -991,7 +958,7 @@ public class Process {
* @see android.app.sdksandbox.SdkSandboxManager
*/
@SuppressLint("UnflaggedApi") // promoting from @SystemApi.
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final boolean isSdkSandboxUid(int uid) {
uid = UserHandle.getAppId(uid);
return (uid >= FIRST_SDK_SANDBOX_UID && uid <= LAST_SDK_SANDBOX_UID);
@@ -1007,7 +974,7 @@ public class Process {
* @throws IllegalArgumentException if input is not an sdk sandbox uid
*/
@SuppressLint("UnflaggedApi") // promoting from @SystemApi.
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final int getAppUidForSdkSandboxUid(int uid) {
if (!isSdkSandboxUid(uid)) {
throw new IllegalArgumentException("Input UID is not an SDK sandbox UID");
@@ -1023,7 +990,7 @@ public class Process {
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
// TODO(b/318651609): Deprecate once Process#getSdkSandboxUidForAppUid is rolled out to 100%
public static final int toSdkSandboxUid(int uid) {
return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
@@ -1039,7 +1006,7 @@ public class Process {
* @throws IllegalArgumentException if input is not an app uid
*/
@FlaggedApi(Flags.FLAG_SDK_SANDBOX_UID_TO_APP_UID_API)
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final int getSdkSandboxUidForAppUid(int uid) {
if (!isApplicationUid(uid)) {
throw new IllegalArgumentException("Input UID is not an app UID");
@@ -1050,7 +1017,7 @@ public class Process {
/**
* Returns whether the current process is a sdk sandbox process.
*/
- @android.ravenwood.annotation.RavenwoodKeep
+ @RavenwoodKeep
public static final boolean isSdkSandbox() {
return isSdkSandboxUid(myUid());
}
@@ -1127,28 +1094,11 @@ public class Process {
* not have permission to modify the given thread, or to use the given
* priority.
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodRedirect
public static final native void setThreadPriority(int tid,
@IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority)
throws IllegalArgumentException, SecurityException;
- /** @hide */
- public static final void setThreadPriority$ravenwood(int tid, int priority) {
- final SomeArgs args =
- Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
- if (args.argi3 == tid) {
- boolean backgroundOk = (args.arg1 == Boolean.TRUE);
- if (priority >= THREAD_PRIORITY_BACKGROUND && !backgroundOk) {
- throw new IllegalArgumentException(
- "Priority " + priority + " blocked by setCanSelfBackground()");
- }
- args.argi4 = priority;
- } else {
- throw new UnsupportedOperationException(
- "Cross-thread priority management not yet available in Ravenwood");
- }
- }
-
/**
* Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to
* throw an exception if passed a background-level thread priority. This is only
@@ -1156,16 +1106,9 @@ public class Process {
*
* @hide
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodRedirect
public static final native void setCanSelfBackground(boolean backgroundOk);
- /** @hide */
- public static final void setCanSelfBackground$ravenwood(boolean backgroundOk) {
- final SomeArgs args =
- Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
- args.arg1 = Boolean.valueOf(backgroundOk);
- }
-
/**
* Sets the scheduling group for a thread.
* @hide
@@ -1294,13 +1237,12 @@ public class Process {
*
* @see #setThreadPriority(int, int)
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodReplace
public static final native void setThreadPriority(
@IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority)
throws IllegalArgumentException, SecurityException;
- /** @hide */
- public static final void setThreadPriority$ravenwood(int priority) {
+ private static void setThreadPriority$ravenwood(int priority) {
setThreadPriority(myTid(), priority);
}
@@ -1317,23 +1259,11 @@ public class Process {
* @throws IllegalArgumentException Throws IllegalArgumentException if
* <var>tid</var> does not exist.
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @RavenwoodRedirect
@IntRange(from = -20, to = THREAD_PRIORITY_LOWEST)
public static final native int getThreadPriority(int tid)
throws IllegalArgumentException;
- /** @hide */
- public static final int getThreadPriority$ravenwood(int tid) {
- final SomeArgs args =
- Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
- if (args.argi3 == tid) {
- return args.argi4;
- } else {
- throw new UnsupportedOperationException(
- "Cross-thread priority management not yet available in Ravenwood");
- }
- }
-
/**
* Return the current scheduling policy of a thread, based on Linux.
*
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 01b1e5e11332..91c482faf7d7 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -194,15 +194,27 @@ public class RemoteCallbackList<E extends IInterface> {
}
}
- public void maybeSubscribeToFrozenCallback() throws RemoteException {
+ void maybeSubscribeToFrozenCallback() throws RemoteException {
if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- mBinder.addFrozenStateChangeCallback(this);
+ try {
+ mBinder.addFrozenStateChangeCallback(this);
+ } catch (UnsupportedOperationException e) {
+ // The kernel does not support frozen notifications. In this case we want to
+ // silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply
+ // ignoring the error and moving on. mCurrentState would always be
+ // STATE_UNFROZEN and all callbacks are invoked immediately.
+ }
}
}
- public void maybeUnsubscribeFromFrozenCallback() {
+ void maybeUnsubscribeFromFrozenCallback() {
if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- mBinder.removeFrozenStateChangeCallback(this);
+ try {
+ mBinder.removeFrozenStateChangeCallback(this);
+ } catch (UnsupportedOperationException e) {
+ // The kernel does not support frozen notifications. Ignore the error and move
+ // on.
+ }
}
}
diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
index db323dcf9009..435c34f832c6 100644
--- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
@@ -52,7 +52,7 @@ import java.util.concurrent.atomic.AtomicLong;
* {@link Looper#myQueue() Looper.myQueue()}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("MessageQueue_host")
+@RavenwoodRedirectionClass("MessageQueue_ravenwood")
public final class MessageQueue {
private static final String TAG = "SemiConcurrentMessageQueue";
private static final boolean DEBUG = false;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 90993e1850d4..edeb75b6193d 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -365,7 +365,7 @@ public final class StrictMode {
public static final int NETWORK_POLICY_REJECT = 2;
/**
- * Detect explicit calls to {@link Runtime#gc()}.
+ * Detects explicit calls to {@link Runtime#gc()}.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -501,7 +501,7 @@ public final class StrictMode {
private Executor mExecutor;
/**
- * Create a Builder that detects nothing and has no violations. (but note that {@link
+ * Creates a Builder that detects nothing and has no violations. (but note that {@link
* #build} will default to enabling {@link #penaltyLog} if no other penalties are
* specified)
*/
@@ -509,7 +509,7 @@ public final class StrictMode {
mMask = 0;
}
- /** Initialize a Builder from an existing ThreadPolicy. */
+ /** Initializes a Builder from an existing ThreadPolicy. */
public Builder(ThreadPolicy policy) {
mMask = policy.mask;
mListener = policy.mListener;
@@ -517,7 +517,7 @@ public final class StrictMode {
}
/**
- * Detect everything that's potentially suspect.
+ * Detects everything that's potentially suspect.
*
* <p>As of the Gingerbread release this includes network and disk operations but will
* likely expand in future releases.
@@ -544,52 +544,52 @@ public final class StrictMode {
return this;
}
- /** Disable the detection of everything. */
+ /** Disables the detection of everything. */
public @NonNull Builder permitAll() {
return disable(DETECT_THREAD_ALL);
}
- /** Enable detection of network operations. */
+ /** Enables detection of network operations. */
public @NonNull Builder detectNetwork() {
return enable(DETECT_THREAD_NETWORK);
}
- /** Disable detection of network operations. */
+ /** Disables detection of network operations. */
public @NonNull Builder permitNetwork() {
return disable(DETECT_THREAD_NETWORK);
}
- /** Enable detection of disk reads. */
+ /** Enables detection of disk reads. */
public @NonNull Builder detectDiskReads() {
return enable(DETECT_THREAD_DISK_READ);
}
- /** Disable detection of disk reads. */
+ /** Disables detection of disk reads. */
public @NonNull Builder permitDiskReads() {
return disable(DETECT_THREAD_DISK_READ);
}
- /** Enable detection of slow calls. */
+ /** Enables detection of slow calls. */
public @NonNull Builder detectCustomSlowCalls() {
return enable(DETECT_THREAD_CUSTOM);
}
- /** Disable detection of slow calls. */
+ /** Disables detection of slow calls. */
public @NonNull Builder permitCustomSlowCalls() {
return disable(DETECT_THREAD_CUSTOM);
}
- /** Disable detection of mismatches between defined resource types and getter calls. */
+ /** Disables detection of mismatches between defined resource types and getter calls. */
public @NonNull Builder permitResourceMismatches() {
return disable(DETECT_THREAD_RESOURCE_MISMATCH);
}
- /** Detect unbuffered input/output operations. */
+ /** Detects unbuffered input/output operations. */
public @NonNull Builder detectUnbufferedIo() {
return enable(DETECT_THREAD_UNBUFFERED_IO);
}
- /** Disable detection of unbuffered input/output operations. */
+ /** Disables detection of unbuffered input/output operations. */
public @NonNull Builder permitUnbufferedIo() {
return disable(DETECT_THREAD_UNBUFFERED_IO);
}
@@ -610,32 +610,32 @@ public final class StrictMode {
return enable(DETECT_THREAD_RESOURCE_MISMATCH);
}
- /** Enable detection of disk writes. */
+ /** Enables detection of disk writes. */
public @NonNull Builder detectDiskWrites() {
return enable(DETECT_THREAD_DISK_WRITE);
}
- /** Disable detection of disk writes. */
+ /** Disables detection of disk writes. */
public @NonNull Builder permitDiskWrites() {
return disable(DETECT_THREAD_DISK_WRITE);
}
/**
- * Detect calls to {@link Runtime#gc()}.
+ * Detects calls to {@link Runtime#gc()}.
*/
public @NonNull Builder detectExplicitGc() {
return enable(DETECT_THREAD_EXPLICIT_GC);
}
/**
- * Disable detection of calls to {@link Runtime#gc()}.
+ * Disables detection of calls to {@link Runtime#gc()}.
*/
public @NonNull Builder permitExplicitGc() {
return disable(DETECT_THREAD_EXPLICIT_GC);
}
/**
- * Show an annoying dialog to the developer on detected violations, rate-limited to be
+ * Shows an annoying dialog to the developer on detected violations, rate-limited to be
* only a little annoying.
*/
public @NonNull Builder penaltyDialog() {
@@ -643,7 +643,7 @@ public final class StrictMode {
}
/**
- * Crash the whole process on violation. This penalty runs at the end of all enabled
+ * Crashes the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get see logging or other violations before the process
* dies.
*
@@ -655,7 +655,7 @@ public final class StrictMode {
}
/**
- * Crash the whole process on any network usage. Unlike {@link #penaltyDeath}, this
+ * Crashes the whole process on any network usage. Unlike {@link #penaltyDeath}, this
* penalty runs <em>before</em> anything else. You must still have called {@link
* #detectNetwork} to enable this.
*
@@ -665,18 +665,18 @@ public final class StrictMode {
return enable(PENALTY_DEATH_ON_NETWORK);
}
- /** Flash the screen during a violation. */
+ /** Flashes the screen during a violation. */
public @NonNull Builder penaltyFlashScreen() {
return enable(PENALTY_FLASH);
}
- /** Log detected violations to the system log. */
+ /** Logs detected violations to the system log. */
public @NonNull Builder penaltyLog() {
return enable(PENALTY_LOG);
}
/**
- * Enable detected violations log a stacktrace and timing data to the {@link
+ * Enables detected violations log a stacktrace and timing data to the {@link
* android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform
* integrators doing beta user field data collection.
*/
@@ -685,7 +685,7 @@ public final class StrictMode {
}
/**
- * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
+ * Calls #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
* executor every violation.
*/
public @NonNull Builder penaltyListener(
@@ -715,7 +715,7 @@ public final class StrictMode {
}
/**
- * Construct the ThreadPolicy instance.
+ * Constructs the ThreadPolicy instance.
*
* <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
* #penaltyLog} is implicitly set.
@@ -805,7 +805,7 @@ public final class StrictMode {
mMask = 0;
}
- /** Build upon an existing VmPolicy. */
+ /** Builds upon an existing VmPolicy. */
public Builder(VmPolicy base) {
mMask = base.mask;
mClassInstanceLimitNeedCow = true;
@@ -815,7 +815,7 @@ public final class StrictMode {
}
/**
- * Set an upper bound on how many instances of a class can be in memory at once. Helps
+ * Sets an upper bound on how many instances of a class can be in memory at once. Helps
* to prevent object leaks.
*/
public @NonNull Builder setClassInstanceLimit(Class klass, int instanceLimit) {
@@ -838,7 +838,7 @@ public final class StrictMode {
return this;
}
- /** Detect leaks of {@link android.app.Activity} subclasses. */
+ /** Detects leaks of {@link android.app.Activity} subclasses. */
public @NonNull Builder detectActivityLeaks() {
return enable(DETECT_VM_ACTIVITY_LEAKS);
}
@@ -852,7 +852,7 @@ public final class StrictMode {
}
/**
- * Detect reflective usage of APIs that are not part of the public Android SDK.
+ * Detects reflective usage of APIs that are not part of the public Android SDK.
*
* <p>Note that any non-SDK APIs that this processes accesses before this detection is
* enabled may not be detected. To ensure that all such API accesses are detected,
@@ -863,7 +863,7 @@ public final class StrictMode {
}
/**
- * Permit reflective usage of APIs that are not part of the public Android SDK. Note
+ * Permits reflective usage of APIs that are not part of the public Android SDK. Note
* that this <b>only</b> affects {@code StrictMode}, the underlying runtime may
* continue to restrict or warn on access to methods that are not part of the
* public SDK.
@@ -873,7 +873,7 @@ public final class StrictMode {
}
/**
- * Detect everything that's potentially suspect.
+ * Detects everything that's potentially suspect.
*
* <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
* other closable objects but will likely expand in future releases.
@@ -924,8 +924,8 @@ public final class StrictMode {
}
/**
- * Detect when an {@link android.database.sqlite.SQLiteCursor} or other SQLite object is
- * finalized without having been closed.
+ * Detects when an {@link android.database.sqlite.SQLiteCursor} or other SQLite
+ * object is finalized without having been closed.
*
* <p>You always want to explicitly close your SQLite cursors to avoid unnecessary
* database contention and temporary memory leaks.
@@ -935,8 +935,8 @@ public final class StrictMode {
}
/**
- * Detect when an {@link java.io.Closeable} or other object with an explicit termination
- * method is finalized without having been closed.
+ * Detects when an {@link java.io.Closeable} or other object with an explicit
+ * termination method is finalized without having been closed.
*
* <p>You always want to explicitly close such objects to avoid unnecessary resources
* leaks.
@@ -946,16 +946,16 @@ public final class StrictMode {
}
/**
- * Detect when a {@link BroadcastReceiver} or {@link ServiceConnection} is leaked during
- * {@link Context} teardown.
+ * Detects when a {@link BroadcastReceiver} or {@link ServiceConnection} is leaked
+ * during {@link Context} teardown.
*/
public @NonNull Builder detectLeakedRegistrationObjects() {
return enable(DETECT_VM_REGISTRATION_LEAKS);
}
/**
- * Detect when the calling application exposes a {@code file://} {@link android.net.Uri}
- * to another app.
+ * Detects when the calling application exposes a {@code file://}
+ * {@link android.net.Uri} to another app.
*
* <p>This exposure is discouraged since the receiving app may not have access to the
* shared path. For example, the receiving app may not have requested the {@link
@@ -973,9 +973,9 @@ public final class StrictMode {
}
/**
- * Detect any network traffic from the calling app which is not wrapped in SSL/TLS. This
- * can help you detect places that your app is inadvertently sending cleartext data
- * across the network.
+ * Detects any network traffic from the calling app which is not wrapped in SSL/TLS.
+ * This can help you detect places that your app is inadvertently sending cleartext
+ * data across the network.
*
* <p>Using {@link #penaltyDeath()} or {@link #penaltyDeathOnCleartextNetwork()} will
* block further traffic on that socket to prevent accidental data leakage, in addition
@@ -992,7 +992,7 @@ public final class StrictMode {
}
/**
- * Detect when the calling application sends a {@code content://} {@link
+ * Detects when the calling application sends a {@code content://} {@link
* android.net.Uri} to another app without setting {@link
* Intent#FLAG_GRANT_READ_URI_PERMISSION} or {@link
* Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
@@ -1008,7 +1008,7 @@ public final class StrictMode {
}
/**
- * Detect any sockets in the calling app which have not been tagged using {@link
+ * Detects any sockets in the calling app which have not been tagged using {@link
* TrafficStats}. Tagging sockets can help you investigate network usage inside your
* app, such as a narrowing down heavy usage to a specific library or component.
*
@@ -1028,7 +1028,7 @@ public final class StrictMode {
}
/**
- * Detect any implicit reliance on Direct Boot automatic filtering
+ * Detects any implicit reliance on Direct Boot automatic filtering
* of {@link PackageManager} values. Violations are only triggered
* when implicit calls are made while the user is locked.
* <p>
@@ -1051,7 +1051,7 @@ public final class StrictMode {
}
/**
- * Detect access to filesystem paths stored in credential protected
+ * Detects access to filesystem paths stored in credential protected
* storage areas while the user is locked.
* <p>
* When a user is locked, credential protected storage is
@@ -1072,7 +1072,7 @@ public final class StrictMode {
}
/**
- * Detect attempts to invoke a method on a {@link Context} that is not suited for such
+ * Detects attempts to invoke a method on a {@link Context} that is not suited for such
* operation.
* <p>An example of this is trying to obtain an instance of UI service (e.g.
* {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
@@ -1086,7 +1086,7 @@ public final class StrictMode {
}
/**
- * Disable detection of incorrect context use.
+ * Disables detection of incorrect context use.
*
* @see #detectIncorrectContextUse()
*
@@ -1098,7 +1098,7 @@ public final class StrictMode {
}
/**
- * Detect when your app sends an unsafe {@link Intent}.
+ * Detects when your app sends an unsafe {@link Intent}.
* <p>
* Violations may indicate security vulnerabilities in the design of
* your app, where a malicious app could trick you into granting
@@ -1139,7 +1139,7 @@ public final class StrictMode {
}
/**
- * Permit your app to launch any {@link Intent} which originated
+ * Permits your app to launch any {@link Intent} which originated
* from outside your app.
* <p>
* Disabling this check is <em>strongly discouraged</em>, as
@@ -1214,13 +1214,13 @@ public final class StrictMode {
return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
}
- /** Log detected violations to the system log. */
+ /** Logs detected violations to the system log. */
public @NonNull Builder penaltyLog() {
return enable(PENALTY_LOG);
}
/**
- * Enable detected violations log a stacktrace and timing data to the {@link
+ * Enables detected violations log a stacktrace and timing data to the {@link
* android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform
* integrators doing beta user field data collection.
*/
@@ -1229,7 +1229,7 @@ public final class StrictMode {
}
/**
- * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
+ * Calls #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
*/
public @NonNull Builder penaltyListener(
@NonNull Executor executor, @NonNull OnVmViolationListener listener) {
@@ -1258,7 +1258,7 @@ public final class StrictMode {
}
/**
- * Construct the VmPolicy instance.
+ * Constructs the VmPolicy instance.
*
* <p>Note: if no penalties are enabled before calling <code>build</code>, {@link
* #penaltyLog} is implicitly set.
@@ -1474,7 +1474,7 @@ public final class StrictMode {
}
/**
- * Determine if the given app is "bundled" as part of the system image. These bundled apps are
+ * Determines if the given app is "bundled" as part of the system image. These bundled apps are
* developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to
* chase any {@link StrictMode} regressions by enabling detection when running on {@link
* Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
@@ -1512,7 +1512,7 @@ public final class StrictMode {
}
/**
- * Initialize default {@link ThreadPolicy} for the current thread.
+ * Initializes default {@link ThreadPolicy} for the current thread.
*
* @hide
*/
@@ -1547,7 +1547,7 @@ public final class StrictMode {
}
/**
- * Initialize default {@link VmPolicy} for the current VM.
+ * Initializes default {@link VmPolicy} for the current VM.
*
* @hide
*/
@@ -2244,7 +2244,7 @@ public final class StrictMode {
}
/**
- * Enable the recommended StrictMode defaults, with violations just being logged.
+ * Enables the recommended StrictMode defaults, with violations just being logged.
*
* <p>This catches disk and network access on the main thread, as well as leaked SQLite cursors
* and unclosed resources. This is simply a wrapper around {@link #setVmPolicy} and {@link
@@ -2545,7 +2545,7 @@ public final class StrictMode {
private static final SparseLongArray sRealLastVmViolationTime = new SparseLongArray();
/**
- * Clamp the given map by removing elements with timestamp older than the given retainSince.
+ * Clamps the given map by removing elements with timestamp older than the given retainSince.
*/
private static void clampViolationTimeMap(final @NonNull SparseLongArray violationTime,
final long retainSince) {
@@ -2812,7 +2812,7 @@ public final class StrictMode {
};
/**
- * Enter a named critical span (e.g. an animation)
+ * Enters a named critical span (e.g. an animation)
*
* <p>The name is an arbitary label (or tag) that will be applied to any strictmode violation
* that happens while this span is active. You must call finish() on the span when done.
@@ -3056,7 +3056,7 @@ public final class StrictMode {
/** If this is a instance count violation, the number of instances in memory, else -1. */
public long numInstances = -1;
- /** Create an instance of ViolationInfo initialized from an exception. */
+ /** Creates an instance of ViolationInfo initialized from an exception. */
ViolationInfo(Violation tr, int penaltyMask) {
this.mViolation = tr;
this.mPenaltyMask = penaltyMask;
@@ -3131,8 +3131,8 @@ public final class StrictMode {
}
/**
- * Add a {@link Throwable} from the current process that caused the underlying violation. We
- * only preserve the stack trace elements.
+ * Adds a {@link Throwable} from the current process that caused the underlying violation.
+ * We only preserve the stack trace elements.
*
* @hide
*/
@@ -3160,14 +3160,14 @@ public final class StrictMode {
return result;
}
- /** Create an instance of ViolationInfo initialized from a Parcel. */
+ /** Creates an instance of ViolationInfo initialized from a Parcel. */
@UnsupportedAppUsage
public ViolationInfo(Parcel in) {
this(in, false);
}
/**
- * Create an instance of ViolationInfo initialized from a Parcel.
+ * Creates an instance of ViolationInfo initialized from a Parcel.
*
* @param unsetGatheringBit if true, the caller is the root caller and the gathering penalty
* should be removed.
@@ -3203,7 +3203,7 @@ public final class StrictMode {
tags = in.readStringArray();
}
- /** Save a ViolationInfo instance to a parcel. */
+ /** Saves a ViolationInfo instance to a parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(mViolation);
@@ -3248,7 +3248,7 @@ public final class StrictMode {
}
}
- /** Dump a ViolationInfo instance to a Printer. */
+ /** Dumps a ViolationInfo instance to a Printer. */
public void dump(Printer pw, String prefix) {
pw.println(prefix + "stackTrace: " + getStackTrace());
pw.println(prefix + "penalty: " + mPenaltyMask);
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 011a3ee91ada..c3cddf32f063 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -18,8 +18,11 @@ package android.os;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.vibrator.IVibratorManager;
+import android.os.vibrator.VendorVibrationSession;
import android.os.vibrator.VibratorInfoFactory;
import android.util.ArrayMap;
import android.util.Log;
@@ -53,6 +56,7 @@ public class SystemVibrator extends Vibrator {
private final Object mLock = new Object();
@GuardedBy("mLock")
private VibratorInfo mVibratorInfo;
+ private int[] mVibratorIds;
@UnsupportedAppUsage
public SystemVibrator(Context context) {
@@ -71,7 +75,11 @@ public class SystemVibrator extends Vibrator {
Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager.");
return VibratorInfo.EMPTY_VIBRATOR_INFO;
}
- int[] vibratorIds = mVibratorManager.getVibratorIds();
+ int[] vibratorIds = getVibratorIds();
+ if (vibratorIds == null) {
+ Log.w(TAG, "Failed to retrieve vibrator info; error retrieving vibrator ids.");
+ return VibratorInfo.EMPTY_VIBRATOR_INFO;
+ }
if (vibratorIds.length == 0) {
// It is known that the device has no vibrator, so cache and return info that
// reflects the lack of support for effects/primitives.
@@ -95,20 +103,22 @@ public class SystemVibrator extends Vibrator {
@Override
public boolean hasVibrator() {
- if (mVibratorManager == null) {
+ int[] vibratorIds = getVibratorIds();
+ if (vibratorIds == null) {
Log.w(TAG, "Failed to check if vibrator exists; no vibrator manager.");
return false;
}
- return mVibratorManager.getVibratorIds().length > 0;
+ return vibratorIds.length > 0;
}
@Override
public boolean isVibrating() {
- if (mVibratorManager == null) {
+ int[] vibratorIds = getVibratorIds();
+ if (vibratorIds == null) {
Log.w(TAG, "Failed to vibrate; no vibrator manager.");
return false;
}
- for (int vibratorId : mVibratorManager.getVibratorIds()) {
+ for (int vibratorId : vibratorIds) {
if (mVibratorManager.getVibrator(vibratorId).isVibrating()) {
return true;
}
@@ -136,6 +146,11 @@ public class SystemVibrator extends Vibrator {
Log.w(TAG, "Failed to add vibrate state listener; no vibrator manager.");
return;
}
+ int[] vibratorIds = getVibratorIds();
+ if (vibratorIds == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; error retrieving vibrator ids.");
+ return;
+ }
MultiVibratorStateListener delegate = null;
try {
synchronized (mRegisteredListeners) {
@@ -145,7 +160,7 @@ public class SystemVibrator extends Vibrator {
return;
}
delegate = new MultiVibratorStateListener(executor, listener);
- delegate.register(mVibratorManager);
+ delegate.register(mVibratorManager, vibratorIds);
mRegisteredListeners.put(listener, delegate);
delegate = null;
}
@@ -184,6 +199,11 @@ public class SystemVibrator extends Vibrator {
}
@Override
+ public boolean areVendorSessionsSupported() {
+ return mVibratorManager.hasCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ }
+
+ @Override
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
VibrationAttributes attrs) {
if (mVibratorManager == null) {
@@ -243,6 +263,41 @@ public class SystemVibrator extends Vibrator {
mVibratorManager.cancel(usageFilter);
}
+ @Override
+ public void startVendorSession(@NonNull VibrationAttributes attrs, @Nullable String reason,
+ @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor,
+ @NonNull VendorVibrationSession.Callback callback) {
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to start vibration session; no vibrator manager.");
+ executor.execute(
+ () -> callback.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR));
+ return;
+ }
+ int[] vibratorIds = getVibratorIds();
+ if (vibratorIds == null) {
+ Log.w(TAG, "Failed to start vibration session; error retrieving vibrator ids.");
+ executor.execute(
+ () -> callback.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR));
+ return;
+ }
+ mVibratorManager.startVendorSession(vibratorIds, attrs, reason, cancellationSignal,
+ executor, callback);
+ }
+
+ @Nullable
+ private int[] getVibratorIds() {
+ synchronized (mLock) {
+ if (mVibratorIds != null) {
+ return mVibratorIds;
+ }
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager.");
+ return null;
+ }
+ return mVibratorIds = mVibratorManager.getVibratorIds();
+ }
+ }
+
/**
* Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener}
* that were left registered to vibrators after failures to register them to all vibrators.
@@ -319,8 +374,7 @@ public class SystemVibrator extends Vibrator {
}
/** Registers a listener to all individual vibrators in {@link VibratorManager}. */
- public void register(VibratorManager vibratorManager) {
- int[] vibratorIds = vibratorManager.getVibratorIds();
+ public void register(VibratorManager vibratorManager, @NonNull int[] vibratorIds) {
synchronized (mLock) {
for (int i = 0; i < vibratorIds.length; i++) {
int vibratorId = vibratorIds[i];
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index a5697fb0e8a8..f9935d2870b0 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -22,6 +22,10 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.vibrator.IVibratorManager;
+import android.os.vibrator.IVibrationSession;
+import android.os.vibrator.IVibrationSessionCallback;
+import android.os.vibrator.VendorVibrationSession;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
@@ -47,6 +51,8 @@ public class SystemVibratorManager extends VibratorManager {
@GuardedBy("mLock")
private int[] mVibratorIds;
@GuardedBy("mLock")
+ private int mCapabilities;
+ @GuardedBy("mLock")
private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
@GuardedBy("mLock")
@@ -84,6 +90,11 @@ public class SystemVibratorManager extends VibratorManager {
}
}
+ @Override
+ public boolean hasCapabilities(int capabilities) {
+ return (getCapabilities() & capabilities) == capabilities;
+ }
+
@NonNull
@Override
public Vibrator getVibrator(int vibratorId) {
@@ -173,7 +184,7 @@ public class SystemVibratorManager extends VibratorManager {
int inputSource, String reason, int flags, int privFlags) {
if (mService == null) {
Log.w(TAG, "Failed to perform haptic feedback for input device;"
- + " no vibrator manager service.");
+ + " no vibrator manager service.");
return;
}
Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice");
@@ -197,6 +208,50 @@ public class SystemVibratorManager extends VibratorManager {
cancelVibration(usageFilter);
}
+ @Override
+ public void startVendorSession(@NonNull int[] vibratorIds, @NonNull VibrationAttributes attrs,
+ @Nullable String reason, @Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor executor, @NonNull VendorVibrationSession.Callback callback) {
+ Objects.requireNonNull(vibratorIds);
+ VendorVibrationSessionCallbackDelegate callbackDelegate =
+ new VendorVibrationSessionCallbackDelegate(executor, callback);
+ if (mService == null) {
+ Log.w(TAG, "Failed to start vibration session; no vibrator manager service.");
+ callbackDelegate.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR);
+ return;
+ }
+ try {
+ ICancellationSignal remoteCancellationSignal = mService.startVendorVibrationSession(
+ mUid, mContext.getDeviceId(), mPackageName, vibratorIds, attrs, reason,
+ callbackDelegate);
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(remoteCancellationSignal);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to start vibration session.", e);
+ callbackDelegate.onFinished(VendorVibrationSession.STATUS_UNKNOWN_ERROR);
+ }
+ }
+
+ private int getCapabilities() {
+ synchronized (mLock) {
+ if (mCapabilities != 0) {
+ return mCapabilities;
+ }
+ try {
+ if (mService == null) {
+ Log.w(TAG, "Failed to retrieve vibrator manager capabilities;"
+ + " no vibrator manager service.");
+ } else {
+ return mCapabilities = mService.getCapabilities();
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return 0;
+ }
+ }
+
private void cancelVibration(int usageFilter) {
if (mService == null) {
Log.w(TAG, "Failed to cancel vibration; no vibrator manager service.");
@@ -228,12 +283,45 @@ public class SystemVibratorManager extends VibratorManager {
}
}
+ /** Callback for vendor vibration sessions. */
+ private static class VendorVibrationSessionCallbackDelegate extends
+ IVibrationSessionCallback.Stub {
+ private final Executor mExecutor;
+ private final VendorVibrationSession.Callback mCallback;
+
+ VendorVibrationSessionCallbackDelegate(
+ @NonNull Executor executor,
+ @NonNull VendorVibrationSession.Callback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onStarted(IVibrationSession session) {
+ mExecutor.execute(() -> mCallback.onStarted(new VendorVibrationSession(session)));
+ }
+
+ @Override
+ public void onFinishing() {
+ mExecutor.execute(() -> mCallback.onFinishing());
+ }
+
+ @Override
+ public void onFinished(int status) {
+ mExecutor.execute(() -> mCallback.onFinished(status));
+ }
+ }
+
/** Controls vibrations on a single vibrator. */
private final class SingleVibrator extends Vibrator {
private final VibratorInfo mVibratorInfo;
+ private final int[] mVibratorId;
SingleVibrator(@NonNull VibratorInfo vibratorInfo) {
mVibratorInfo = vibratorInfo;
+ mVibratorId = new int[]{mVibratorInfo.getId()};
}
@Override
@@ -252,6 +340,11 @@ public class SystemVibratorManager extends VibratorManager {
}
@Override
+ public boolean areVendorSessionsSupported() {
+ return SystemVibratorManager.this.hasCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ }
+
+ @Override
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
@Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs) {
CombinedVibration combined = CombinedVibration.startParallel()
@@ -369,5 +462,13 @@ public class SystemVibratorManager extends VibratorManager {
}
}
}
+
+ @Override
+ public void startVendorSession(@NonNull VibrationAttributes attrs, String reason,
+ @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor,
+ @NonNull VendorVibrationSession.Callback callback) {
+ SystemVibratorManager.this.startVendorSession(mVibratorId, attrs, reason,
+ cancellationSignal, executor, callback);
+ }
}
}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index f893739fcf28..976bfe41ba45 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -210,12 +210,6 @@ public final class UidBatteryConsumer extends BatteryConsumer {
serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE,
packageWithHighestDrain);
}
- serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
- getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND));
- serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
- getTimeInProcessStateMs(PROCESS_STATE_BACKGROUND));
- serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE,
- getTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE));
mPowerComponents.writeToXml(serializer);
serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
}
@@ -235,13 +229,6 @@ public final class UidBatteryConsumer extends BatteryConsumer {
consumerBuilder.setPackageWithHighestDrain(
parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
- consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND,
- parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
- consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND,
- parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
- consumerBuilder.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
- parser.getAttributeLong(null,
- BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND_SERVICE));
while (!(eventType == XmlPullParser.END_TAG
&& parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
&& eventType != XmlPullParser.END_DOCUMENT) {
@@ -335,7 +322,11 @@ public final class UidBatteryConsumer extends BatteryConsumer {
/**
* Sets the duration, in milliseconds, that this UID was active in a particular process
* state, such as foreground service.
+ *
+ * @deprecated time in process is now derived from the
+ * {@link BatteryConsumer#POWER_COMPONENT_BASE} duration
*/
+ @Deprecated
@NonNull
public Builder setTimeInProcessStateMs(@ProcessState int state, long timeInProcessStateMs) {
Key key = getKey(POWER_COMPONENT_BASE, state);
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index c4c4580bf0a8..53f8a9267499 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -33,6 +33,7 @@ import android.content.res.Resources;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.os.vibrator.Flags;
+import android.os.vibrator.VendorVibrationSession;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibratorFrequencyProfile;
import android.os.vibrator.VibratorFrequencyProfileLegacy;
@@ -247,6 +248,34 @@ public abstract class Vibrator {
}
/**
+ * Check whether the vibrator has support for vendor-specific effects.
+ *
+ * <p>Vendor vibration effects can be created via {@link VibrationEffect#createVendorEffect}.
+ *
+ * @return True if the hardware can play vendor-specific vibration effects, false otherwise.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public boolean areVendorEffectsSupported() {
+ return getInfo().hasCapability(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+ }
+
+ /**
+ * Check whether the vibrator has support for vendor-specific vibration sessions.
+ *
+ * <p>Vendor vibration sessions can be started via {@link #startVendorSession}.
+ *
+ * @return True if the hardware can play vendor-specific vibration sessions, false otherwise.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public boolean areVendorSessionsSupported() {
+ return false;
+ }
+
+ /**
* Check whether the vibrator can be controlled by an external service with the
* {@link IExternalVibratorService}.
*
@@ -922,4 +951,44 @@ public abstract class Vibrator {
@RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
}
+
+ /**
+ * Starts a vibration session in this vibrator.
+ *
+ * <p>The session will start asynchronously once the vibrator control can be acquired. Once it's
+ * started the {@link VendorVibrationSession} will be provided to the callback. This session
+ * should be used to play vibrations until the session is ended or canceled.
+ *
+ * <p>The vendor app will have exclusive control over the vibrator during this session. This
+ * control can be revoked by the vibrator service, which will be notified to the same session
+ * callback with the {@link VendorVibrationSession#STATUS_CANCELED}.
+ *
+ * <p>The {@link VibrationAttributes} will be used to decide the priority of the vendor
+ * vibrations that will be performed in this session. All vibrations within this session will
+ * apply the same attributes.
+ *
+ * @param attrs The {@link VibrationAttributes} corresponding to the vibrations that will be
+ * performed in the session. This will be used to decide the priority of this
+ * session against other system vibrations.
+ * @param reason The description for this session, used for debugging purposes.
+ * @param cancellationSignal A signal to cancel the session before it starts.
+ * @param executor The executor for the session callbacks.
+ * @param callback The {@link VendorVibrationSession.Callback} for the started session.
+ *
+ * @see VendorVibrationSession
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.VIBRATE,
+ android.Manifest.permission.VIBRATE_VENDOR_EFFECTS,
+ android.Manifest.permission.START_VIBRATION_SESSIONS,
+ })
+ public void startVendorSession(@NonNull VibrationAttributes attrs, @Nullable String reason,
+ @Nullable CancellationSignal cancellationSignal, @NonNull Executor executor,
+ @NonNull VendorVibrationSession.Callback callback) {
+ Log.w(TAG, "startVendorSession is not supported");
+ executor.execute(() -> callback.onFinished(VendorVibrationSession.STATUS_UNSUPPORTED));
+ }
}
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index 0428876891f9..0072bc22ad8f 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -22,9 +22,12 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.ActivityThread;
import android.content.Context;
+import android.os.vibrator.VendorVibrationSession;
import android.util.Log;
import android.view.HapticFeedbackConstants;
+import java.util.concurrent.Executor;
+
/**
* Provides access to all vibrators from the device, as well as the ability to run them
* in a synchronized fashion.
@@ -62,6 +65,14 @@ public abstract class VibratorManager {
public abstract int[] getVibratorIds();
/**
+ * Return true if the vibrator manager has all capabilities, false otherwise.
+ * @hide
+ */
+ public boolean hasCapabilities(int capabilities) {
+ return false;
+ }
+
+ /**
* Retrieve a single vibrator by id.
*
* @param vibratorId The id of the vibrator to be retrieved.
@@ -190,4 +201,30 @@ public abstract class VibratorManager {
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
public abstract void cancel(int usageFilter);
+
+
+ /**
+ * Starts a vibration session on given vibrators.
+ *
+ * @param vibratorIds The vibrators that will be controlled by this session.
+ * @param attrs The {@link VibrationAttributes} corresponding to the vibrations that will
+ * be performed in the session. This will be used to decide the priority of
+ * this session against other system vibrations.
+ * @param reason The description for this session, used for debugging purposes.
+ * @param cancellationSignal A signal to cancel the session before it starts.
+ * @param executor The executor for the session callbacks.
+ * @param callback The {@link VendorVibrationSession.Callback} for the started session.
+ * @see Vibrator#startVendorSession
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.VIBRATE,
+ android.Manifest.permission.VIBRATE_VENDOR_EFFECTS,
+ android.Manifest.permission.START_VIBRATION_SESSIONS,
+ })
+ public void startVendorSession(@NonNull int[] vibratorIds, @NonNull VibrationAttributes attrs,
+ @Nullable String reason, @Nullable CancellationSignal cancellationSignal,
+ @NonNull Executor executor, @NonNull VendorVibrationSession.Callback callback) {
+ Log.w(TAG, "startVendorSession is not supported");
+ }
}
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 6d4e28403908..517418a717fb 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -1011,13 +1011,7 @@ public class WorkSource implements Parcelable {
return mTags.length > 0 ? mTags[0] : null;
}
- // TODO: The following three trivial getters are purely for testing and will be removed
- // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
- // diffing it etc.
-
-
/** @hide */
- @VisibleForTesting
public int[] getUids() {
int[] uids = new int[mSize];
System.arraycopy(mUids, 0, uids, 0, mSize);
@@ -1025,7 +1019,6 @@ public class WorkSource implements Parcelable {
}
/** @hide */
- @VisibleForTesting
public String[] getTags() {
String[] tags = new String[mSize];
System.arraycopy(mTags, 0, tags, 0, mSize);
@@ -1033,7 +1026,6 @@ public class WorkSource implements Parcelable {
}
/** @hide */
- @VisibleForTesting
public int getSize() {
return mSize;
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 9c83bc2c88ec..d9db28e0b3c3 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -243,6 +243,15 @@ flag {
}
flag {
+ name: "update_engine_api"
+ namespace: "art_mainline"
+ description: "Update Engine APIs for ART"
+ is_exported: true
+ is_fixed_read_only: true
+ bug: "377557749"
+}
+
+flag {
namespace: "system_performance"
name: "perfetto_sdk_tracing"
description: "Tracing using Perfetto SDK."
diff --git a/core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl b/core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl
new file mode 100644
index 000000000000..dbe54891b0f2
--- /dev/null
+++ b/core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.instrumentation;
+
+/**
+ * Represents the location of the code for a compiled method within a process'
+ * memory.
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable ExecutableMethodFileOffsets {
+ /**
+ * The OS path of the containing file (could be virtual).
+ */
+ @utf8InCpp String containerPath;
+ /**
+ * The offset of the containing file within the process' memory.
+ */
+ long containerOffset;
+ /**
+ * The offset of the method within the containing file.
+ */
+ long methodOffset;
+}
diff --git a/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
new file mode 100644
index 000000000000..c45c51d15cc9
--- /dev/null
+++ b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.instrumentation;
+
+import android.os.instrumentation.ExecutableMethodFileOffsets;
+import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.TargetProcess;
+
+/**
+ * System private API for managing the dynamic attachment of instrumentation.
+ *
+ * {@hide}
+ */
+interface IDynamicInstrumentationManager {
+ /** Provides ART metadata about the described compiled method within the target process */
+ @PermissionManuallyEnforced
+ @nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets(
+ in TargetProcess targetProcess, in MethodDescriptor methodDescriptor);
+}
diff --git a/core/java/android/os/instrumentation/MethodDescriptor.aidl b/core/java/android/os/instrumentation/MethodDescriptor.aidl
new file mode 100644
index 000000000000..055d0ecb66e4
--- /dev/null
+++ b/core/java/android/os/instrumentation/MethodDescriptor.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.instrumentation;
+
+/**
+ * Represents a JVM method, where class fields that make up its signature.
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable MethodDescriptor {
+ /**
+ * Fully qualified class in reverse.domain.Naming
+ */
+ @utf8InCpp String fullyQualifiedClassName;
+ /**
+ * Name of the method.
+ */
+ @utf8InCpp String methodName;
+ /**
+ * Fully qualified types of method parameters, or string representations if primitive e.g. "int".
+ */
+ @utf8InCpp String[] fullyQualifiedParameters;
+}
diff --git a/core/java/android/os/instrumentation/TargetProcess.aidl b/core/java/android/os/instrumentation/TargetProcess.aidl
new file mode 100644
index 000000000000..e90780d07ef2
--- /dev/null
+++ b/core/java/android/os/instrumentation/TargetProcess.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.instrumentation;
+
+/**
+ * Addresses a process that would run on the device.
+ * Helps disambiguate targeted processes in cases of pid re-use.
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable TargetProcess {
+ int uid;
+ int pid;
+ @utf8InCpp String processName;
+}
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 1ab48a22cbbd..09b96da39a84 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -181,6 +181,5 @@ interface IStorageManager {
* device's useful lifetime remains. If no information is available, -1
* is returned.
*/
- @EnforcePermission("READ_PRIVILEGED_PHONE_STATE")
int getInternalStorageRemainingLifetime() = 99;
}
diff --git a/core/java/android/os/vibrator/IVibrationSession.aidl b/core/java/android/os/vibrator/IVibrationSession.aidl
new file mode 100644
index 000000000000..e8295492665d
--- /dev/null
+++ b/core/java/android/os/vibrator/IVibrationSession.aidl
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.vibrator;
+
+import android.os.CombinedVibration;
+
+/**
+ * The communication channel by which an app control the system vibrators.
+ *
+ * In order to synchronize the places where vibrations might be controlled we provide this interface
+ * so the vibrator subsystem has a chance to:
+ *
+ * 1) Decide whether the current session should have the vibrator control.
+ * 2) Stop any on-going session for a new session/vibration, based on current system policy.
+ * {@hide}
+ */
+interface IVibrationSession {
+ const int STATUS_UNKNOWN = 0;
+ const int STATUS_SUCCESS = 1;
+ const int STATUS_IGNORED = 2;
+ const int STATUS_UNSUPPORTED = 3;
+ const int STATUS_CANCELED = 4;
+ const int STATUS_UNKNOWN_ERROR = 5;
+
+ /**
+ * A method called to start a vibration within this session. This will fail if the session
+ * is finishing or was canceled.
+ */
+ void vibrate(in CombinedVibration vibration, String reason);
+
+ /**
+ * A method called by the app to stop this session gracefully. The vibrator will complete any
+ * ongoing vibration before the session is ended.
+ */
+ void finishSession();
+
+ /**
+ * A method called by the app to stop this session immediatelly by interrupting any ongoing
+ * vibration.
+ */
+ void cancelSession();
+}
diff --git a/core/java/android/os/vibrator/IVibrationSessionCallback.aidl b/core/java/android/os/vibrator/IVibrationSessionCallback.aidl
new file mode 100644
index 000000000000..36c3695a1bfe
--- /dev/null
+++ b/core/java/android/os/vibrator/IVibrationSessionCallback.aidl
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.vibrator;
+
+import android.os.vibrator.IVibrationSession;
+
+/**
+ * Callback for vibration session state.
+ * {@hide}
+ */
+oneway interface IVibrationSessionCallback {
+
+ /**
+ * A method called by the service after a vibration session has successfully started. After this
+ * is called the app has control over the vibrator through this given session.
+ */
+ void onStarted(in IVibrationSession session);
+
+ /**
+ * A method called by the service to indicate the session is ending and should no longer receive
+ * vibration requests.
+ */
+ void onFinishing();
+
+ /**
+ * A method called by the service after the session has ended. This might be triggered by the
+ * app or the service. The status code indicates the end reason.
+ */
+ void onFinished(int status);
+}
diff --git a/core/java/android/os/vibrator/VendorVibrationSession.java b/core/java/android/os/vibrator/VendorVibrationSession.java
new file mode 100644
index 000000000000..c23f2ed1a303
--- /dev/null
+++ b/core/java/android/os/vibrator/VendorVibrationSession.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import static android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.CombinedVibration;
+import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A vendor session that temporarily gains control over the system vibrators.
+ *
+ * <p>Vibration effects can be played by the vibrator in a vendor session via {@link #vibrate}. The
+ * effects will be forwarded to the vibrator hardware immediately. Any concurrency support is
+ * defined and controlled by the vibrator hardware implementation.
+ *
+ * <p>The session should be ended by {@link #close()}, which will wait until the last vibration ends
+ * and the vibrator is released. The end of the session will be notified to the {@link Callback}
+ * provided when the session was created.
+ *
+ * <p>Any ongoing session can be immediately interrupted by the vendor app via {@link #cancel()},
+ * including after {@link #close()} was called and the session is tearing down. A session can also
+ * be canceled by the vibrator service when it needs to regain control of the system vibrators.
+ *
+ * @see Vibrator#startVendorSession
+ * @hide
+ */
+@FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS)
+@SystemApi
+public final class VendorVibrationSession implements AutoCloseable {
+ private static final String TAG = "VendorVibrationSession";
+
+ /**
+ * The session ended successfully.
+ */
+ public static final int STATUS_SUCCESS = IVibrationSession.STATUS_SUCCESS;
+
+ /**
+ * The session was ignored.
+ *
+ * <p>This might be caused by user settings, vibration policies or the device state that
+ * prevents the app from performing vibrations for the requested
+ * {@link android.os.VibrationAttributes}.
+ */
+ public static final int STATUS_IGNORED = IVibrationSession.STATUS_IGNORED;
+
+ /**
+ * The session is not supported.
+ *
+ * <p>The support for vendor vibration sessions can be checked via
+ * {@link Vibrator#areVendorSessionsSupported()}.
+ */
+ public static final int STATUS_UNSUPPORTED = IVibrationSession.STATUS_UNSUPPORTED;
+
+ /**
+ * The session was canceled.
+ *
+ * <p>This might be triggered by the app after a session starts via {@link #cancel()}, or it
+ * can be triggered by the platform before or after the session has started.
+ */
+ public static final int STATUS_CANCELED = IVibrationSession.STATUS_CANCELED;
+
+ /**
+ * The session status is unknown.
+ */
+ public static final int STATUS_UNKNOWN = IVibrationSession.STATUS_UNKNOWN;
+
+ /**
+ * The session failed with unknown error.
+ *
+ * <p>This can be caused by a failure to start a vibration session or after it has started, to
+ * indicate it has ended unexpectedly because of a system failure.
+ */
+ public static final int STATUS_UNKNOWN_ERROR = IVibrationSession.STATUS_UNKNOWN_ERROR;
+
+ /** @hide */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_SUCCESS,
+ STATUS_IGNORED,
+ STATUS_UNSUPPORTED,
+ STATUS_CANCELED,
+ STATUS_UNKNOWN,
+ STATUS_UNKNOWN_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status{}
+
+ private final IVibrationSession mSession;
+
+ /** @hide */
+ public VendorVibrationSession(@NonNull IVibrationSession session) {
+ Objects.requireNonNull(session);
+ mSession = session;
+ }
+
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The vibration will be sent to the vibrator hardware immediately, without waiting for any
+ * previous vibration completion. The vendor should control the concurrency behavior at the
+ * hardware level (e.g. queueing, mixing, interrupting).
+ *
+ * <p>If the provided effect is played by the vibrator service with controlled timings (e.g.
+ * effects created via {@link VibrationEffect#createWaveform}), then triggering a new vibration
+ * will cause the ongoing playback to be interrupted in favor of the new vibration. If the
+ * effect is broken down into multiple consecutive commands (e.g. large primitive compositions)
+ * then the hardware commands will be triggered in succession without waiting for the completion
+ * callback.
+ *
+ * <p>The vendor app is responsible for timing the session requests and the vibrator hardware
+ * implementation is free to handle concurrency with different policies.
+ *
+ * @param effect The {@link VibrationEffect} describing the vibration to be performed.
+ * @param reason The description for the vibration reason, for debugging purposes.
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public void vibrate(@NonNull VibrationEffect effect, @Nullable String reason) {
+ try {
+ mSession.vibrate(CombinedVibration.createParallel(effect), reason);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate in a vendor vibration session.", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Cancel ongoing session.
+ *
+ * <p>This will stop the vibration immediately and return the vibrator control to the
+ * platform. This can also be triggered after {@link #close()} to immediately release the
+ * vibrator.
+ *
+ * <p>This will trigger {@link VendorVibrationSession.Callback#onFinished} directly with
+ * {@link #STATUS_CANCELED}.
+ */
+ public void cancel() {
+ try {
+ mSession.cancelSession();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel vendor vibration session.", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * End ongoing session gracefully.
+ *
+ * <p>This might continue the vibration while it's ramping down and wrapping up the session
+ * in the vibrator hardware. No more vibration commands can be sent through this session
+ * after this method is called.
+ *
+ * <p>This will trigger {@link VendorVibrationSession.Callback#onFinishing()}.
+ */
+ @Override
+ public void close() {
+ try {
+ mSession.finishSession();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to finish vendor vibration session.", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Callbacks for {@link VendorVibrationSession} events.
+ *
+ * @see Vibrator#startVendorSession
+ * @see VendorVibrationSession
+ */
+ public interface Callback {
+
+ /**
+ * New session was successfully started.
+ *
+ * <p>The vendor app can interact with the vibrator using the
+ * {@link VendorVibrationSession} provided.
+ */
+ void onStarted(@NonNull VendorVibrationSession session);
+
+ /**
+ * The session is ending and finishing any pending vibrations.
+ *
+ * <p>This is only invoked after {@link #onStarted(VendorVibrationSession)}. It will be
+ * triggered by both {@link VendorVibrationSession#cancel()} and
+ * {@link VendorVibrationSession#close()}. This might also be triggered if the platform
+ * cancels the ongoing session.
+ *
+ * <p>Session vibrations might be still ongoing in the vibrator hardware but the app can
+ * no longer send commands through the session. A finishing session can still be immediately
+ * stopped via calls to {@link VendorVibrationSession.Callback#cancel()}.
+ */
+ void onFinishing();
+
+ /**
+ * The session is finished.
+ *
+ * <p>The vibrator has finished any vibration and returned to the platform's control. This
+ * might be triggered by the vendor app or by the vibrator service.
+ *
+ * <p>If this is triggered before {@link #onStarted} then the session was finished before
+ * starting, either because it was cancelled or failed to start. If the session has already
+ * started then this will be triggered after {@link #onFinishing()} to indicate all session
+ * vibrations are complete and the vibrator is no longer under the session's control.
+ *
+ * @param status The session status.
+ */
+ void onFinished(@VendorVibrationSession.Status int status);
+ }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 6a4932211f27..33040be7c0fb 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -53,6 +53,24 @@ flag {
}
flag {
+ name: "enhanced_confirmation_in_call_apis_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "enable enhanced confirmation incall apis"
+ bug: "310220212"
+}
+
+flag {
+ name: "unknown_call_package_install_blocking_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "enable the blocking of certain app installs during an unknown call"
+ bug: "310220212"
+}
+
+flag {
name: "op_enable_mobile_data_by_user"
is_exported: true
namespace: "permissions"
@@ -266,7 +284,7 @@ flag {
is_fixed_read_only: true
is_exported: true
namespace: "android_health_services"
- description: "This fixed read-only flag is used to enable replacing permission BODY_SENSORS (and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE (and READ_HEALTH_DATA_IN_BACKGROUND)"
+ description: "Enables replacement of BODY_SENSORS/BODY_SENSORS_BACKGROUND permissions with granular health permissions READ_HEART_RATE, READ_SKIN_TEMPERATURE, READ_OXYGEN_SATURATION, and READ_HEALTH_DATA_IN_BACKGROUND"
bug: "364638912"
}
@@ -282,24 +300,6 @@ flag {
}
flag {
- name: "platform_skin_temperature_enabled"
- is_fixed_read_only: true
- is_exported: true
- namespace: "android_health_services"
- description: "This fixed read-only flag is used to enable platform support for Skin Temperature."
- bug: "369872443"
-}
-
-flag {
- name: "platform_oxygen_saturation_enabled"
- is_fixed_read_only: true
- is_exported: true
- namespace: "android_health_services"
- description: "This fixed read-only flag is used to enable platform support for Oxygen Saturation (SpO2)."
- bug: "369873227"
-}
-
-flag {
name: "allow_host_permission_dialogs_on_virtual_devices"
is_exported: true
namespace: "permissions"
@@ -332,3 +332,62 @@ flag {
description: "Enables ExtServices to leverage TextClassifier for OTP detection"
bug: "351976749"
}
+
+flag {
+ name: "health_connect_backup_restore_permission_enabled"
+ is_fixed_read_only: true
+ namespace: "health_fitness_aconfig"
+ description: "This flag protects the permission that is required to call Health Connect backup and restore apis"
+ bug: "376014879" # android_fr bug
+ is_exported: true
+}
+
+flag {
+ name: "enable_aiai_proxied_text_classifiers"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables the AiAi to utilize the default OTP text classifier that is also used by ExtServices"
+ bug: "377229653"
+}
+
+flag {
+ name: "enable_sqlite_appops_accesses"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables SQlite for recording discrete and historical AppOp accesses"
+ bug: "377584611"
+}
+
+flag {
+ name: "ranging_permission_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "uwb"
+ description: "This fixed read-only flag is used to enable new ranging permission for all ranging use cases."
+ bug: "370977414"
+}
+
+flag {
+ name: "system_selection_toolbar_enabled"
+ namespace: "permissions"
+ description: "Enables the system selection toolbar feature."
+ bug: "363318732"
+}
+
+flag {
+ name: "use_system_selection_toolbar_in_sysui"
+ namespace: "permissions"
+ description: "Uses the SysUi process to host the SelectionToolbarRenderService."
+ bug: "363318732"
+}
+
+flag{
+ name: "note_op_batching_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Batch noteOperations on the client to reduce binder call volume"
+ bug: "366013082"
+}
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 0809de25b45c..ce79f5d0c669 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -2,3 +2,4 @@
anothermark@google.com
kumarashishg@google.com
+bmgordon@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 0809de25b45c..ce79f5d0c669 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -2,3 +2,4 @@
anothermark@google.com
kumarashishg@google.com
+bmgordon@google.com
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d19681c86320..d2a20b65c1ee 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1217,6 +1217,69 @@ public final class Settings {
"android.settings.REGIONAL_PREFERENCES_SETTINGS";
/**
+ * Activity Action: Show screen for allowing the region configuration.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REGION_SETTINGS =
+ "android.settings.REGION_SETTINGS";
+
+ /**
+ * Activity Action: Show first day of week configuration settings.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_FIRST_DAY_OF_WEEK_SETTINGS =
+ "android.settings.FIRST_DAY_OF_WEEK_SETTINGS";
+
+ /**
+ * Activity Action: Show temperature unit configuration settings.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TEMPERATURE_UNIT_SETTINGS =
+ "android.settings.TEMPERATURE_UNIT_SETTINGS";
+
+ /**
+ * Activity Action: Show numbering system configuration settings.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NUMBERING_SYSTEM_SETTINGS =
+ "android.settings.NUMBERING_SYSTEM_SETTINGS";
+
+ /**
+ * Activity Action: Show measurement system configuration settings.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MEASUREMENT_SYSTEM_SETTINGS =
+ "android.settings.MEASUREMENT_SYSTEM_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of lockscreen.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -8687,6 +8750,19 @@ public final class Settings {
public static final String ACCESSIBILITY_QS_TARGETS = "accessibility_qs_targets";
/**
+ * Setting specifying the accessibility services, accessibility shortcut targets,
+ * or features to be toggled via a keyboard shortcut gesture.
+ *
+ * <p> This is a colon-separated string list which contains the flattened
+ * {@link ComponentName} and the class name of a system class implementing a supported
+ * accessibility feature.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_KEY_GESTURE_TARGETS =
+ "accessibility_key_gesture_targets";
+
+ /**
* The system class name of magnification controller which is a target to be toggled via
* accessibility shortcut or accessibility button.
*
diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig
index 4c636735b5ce..fff53637485b 100644
--- a/core/java/android/provider/flags.aconfig
+++ b/core/java/android/provider/flags.aconfig
@@ -63,3 +63,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "system_regional_preferences_api_enabled"
+ is_exported: true
+ namespace: "globalintl"
+ description: "Feature flag for regional preferences APIs"
+ bug: "370379000"
+}
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 6f3e3d8f0d3b..9fe0dda136d1 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -16,20 +16,30 @@
package android.security.advancedprotection;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.Intent;
import android.os.Binder;
import android.os.RemoteException;
import android.security.Flags;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -45,6 +55,139 @@ import java.util.concurrent.Executor;
public final class AdvancedProtectionManager {
private static final String TAG = "AdvancedProtectionMgr";
+ /**
+ * Advanced Protection's identifier for setting policies or restrictions in DevicePolicyManager.
+ *
+ * @hide */
+ public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY =
+ "android.security.advancedprotection";
+
+ /**
+ * Feature identifier for disallowing 2G.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_CELLULAR_2G =
+ "android.security.advancedprotection.feature_disallow_2g";
+
+ /**
+ * Feature identifier for disallowing install of unknown sources.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES =
+ "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+
+ /**
+ * Feature identifier for disallowing USB.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_USB =
+ "android.security.advancedprotection.feature_disallow_usb";
+
+ /**
+ * Feature identifier for disallowing WEP.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_WEP =
+ "android.security.advancedprotection.feature_disallow_wep";
+
+ /**
+ * Feature identifier for enabling MTE.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_ENABLE_MTE =
+ "android.security.advancedprotection.feature_enable_mte";
+
+ /** @hide */
+ @StringDef(prefix = { "FEATURE_ID_" }, value = {
+ FEATURE_ID_DISALLOW_CELLULAR_2G,
+ FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ FEATURE_ID_DISALLOW_USB,
+ FEATURE_ID_DISALLOW_WEP,
+ FEATURE_ID_ENABLE_MTE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureId {}
+
+ private static final Set<String> ALL_FEATURE_IDS = Set.of(
+ FEATURE_ID_DISALLOW_CELLULAR_2G,
+ FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ FEATURE_ID_DISALLOW_USB,
+ FEATURE_ID_DISALLOW_WEP,
+ FEATURE_ID_ENABLE_MTE);
+
+ /**
+ * Activity Action: Show a dialog with disabled by advanced protection message.
+ * <p> If a user action or a setting toggle is disabled by advanced protection, this dialog can
+ * be triggered to let the user know about this.
+ * <p>
+ * Input:
+ * <p>{@link #EXTRA_SUPPORT_DIALOG_FEATURE}: The feature identifier.
+ * <p>{@link #EXTRA_SUPPORT_DIALOG_TYPE}: The type of the action.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @FlaggedApi(android.security.Flags.FLAG_AAPM_API)
+ public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG =
+ "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
+
+ /**
+ * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+ * show a support dialog explaining it was disabled by advanced protection.
+ *
+ * @hide */
+ @FeatureId
+ @SystemApi
+ public static final String EXTRA_SUPPORT_DIALOG_FEATURE =
+ "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
+
+ /**
+ * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+ * needs to be explained in the support dialog.
+ *
+ * @hide */
+ @SupportDialogType
+ @SystemApi
+ public static final String EXTRA_SUPPORT_DIALOG_TYPE =
+ "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
+
+ /**
+ * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user performed an action that was
+ * blocked by advanced protection.
+ *
+ * @hide */
+ @SystemApi
+ public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION =
+ "android.security.advancedprotection.type_blocked_interaction";
+
+ /**
+ * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user pressed on a setting toggle
+ * that was disabled by advanced protection.
+ *
+ * @hide */
+ @SystemApi
+ public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING =
+ "android.security.advancedprotection.type_disabled_setting";
+
+ /** @hide */
+ @StringDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = {
+ SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
+ SUPPORT_DIALOG_TYPE_DISABLED_SETTING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SupportDialogType {}
+
+ private static final Set<String> ALL_SUPPORT_DIALOG_TYPES = Set.of(
+ SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
+ SUPPORT_DIALOG_TYPE_DISABLED_SETTING);
+
private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
mCallbackMap = new ConcurrentHashMap<>();
@@ -164,6 +307,43 @@ public final class AdvancedProtectionManager {
}
/**
+ * Called by a feature to display a support dialog when a feature was disabled by advanced
+ * protection. This returns an intent that can be used with
+ * {@link Context#startActivity(Intent)} to display the dialog.
+ *
+ * <p>Note that this method doesn't check if the feature is actually disabled, i.e. this method
+ * will always return an intent.
+ *
+ * @param featureId The feature identifier.
+ * @param type The type of the feature describing the action that needs to be explained
+ * in the dialog or null for default explanation.
+ * @return Intent An intent to be used to start the dialog-activity that explains a feature was
+ * disabled by advanced protection.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Intent createSupportIntent(@NonNull @FeatureId String featureId,
+ @Nullable @SupportDialogType String type) {
+ Objects.requireNonNull(featureId);
+ if (!ALL_FEATURE_IDS.contains(featureId)) {
+ throw new IllegalArgumentException(featureId + " is not a valid feature ID. See"
+ + " FEATURE_ID_* APIs.");
+ }
+ if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
+ throw new IllegalArgumentException(type + " is not a valid type. See"
+ + " SUPPORT_DIALOG_TYPE_* APIs.");
+ }
+
+ Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId);
+ if (type != null) {
+ intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type);
+ }
+ return intent;
+ }
+
+ /**
* A callback class for monitoring changes to Advanced Protection state
*
* <p>To register a callback, implement this interface, and register it with
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 1d35344e5218..ce901217d700 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -109,7 +109,7 @@ flag {
flag {
name: "afl_api"
- namespace: "platform_security"
+ namespace: "hardware_backed_security"
description: "AFL feature"
bug: "365994454"
}
@@ -120,3 +120,10 @@ flag {
description: "Feature flag for exposing KeyStore grant APIs"
bug: "351158708"
}
+
+flag {
+ name: "secure_lockdown"
+ namespace: "biometrics"
+ description: "Feature flag for Secure Lockdown feature"
+ bug: "373422357"
+} \ No newline at end of file
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 5995760a41ec..66e1f38621ae 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -67,6 +67,7 @@ flag {
name: "aapm_api"
namespace: "responsible_apis"
description: "Android Advanced Protection Mode Service and Manager"
+ is_exported: true
bug: "352420507"
is_fixed_read_only: true
}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index ca20801852f1..be4629ab8178 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -16,6 +16,9 @@
package android.service.autofill;
+import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -100,7 +103,12 @@ public final class FillRequest implements Parcelable {
/**
* Indicates the request supports fill dialog presentation for the fields, the
* system will send the request when the activity just started.
+ *
+ * @deprecated All requests would support fill dialog by default.
+ * Presence of this flag isn't needed.
*/
+ @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+ @Deprecated
public static final @RequestFlags int FLAG_SUPPORTS_FILL_DIALOG = 0x40;
/**
@@ -588,10 +596,10 @@ public final class FillRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1701010178309L,
+ time = 1730991738865L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
- inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.annotation.FlaggedApi @java.lang.Deprecated @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index 72f2de805474..dfc11dcb5427 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -67,3 +67,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "cleanup_dream_settings_on_uninstall"
+ namespace: "systemui"
+ description: "Cleans up dream settings if dream package is uninstalled."
+ bug: "338210427"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index e8d53d351795..531e0b11996a 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -516,6 +516,8 @@ public abstract class GameSession {
options,
future);
+ trampolineIntent.collectExtraIntentKeys();
+
try {
int result = ActivityTaskManager.getService().startActivityFromGameSession(
mContext.getIApplicationThread(), mContext.getPackageName(), "GameSession",
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 34e311f8c932..d065939bc19d 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -65,4 +65,11 @@ flag {
namespace: "systemui"
description: "Allows the NAS to create and modify conversation notifications"
bug: "373599715"
-} \ No newline at end of file
+}
+
+flag {
+ name: "notification_regroup_on_classification"
+ namespace: "systemui"
+ description: "This flag controls regrouping after notification classification"
+ bug: "372775153"
+}
diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig
new file mode 100644
index 000000000000..07311d5ffbe1
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.quickaccesswallet"
+container: "system"
+
+flag {
+ name: "launch_wallet_option_on_power_double_tap"
+ namespace: "wallet_integrations"
+ description: "Option to launch the Wallet app on double-tap of the power button"
+ bug: "378469025"
+} \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/GetValueRequest.aidl b/core/java/android/service/settings/preferences/GetValueRequest.aidl
new file mode 100644
index 000000000000..2a0eb09aa2a4
--- /dev/null
+++ b/core/java/android/service/settings/preferences/GetValueRequest.aidl
@@ -0,0 +1,4 @@
+package android.service.settings.preferences;
+
+/** @hide */
+parcelable GetValueRequest; \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/GetValueRequest.java b/core/java/android/service/settings/preferences/GetValueRequest.java
new file mode 100644
index 000000000000..4f82800d1855
--- /dev/null
+++ b/core/java/android/service/settings/preferences/GetValueRequest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Request parameters to retrieve the current value of a Settings Preference.
+ *
+ * <p>This object passed to {@link SettingsPreferenceService#onGetPreferenceValue} will result
+ * in a {@link GetValueResult}.
+ *
+ * <ul>
+ * <li>{@link #getScreenKey} is a parameter to distinguish the container screen
+ * of a preference as a preference key may not be unique within its application.
+ * <li>{@link #getPreferenceKey} is a parameter to identify the preference for which the value is
+ * being requested. These keys will be unique with their Preference Screen, but may not be unique
+ * within their application, so it is required to pair this with {@link #getScreenKey} to
+ * ensure this request matches the intended target.
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class GetValueRequest implements Parcelable {
+
+ @NonNull
+ private final String mScreenKey;
+ @NonNull
+ private final String mPreferenceKey;
+
+ /**
+ * Returns the screen key of requested Preference.
+ */
+ @NonNull
+ public String getScreenKey() {
+ return mScreenKey;
+ }
+
+ /**
+ * Returns the key of requested Preference.
+ */
+ @NonNull
+ public String getPreferenceKey() {
+ return mPreferenceKey;
+ }
+
+ private GetValueRequest(@NonNull Builder builder) {
+ mScreenKey = builder.mScreenKey;
+ mPreferenceKey = builder.mPreferenceKey;
+ }
+
+ private GetValueRequest(@NonNull Parcel in) {
+ mScreenKey = Objects.requireNonNull(in.readString8());
+ mPreferenceKey = Objects.requireNonNull(in.readString8());
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mScreenKey);
+ dest.writeString8(mPreferenceKey);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link GetValueRequest}.
+ */
+ @NonNull
+ public static final Creator<GetValueRequest> CREATOR = new Creator<GetValueRequest>() {
+ @Override
+ public GetValueRequest createFromParcel(@NonNull Parcel in) {
+ return new GetValueRequest(in);
+ }
+
+ @Override
+ public GetValueRequest[] newArray(int size) {
+ return new GetValueRequest[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link GetValueRequest}.
+ */
+ public static final class Builder {
+ private final String mScreenKey;
+ private final String mPreferenceKey;
+
+ /**
+ * Create Builder instance.
+ * @param screenKey required to be not empty
+ * @param preferenceKey required to be not empty
+ */
+ public Builder(@NonNull String screenKey, @NonNull String preferenceKey) {
+ if (TextUtils.isEmpty(screenKey)) {
+ throw new IllegalArgumentException("screenKey cannot be empty");
+ }
+ if (TextUtils.isEmpty(preferenceKey)) {
+ throw new IllegalArgumentException("preferenceKey cannot be empty");
+ }
+ mScreenKey = screenKey;
+ mPreferenceKey = preferenceKey;
+ }
+
+ /**
+ * Constructs an immutable {@link GetValueRequest} object.
+ */
+ @NonNull
+ public GetValueRequest build() {
+ return new GetValueRequest(this);
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/GetValueResult.aidl b/core/java/android/service/settings/preferences/GetValueResult.aidl
new file mode 100644
index 000000000000..b5ebd35a3a37
--- /dev/null
+++ b/core/java/android/service/settings/preferences/GetValueResult.aidl
@@ -0,0 +1,4 @@
+package android.service.settings.preferences;
+
+/** @hide */
+parcelable GetValueResult; \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/GetValueResult.java b/core/java/android/service/settings/preferences/GetValueResult.java
new file mode 100644
index 000000000000..369dea77cc85
--- /dev/null
+++ b/core/java/android/service/settings/preferences/GetValueResult.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result object given a corresponding {@link GetValueRequest}.
+ * <ul>
+ * <li>If the request was successful, {@link #getResultCode} will be {@link #RESULT_OK},
+ * {@link #getValue} will be populated with the settings preference value and
+ * {@link #getMetadata} will be populated with its metadata.
+ * <li>If the request is unsuccessful, {@link #getResultCode} be a value other than
+ * {@link #RESULT_OK} - see documentation for those possibilities to understand the cause
+ * of the failure.
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class GetValueResult implements Parcelable {
+
+ @ResultCode
+ private final int mResultCode;
+ @Nullable
+ private final SettingsPreferenceValue mValue;
+ @Nullable
+ private final SettingsPreferenceMetadata mMetadata;
+
+ /**
+ * Returns the result code indicating status of the request.
+ */
+ @ResultCode
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ /**
+ * Returns the value of requested Preference if request successful.
+ */
+ @Nullable
+ public SettingsPreferenceValue getValue() {
+ return mValue;
+ }
+
+ /**
+ * Returns the metadata of requested Preference if request successful.
+ */
+ @Nullable
+ public SettingsPreferenceMetadata getMetadata() {
+ return mMetadata;
+ }
+
+ /** @hide */
+ @IntDef(prefix = { "RESULT_" }, value = {
+ RESULT_OK,
+ RESULT_UNSUPPORTED,
+ RESULT_UNAVAILABLE,
+ RESULT_REQUIRE_APP_PERMISSION,
+ RESULT_DISALLOW,
+ RESULT_INVALID_REQUEST,
+ RESULT_INTERNAL_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ResultCode {
+ }
+
+ /** Request is successful. */
+ public static final int RESULT_OK = 0;
+ /**
+ * Requested preference is not supported by this API.
+ * <p>Retry not advised.
+ */
+ public static final int RESULT_UNSUPPORTED = 1;
+ /**
+ * Preference is currently not available, likely due to device state or the state of
+ * a dependency.
+ * <p>Retry may succeed if underlying conditions change.
+ */
+ public static final int RESULT_UNAVAILABLE = 2;
+ /**
+ * Requested preference requires permissions not held by the calling application.
+ * <p>Retry may succeed if necessary permissions are obtained.
+ */
+ public static final int RESULT_REQUIRE_APP_PERMISSION = 3;
+ /**
+ * Requested preference is not allowed for access in this API under the current device policy.
+ * <p>Retry may succeed if underlying conditions change.
+ */
+ public static final int RESULT_DISALLOW = 4;
+ /**
+ * Request object is not valid.
+ * <p>Retry not advised with current parameters.
+ */
+ public static final int RESULT_INVALID_REQUEST = 5;
+ /**
+ * API call failed due to an issue with the service binding.
+ * <p>Retry may succeed.
+ */
+ public static final int RESULT_INTERNAL_ERROR = 6;
+
+
+ private GetValueResult(@NonNull Builder builder) {
+ mResultCode = builder.mResultCode;
+ mValue = builder.mValue;
+ mMetadata = builder.mMetadata;
+ }
+
+ private GetValueResult(@NonNull Parcel in) {
+ mResultCode = in.readInt();
+ mValue = in.readParcelable(SettingsPreferenceValue.class.getClassLoader(),
+ SettingsPreferenceValue.class);
+ mMetadata = in.readParcelable(SettingsPreferenceMetadata.class.getClassLoader(),
+ SettingsPreferenceMetadata.class);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mResultCode);
+ dest.writeParcelable(mValue, flags);
+ dest.writeParcelable(mMetadata, flags);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link GetValueResult}.
+ */
+ @NonNull
+ public static final Creator<GetValueResult> CREATOR = new Creator<>() {
+ @Override
+ public GetValueResult createFromParcel(@NonNull Parcel in) {
+ return new GetValueResult(in);
+ }
+
+ @Override
+ public GetValueResult[] newArray(int size) {
+ return new GetValueResult[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link GetValueResult}.
+ */
+ public static final class Builder {
+ @ResultCode
+ private final int mResultCode;
+ private SettingsPreferenceValue mValue;
+ private SettingsPreferenceMetadata mMetadata;
+
+ /**
+ * Create Builder instance.
+ * @param resultCode indicates status of the request
+ */
+ public Builder(@ResultCode int resultCode) {
+ mResultCode = resultCode;
+ }
+
+ /**
+ * Sets the preference value on the result.
+ */
+ @NonNull
+ public Builder setValue(@Nullable SettingsPreferenceValue value) {
+ mValue = value;
+ return this;
+ }
+
+ /**
+ * Sets the metadata on the result.
+ */
+ @NonNull
+ public Builder setMetadata(@Nullable SettingsPreferenceMetadata metadata) {
+ mMetadata = metadata;
+ return this;
+ }
+
+ /**
+ * Constructs an immutable {@link GetValueResult} object.
+ */
+ @NonNull
+ public GetValueResult build() {
+ return new GetValueResult(this);
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/IGetValueCallback.aidl b/core/java/android/service/settings/preferences/IGetValueCallback.aidl
new file mode 100644
index 000000000000..bbc7423f453e
--- /dev/null
+++ b/core/java/android/service/settings/preferences/IGetValueCallback.aidl
@@ -0,0 +1,9 @@
+package android.service.settings.preferences;
+
+import android.service.settings.preferences.GetValueResult;
+
+/** @hide */
+oneway interface IGetValueCallback {
+ void onSuccess(in GetValueResult result) = 1;
+ void onFailure() = 2;
+}
diff --git a/core/java/android/service/settings/preferences/IMetadataCallback.aidl b/core/java/android/service/settings/preferences/IMetadataCallback.aidl
new file mode 100644
index 000000000000..3bd5ebe93660
--- /dev/null
+++ b/core/java/android/service/settings/preferences/IMetadataCallback.aidl
@@ -0,0 +1,9 @@
+package android.service.settings.preferences;
+
+import android.service.settings.preferences.MetadataResult;
+
+/** @hide */
+oneway interface IMetadataCallback {
+ void onSuccess(in MetadataResult result);
+ void onFailure();
+}
diff --git a/core/java/android/service/settings/preferences/ISetValueCallback.aidl b/core/java/android/service/settings/preferences/ISetValueCallback.aidl
new file mode 100644
index 000000000000..0765660c83c3
--- /dev/null
+++ b/core/java/android/service/settings/preferences/ISetValueCallback.aidl
@@ -0,0 +1,9 @@
+package android.service.settings.preferences;
+
+import android.service.settings.preferences.SetValueResult;
+
+/** @hide */
+oneway interface ISetValueCallback {
+ void onSuccess(in SetValueResult result);
+ void onFailure();
+}
diff --git a/core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl b/core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl
new file mode 100644
index 000000000000..64a8b90fe581
--- /dev/null
+++ b/core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl
@@ -0,0 +1,18 @@
+package android.service.settings.preferences;
+
+import android.service.settings.preferences.GetValueRequest;
+import android.service.settings.preferences.IGetValueCallback;
+import android.service.settings.preferences.IMetadataCallback;
+import android.service.settings.preferences.ISetValueCallback;
+import android.service.settings.preferences.MetadataRequest;
+import android.service.settings.preferences.SetValueRequest;
+
+/** @hide */
+oneway interface ISettingsPreferenceService {
+ @EnforcePermission("READ_SYSTEM_PREFERENCES")
+ void getAllPreferenceMetadata(in MetadataRequest request, IMetadataCallback callback) = 1;
+ @EnforcePermission("READ_SYSTEM_PREFERENCES")
+ void getPreferenceValue(in GetValueRequest request, IGetValueCallback callback) = 2;
+ @EnforcePermission(allOf = {"READ_SYSTEM_PREFERENCES", "WRITE_SYSTEM_PREFERENCES"})
+ void setPreferenceValue(in SetValueRequest request, ISetValueCallback callback) = 3;
+}
diff --git a/core/java/android/service/settings/preferences/MetadataRequest.aidl b/core/java/android/service/settings/preferences/MetadataRequest.aidl
new file mode 100644
index 000000000000..dc3cbc42661e
--- /dev/null
+++ b/core/java/android/service/settings/preferences/MetadataRequest.aidl
@@ -0,0 +1,4 @@
+package android.service.settings.preferences;
+
+/** @hide */
+parcelable MetadataRequest; \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/MetadataRequest.java b/core/java/android/service/settings/preferences/MetadataRequest.java
new file mode 100644
index 000000000000..ffecc6bec5b2
--- /dev/null
+++ b/core/java/android/service/settings/preferences/MetadataRequest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.flags.Flags;
+
+/**
+ * Request parameters to retrieve all metadata for all available settings preferences within this
+ * application.
+ *
+ * <p>This object passed to {@link SettingsPreferenceService#onGetAllPreferenceMetadata} will result
+ * in a {@link MetadataResult}.
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class MetadataRequest implements Parcelable {
+ private MetadataRequest() {}
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link MetadataRequest}.
+ */
+ @NonNull
+ public static final Creator<MetadataRequest> CREATOR = new Creator<>() {
+ @Override
+ public MetadataRequest createFromParcel(@NonNull Parcel in) {
+ return new MetadataRequest();
+ }
+
+ @Override
+ public MetadataRequest[] newArray(int size) {
+ return new MetadataRequest[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link MetadataRequest}.
+ */
+ public static final class Builder {
+ /** Constructs an immutable {@link MetadataRequest} object. */
+ @NonNull
+ public MetadataRequest build() {
+ return new MetadataRequest();
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/MetadataResult.aidl b/core/java/android/service/settings/preferences/MetadataResult.aidl
new file mode 100644
index 000000000000..af9e8a86e3ab
--- /dev/null
+++ b/core/java/android/service/settings/preferences/MetadataResult.aidl
@@ -0,0 +1,4 @@
+package android.service.settings.preferences;
+
+/** @hide */
+parcelable MetadataResult; \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/MetadataResult.java b/core/java/android/service/settings/preferences/MetadataResult.java
new file mode 100644
index 000000000000..6a65dcc9c757
--- /dev/null
+++ b/core/java/android/service/settings/preferences/MetadataResult.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Result object given a corresponding {@link MetadataRequest}.
+ * <ul>
+ * <li>If the request was successful, {@link #getResultCode} will be {@link #RESULT_OK} and
+ * {@link #getMetadataList} will be populated with metadata for all available preferences within
+ * this application.
+ * <li>If the request is unsuccessful, {@link #getResultCode} be a value other than
+ * {@link #RESULT_OK} - see documentation for those possibilities to understand the cause
+ * of the failure.
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class MetadataResult implements Parcelable {
+
+ @ResultCode
+ private final int mResultCode;
+ @NonNull
+ private final List<SettingsPreferenceMetadata> mMetadataList;
+
+ /**
+ * Returns the result code indicating status of the request.
+ */
+ @ResultCode
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ /**
+ * Returns the list of available Preference Metadata.
+ * <p>This instance is shared so this list should not be modified.
+ */
+ @NonNull
+ public List<SettingsPreferenceMetadata> getMetadataList() {
+ return mMetadataList;
+ }
+
+ /** @hide */
+ @IntDef(prefix = { "RESULT_" }, value = {
+ RESULT_OK,
+ RESULT_UNSUPPORTED,
+ RESULT_INTERNAL_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ResultCode {
+ }
+
+ /** Request is successful. */
+ public static final int RESULT_OK = 0;
+ /**
+ * No preferences in this application support this API.
+ * <p>Retry not advised.
+ */
+ public static final int RESULT_UNSUPPORTED = 1;
+ /**
+ * API call failed due to an issue with the service binding.
+ * <p>Retry may succeed.
+ */
+ public static final int RESULT_INTERNAL_ERROR = 2;
+
+ private MetadataResult(@NonNull Builder builder) {
+ mResultCode = builder.mResultCode;
+ mMetadataList = builder.mMetadataList;
+ }
+ private MetadataResult(@NonNull Parcel in) {
+ mResultCode = in.readInt();
+ mMetadataList = new ArrayList<>();
+ in.readTypedList(mMetadataList, SettingsPreferenceMetadata.CREATOR);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mResultCode);
+ dest.writeTypedList(mMetadataList, flags);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link MetadataResult}.
+ */
+ @NonNull
+ public static final Creator<MetadataResult> CREATOR = new Creator<>() {
+ @Override
+ public MetadataResult createFromParcel(@NonNull Parcel in) {
+ return new MetadataResult(in);
+ }
+
+ @Override
+ public MetadataResult[] newArray(int size) {
+ return new MetadataResult[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link MetadataResult}.
+ */
+ public static final class Builder {
+ @ResultCode
+ private final int mResultCode;
+ private List<SettingsPreferenceMetadata> mMetadataList = Collections.emptyList();
+
+ /**
+ * Create Builder instance.
+ * @param resultCode indicates status of the request
+ */
+ public Builder(@ResultCode int resultCode) {
+ mResultCode = resultCode;
+ }
+
+ /**
+ * Sets the metadata list on the result.
+ */
+ @NonNull
+ public Builder setMetadataList(@NonNull List<SettingsPreferenceMetadata> metadataList) {
+ mMetadataList = metadataList;
+ return this;
+ }
+
+ /**
+ * Constructs an immutable {@link MetadataResult} object.
+ */
+ @NonNull
+ public MetadataResult build() {
+ return new MetadataResult(this);
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/SetValueRequest.aidl b/core/java/android/service/settings/preferences/SetValueRequest.aidl
new file mode 100644
index 000000000000..198e333d5cb6
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SetValueRequest.aidl
@@ -0,0 +1,4 @@
+package android.service.settings.preferences;
+
+/** @hide */
+parcelable SetValueRequest; \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/SetValueRequest.java b/core/java/android/service/settings/preferences/SetValueRequest.java
new file mode 100644
index 000000000000..f7600aecdfaf
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SetValueRequest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Request parameters to set the current value to a Settings Preference.
+ * <p>This object passed to {@link SettingsPreferenceService#onSetPreferenceValue} will result in a
+ * {@link SetValueResult}.
+ * <ul>
+ * <li>{@link #getScreenKey} is a parameter to distinguish the container screen
+ * of a preference as a preference key may not be unique within its application.
+ * <li>{@link #getPreferenceKey} is a parameter to identify the preference for which the value is
+ * being requested. These keys will be unique with their Preference Screen, but may not be unique
+ * within their application, so it is required to pair this with {@link #getScreenKey} to
+ * ensure this request matches the intended target.
+ * <li>{@link #getPreferenceValue} is a parameter to specify the value that this request aims to
+ * set. If this value is invalid (malformed or does not match the type of the preference) then
+ * this request will fail.
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class SetValueRequest implements Parcelable {
+
+ @NonNull
+ private final String mScreenKey;
+ @NonNull
+ private final String mPreferenceKey;
+ @NonNull
+ private final SettingsPreferenceValue mPreferenceValue;
+
+ /**
+ * Returns the screen key of requested Preference.
+ */
+ @NonNull
+ public String getScreenKey() {
+ return mScreenKey;
+ }
+
+ /**
+ * Returns the key of requested Preference.
+ */
+ @NonNull
+ public String getPreferenceKey() {
+ return mPreferenceKey;
+ }
+
+ /**
+ * Returns the value of requested Preference.
+ */
+ @NonNull
+ public SettingsPreferenceValue getPreferenceValue() {
+ return mPreferenceValue;
+ }
+
+ private SetValueRequest(@NonNull Builder builder) {
+ mScreenKey = builder.mScreenKey;
+ mPreferenceKey = builder.mPreferenceKey;
+ mPreferenceValue = builder.mPreferenceValue;
+ }
+
+ private SetValueRequest(@NonNull Parcel in) {
+ mScreenKey = Objects.requireNonNull(in.readString8());
+ mPreferenceKey = Objects.requireNonNull(in.readString8());
+ mPreferenceValue = Objects.requireNonNull(in.readParcelable(
+ SettingsPreferenceValue.class.getClassLoader(), SettingsPreferenceValue.class));
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mScreenKey);
+ dest.writeString8(mPreferenceKey);
+ dest.writeParcelable(mPreferenceValue, flags);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link SetValueRequest}.
+ */
+ @NonNull
+ public static final Creator<SetValueRequest> CREATOR = new Creator<SetValueRequest>() {
+ @Override
+ public SetValueRequest createFromParcel(@NonNull Parcel in) {
+ return new SetValueRequest(in);
+ }
+
+ @Override
+ public SetValueRequest[] newArray(int size) {
+ return new SetValueRequest[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link SetValueRequest}.
+ */
+ public static final class Builder {
+ private final String mScreenKey;
+ private final String mPreferenceKey;
+ private final SettingsPreferenceValue mPreferenceValue;
+
+ /**
+ * Create Builder instance.
+ * @param screenKey required to be not empty
+ * @param preferenceKey required to be not empty
+ * @param value value to set to requested Preference
+ */
+ public Builder(@NonNull String screenKey, @NonNull String preferenceKey,
+ @NonNull SettingsPreferenceValue value) {
+ if (TextUtils.isEmpty(screenKey)) {
+ throw new IllegalArgumentException("screenKey cannot be empty");
+ }
+ if (TextUtils.isEmpty(preferenceKey)) {
+ throw new IllegalArgumentException("preferenceKey cannot be empty");
+ }
+ mScreenKey = screenKey;
+ mPreferenceKey = preferenceKey;
+ mPreferenceValue = value;
+ }
+
+ /**
+ * Constructs an immutable {@link SetValueRequest} object.
+ */
+ @NonNull
+ public SetValueRequest build() {
+ return new SetValueRequest(this);
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/SetValueResult.aidl b/core/java/android/service/settings/preferences/SetValueResult.aidl
new file mode 100644
index 000000000000..f54813484d68
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SetValueResult.aidl
@@ -0,0 +1,4 @@
+package android.service.settings.preferences;
+
+/** @hide */
+parcelable SetValueResult; \ No newline at end of file
diff --git a/core/java/android/service/settings/preferences/SetValueResult.java b/core/java/android/service/settings/preferences/SetValueResult.java
new file mode 100644
index 000000000000..cb1776abd3bc
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SetValueResult.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result object given a corresponding {@link SetValueRequest}.
+ * <ul>
+ * <li>If the request was successful, {@link #getResultCode} will be {@link #RESULT_OK}.
+ * <li>If the request is unsuccessful, {@link #getResultCode} be a value other than
+ * {@link #RESULT_OK} - see documentation for those possibilities to understand the cause
+ * of the failure.
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class SetValueResult implements Parcelable {
+
+ @ResultCode
+ private final int mResultCode;
+
+ /**
+ * Returns the result code indicating status of the request.
+ */
+ @ResultCode
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ /** @hide */
+ @IntDef(prefix = { "RESULT_" }, value = {
+ RESULT_OK,
+ RESULT_UNSUPPORTED,
+ RESULT_DISABLED,
+ RESULT_RESTRICTED,
+ RESULT_UNAVAILABLE,
+ RESULT_REQUIRE_APP_PERMISSION,
+ RESULT_REQUIRE_USER_CONSENT,
+ RESULT_DISALLOW,
+ RESULT_INVALID_REQUEST,
+ RESULT_INTERNAL_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ResultCode {
+ }
+
+ /** Request is successful and the value was set. */
+ public static final int RESULT_OK = 0;
+ /**
+ * Requested preference is not supported by this API.
+ * <p>Retry not advised.
+ */
+ public static final int RESULT_UNSUPPORTED = 1;
+ /**
+ * Requested preference is disabled, thus unable to be set in this state.
+ * <p>Retry may succeed if underlying conditions change.
+ */
+ public static final int RESULT_DISABLED = 2;
+ /**
+ * Requested preference is restricted, thus unable to be set under this policy.
+ * <p>Retry may succeed if underlying conditions change.
+ */
+ public static final int RESULT_RESTRICTED = 3;
+ /**
+ * Preference is currently not available, likely due to device state or the state of
+ * a dependency.
+ * <p>Retry may succeed if underlying conditions change.
+ */
+ public static final int RESULT_UNAVAILABLE = 4;
+ /**
+ * Requested preference requires permissions not held by the calling application.
+ * <p>Retry may succeed if necessary permissions are obtained.
+ */
+ public static final int RESULT_REQUIRE_APP_PERMISSION = 5;
+ /**
+ * User consent was not approved for this operation.
+ * <p>Retry may succeed if user provides consent.
+ */
+ public static final int RESULT_REQUIRE_USER_CONSENT = 6;
+ /**
+ * Requested preference is not allowed for access in this API under the current device policy.
+ * <p>Retry may succeed if underlying conditions change.
+ */
+ public static final int RESULT_DISALLOW = 7;
+ /**
+ * Request object is not valid.
+ * <p>Retry not advised with current parameters.
+ */
+ public static final int RESULT_INVALID_REQUEST = 8;
+ /**
+ * API call failed due to an issue with the service binding.
+ * <p>Retry may succeed.
+ */
+ public static final int RESULT_INTERNAL_ERROR = 9;
+
+ private SetValueResult(@NonNull Builder builder) {
+ mResultCode = builder.mResultCode;
+ }
+
+ private SetValueResult(@NonNull Parcel in) {
+ mResultCode = in.readInt();
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mResultCode);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link SetValueResult}.
+ */
+ @NonNull
+ public static final Creator<SetValueResult> CREATOR = new Creator<>() {
+ @Override
+ public SetValueResult createFromParcel(@NonNull Parcel in) {
+ return new SetValueResult(in);
+ }
+
+ @Override
+ public SetValueResult[] newArray(int size) {
+ return new SetValueResult[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link SetValueResult}.
+ */
+ public static final class Builder {
+ @ResultCode
+ private final int mResultCode;
+
+ /**
+ * Create Builder instance.
+ * @param resultCode indicates status of the request
+ */
+ public Builder(@ResultCode int resultCode) {
+ mResultCode = resultCode;
+ }
+
+ /**
+ * Constructs an immutable {@link SetValueResult} object.
+ */
+ @NonNull
+ public SetValueResult build() {
+ return new SetValueResult(this);
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
new file mode 100644
index 000000000000..1d08c5217129
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.SuppressLint;
+import android.app.PendingIntent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Data object representation of a Settings Preference definition and state.
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class SettingsPreferenceMetadata implements Parcelable {
+
+ @NonNull
+ private final String mKey;
+ @NonNull
+ private final String mScreenKey;
+ @Nullable
+ private final String mTitle;
+ @Nullable
+ private final String mSummary;
+ @NonNull
+ private final List<String> mBreadcrumbs;
+ @NonNull
+ private final List<String> mReadPermissions;
+ @NonNull
+ private final List<String> mWritePermissions;
+ private final boolean mEnabled;
+ private final boolean mAvailable;
+ private final boolean mWritable;
+ private final boolean mRestricted;
+ private final int mSensitivity;
+ @Nullable
+ private final PendingIntent mLaunchIntent;
+ @NonNull
+ private final Bundle mExtras;
+
+ /**
+ * Returns the key of Preference.
+ */
+ @NonNull
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
+ * Returns the screen key of Preference.
+ */
+ @NonNull
+ public String getScreenKey() {
+ return mScreenKey;
+ }
+
+ /**
+ * Returns the title of Preference.
+ */
+ @Nullable
+ public String getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Returns the summary of Preference.
+ */
+ @Nullable
+ public String getSummary() {
+ return mSummary;
+ }
+
+ /**
+ * Returns the breadcrumbs (navigation context) of Preference.
+ * <p>May be empty.
+ */
+ @NonNull
+ public List<String> getBreadcrumbs() {
+ return mBreadcrumbs;
+ }
+
+ /**
+ * Returns the permissions required to read this Preference's value.
+ * <p>May be empty.
+ */
+ @NonNull
+ public List<String> getReadPermissions() {
+ return mReadPermissions;
+ }
+
+ /**
+ * Returns the permissions required to write this Preference's value.
+ * <p>May be empty.
+ */
+ @NonNull
+ public List<String> getWritePermissions() {
+ return mWritePermissions;
+ }
+
+ /**
+ * Returns whether Preference is enabled.
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Returns whether Preference is available.
+ */
+ public boolean isAvailable() {
+ return mAvailable;
+ }
+
+ /**
+ * Returns whether Preference is writable.
+ */
+ public boolean isWritable() {
+ return mWritable;
+ }
+
+ /**
+ * Returns whether Preference is restricted.
+ */
+ public boolean isRestricted() {
+ return mRestricted;
+ }
+
+ /**
+ * Returns the write-level sensitivity of Preference.
+ */
+ @WriteSensitivity
+ public int getWriteSensitivity() {
+ return mSensitivity;
+ }
+
+ /**
+ * Returns the intent to launch the host app page for this Preference.
+ */
+ @Nullable
+ public PendingIntent getLaunchIntent() {
+ return mLaunchIntent;
+ }
+
+ /**
+ * Returns any additional fields specific to this preference.
+ * <p>Treat all data as optional.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /** @hide */
+ @IntDef(value = {
+ NOT_SENSITIVE,
+ SENSITIVE,
+ INTENT_ONLY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WriteSensitivity {}
+
+ /**
+ * Preference is not sensitive, thus its value is writable without explicit consent, assuming
+ * all necessary permissions are granted.
+ */
+ public static final int NOT_SENSITIVE = 0;
+ /**
+ * Preference is sensitive, meaning that in addition to necessary permissions, writing its value
+ * will also request explicit user consent.
+ */
+ public static final int SENSITIVE = 1;
+ /**
+ * Preference is not permitted for write-access via API and must be changed via Settings page.
+ */
+ public static final int INTENT_ONLY = 2;
+
+ private SettingsPreferenceMetadata(@NonNull Builder builder) {
+ mKey = builder.mKey;
+ mScreenKey = builder.mScreenKey;
+ mTitle = builder.mTitle;
+ mSummary = builder.mSummary;
+ mBreadcrumbs = builder.mBreadcrumbs;
+ mReadPermissions = builder.mReadPermissions;
+ mWritePermissions = builder.mWritePermissions;
+ mEnabled = builder.mEnabled;
+ mAvailable = builder.mAvailable;
+ mWritable = builder.mWritable;
+ mRestricted = builder.mRestricted;
+ mSensitivity = builder.mSensitivity;
+ mLaunchIntent = builder.mLaunchIntent;
+ mExtras = Objects.requireNonNullElseGet(builder.mExtras, Bundle::new);
+ }
+ @SuppressLint("ParcelClassLoader")
+ private SettingsPreferenceMetadata(@NonNull Parcel in) {
+ mKey = Objects.requireNonNull(in.readString8());
+ mScreenKey = Objects.requireNonNull(in.readString8());
+ mTitle = in.readString8();
+ mSummary = in.readString8();
+ mBreadcrumbs = new ArrayList<>();
+ in.readStringList(mBreadcrumbs);
+ mReadPermissions = new ArrayList<>();
+ in.readStringList(mReadPermissions);
+ mWritePermissions = new ArrayList<>();
+ in.readStringList(mWritePermissions);
+ mEnabled = in.readBoolean();
+ mAvailable = in.readBoolean();
+ mWritable = in.readBoolean();
+ mRestricted = in.readBoolean();
+ mSensitivity = in.readInt();
+ mLaunchIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ mExtras = Objects.requireNonNullElseGet(in.readBundle(), Bundle::new);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mKey);
+ dest.writeString8(mScreenKey);
+ dest.writeString8(mTitle);
+ dest.writeString8(mSummary);
+ dest.writeStringList(mBreadcrumbs);
+ dest.writeStringList(mReadPermissions);
+ dest.writeStringList(mWritePermissions);
+ dest.writeBoolean(mEnabled);
+ dest.writeBoolean(mAvailable);
+ dest.writeBoolean(mWritable);
+ dest.writeBoolean(mRestricted);
+ dest.writeInt(mSensitivity);
+ dest.writeParcelable(mLaunchIntent, flags);
+ dest.writeBundle(mExtras);
+ }
+
+ /**
+ * Parcelable Creator for {@link SettingsPreferenceMetadata}.
+ */
+ @NonNull
+ public static final Creator<SettingsPreferenceMetadata> CREATOR = new Creator<>() {
+ @Override
+ public SettingsPreferenceMetadata createFromParcel(@NonNull Parcel in) {
+ return new SettingsPreferenceMetadata(in);
+ }
+
+ @Override
+ public SettingsPreferenceMetadata[] newArray(int size) {
+ return new SettingsPreferenceMetadata[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link SettingsPreferenceMetadata}.
+ */
+ public static final class Builder {
+ private final String mScreenKey;
+ private final String mKey;
+ private String mTitle;
+ private String mSummary;
+ private List<String> mBreadcrumbs = Collections.emptyList();
+ private List<String> mReadPermissions = Collections.emptyList();
+ private List<String> mWritePermissions = Collections.emptyList();
+ private boolean mEnabled = false;
+ private boolean mAvailable = false;
+ private boolean mWritable = false;
+ private boolean mRestricted = false;
+ @WriteSensitivity private int mSensitivity = INTENT_ONLY;
+ private PendingIntent mLaunchIntent;
+ private Bundle mExtras;
+
+ /**
+ * Create Builder instance.
+ * @param screenKey required to be not empty
+ * @param key required to be not empty
+ */
+ public Builder(@NonNull String screenKey, @NonNull String key) {
+ if (TextUtils.isEmpty(screenKey)) {
+ throw new IllegalArgumentException("screenKey cannot be empty");
+ }
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("key cannot be empty");
+ }
+ mScreenKey = screenKey;
+ mKey = key;
+ }
+
+ /**
+ * Sets the preference title.
+ */
+ @NonNull
+ public Builder setTitle(@Nullable String title) {
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * Sets the preference summary.
+ */
+ @NonNull
+ public Builder setSummary(@Nullable String summary) {
+ mSummary = summary;
+ return this;
+ }
+
+ /**
+ * Sets the preference breadcrumbs (navigation context).
+ */
+ @NonNull
+ public Builder setBreadcrumbs(@NonNull List<String> breadcrumbs) {
+ mBreadcrumbs = breadcrumbs;
+ return this;
+ }
+
+ /**
+ * Sets the permissions required for reading this preference.
+ */
+ @NonNull
+ public Builder setReadPermissions(@NonNull List<String> readPermissions) {
+ mReadPermissions = readPermissions;
+ return this;
+ }
+
+ /**
+ * Sets the permissions required for writing this preference.
+ */
+ @NonNull
+ public Builder setWritePermissions(@NonNull List<String> writePermissions) {
+ mWritePermissions = writePermissions;
+ return this;
+ }
+
+ /**
+ * Set whether the preference is enabled.
+ */
+ @NonNull
+ public Builder setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Sets whether the preference is available.
+ */
+ @NonNull
+ public Builder setAvailable(boolean available) {
+ mAvailable = available;
+ return this;
+ }
+
+ /**
+ * Sets whether the preference is writable.
+ */
+ @NonNull
+ public Builder setWritable(boolean writable) {
+ mWritable = writable;
+ return this;
+ }
+
+ /**
+ * Sets whether the preference is restricted.
+ */
+ @NonNull
+ public Builder setRestricted(boolean restricted) {
+ mRestricted = restricted;
+ return this;
+ }
+
+ /**
+ * Sets the preference write-level sensitivity.
+ */
+ @NonNull
+ public Builder setWriteSensitivity(@WriteSensitivity int sensitivity) {
+ mSensitivity = sensitivity;
+ return this;
+ }
+
+ /**
+ * Sets the intent to launch the host app page for this preference.
+ */
+ @NonNull
+ public Builder setLaunchIntent(@Nullable PendingIntent launchIntent) {
+ mLaunchIntent = launchIntent;
+ return this;
+ }
+
+ /**
+ * Sets additional fields specific to this preference. Treat all data as optional.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Constructs an immutable {@link SettingsPreferenceMetadata} object.
+ */
+ @NonNull
+ public SettingsPreferenceMetadata build() {
+ return new SettingsPreferenceMetadata(this);
+ }
+ }
+}
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceService.java b/core/java/android/service/settings/preferences/SettingsPreferenceService.java
new file mode 100644
index 000000000000..4a4b5d201f09
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceService.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.Manifest;
+import android.annotation.EnforcePermission;
+import android.annotation.FlaggedApi;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.PermissionEnforcer;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.flags.Flags;
+
+/**
+ * Base class for a service that exposes its settings preferences to external access.
+ * <p>This class is to be implemented by apps that contribute to the Android Settings surface.
+ * Access to this service is permission guarded by
+ * {@link android.permission.READ_SYSTEM_PREFERENCES} for binding and reading, and guarded by both
+ * {@link android.permission.READ_SYSTEM_PREFERENCES} and
+ * {@link android.permission.WRITE_SYSTEM_PREFERENCES} for writing. An additional checks for access
+ * control are the responsibility of the implementing class.
+ *
+ * <p>This implementation must correspond to an exported service declaration in the host app
+ * AndroidManifest.xml as follows
+ * <pre class="prettyprint">
+ * {@literal
+ * <service
+ * android:permission="android.permission.READ_SYSTEM_PREFERENCES"
+ * android:exported="true">
+ * <intent-filter>
+ * <action android:name="android.service.settings.preferences.action.PREFERENCE_SERVICE" />
+ * </intent-filter>
+ * </service>}
+ * </pre>
+ *
+ * <ul>
+ * <li>It is recommended to expose the metadata for most, if not all, preferences within a
+ * settings app, thus implementing {@link #onGetAllPreferenceMetadata}.
+ * <li>Exposing preferences for read access of their values is up to the implementer, but any
+ * exposed must be a subset of the preferences exposed in {@link #onGetAllPreferenceMetadata}.
+ * To expose a preference for read access, the implementation will contain
+ * {@link #onGetPreferenceValue}.
+ * <li>Exposing a preference for write access of their values is up to the implementer, but should
+ * be done so with extra care and consideration, both for security and privacy. These must also
+ * be a subset of those exposed in {@link #onGetAllPreferenceMetadata}. To expose a preference for
+ * write access, the implementation will contain {@link #onSetPreferenceValue}.
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public abstract class SettingsPreferenceService extends Service {
+
+ /**
+ * Intent Action corresponding to a {@link SettingsPreferenceService}. Note that any checks for
+ * such services must be accompanied by a check to ensure the host is a system application.
+ * Given an {@link android.content.pm.ApplicationInfo} you can check for
+ * {@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}, or when querying
+ * {@link PackageManager#queryIntentServices} you can provide the flag
+ * {@link PackageManager#MATCH_SYSTEM_ONLY}.
+ */
+ public static final String ACTION_PREFERENCE_SERVICE =
+ "android.service.settings.preferences.action.PREFERENCE_SERVICE";
+
+ /** @hide */
+ @NonNull
+ @Override
+ public final IBinder onBind(@Nullable Intent intent) {
+ return new ISettingsPreferenceService.Stub(
+ PermissionEnforcer.fromContext(getApplicationContext())) {
+ @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES)
+ @Override
+ public void getAllPreferenceMetadata(MetadataRequest request,
+ IMetadataCallback callback) {
+ getAllPreferenceMetadata_enforcePermission();
+ onGetAllPreferenceMetadata(request, new OutcomeReceiver<>() {
+ @Override
+ public void onResult(MetadataResult result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(@NonNull Exception error) {
+ try {
+ callback.onFailure();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ });
+ }
+
+ @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES)
+ @Override
+ public void getPreferenceValue(GetValueRequest request, IGetValueCallback callback) {
+ getPreferenceValue_enforcePermission();
+ onGetPreferenceValue(request, new OutcomeReceiver<>() {
+ @Override
+ public void onResult(GetValueResult result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(@NonNull Exception error) {
+ try {
+ callback.onFailure();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ });
+ }
+
+ @EnforcePermission(allOf = {
+ Manifest.permission.READ_SYSTEM_PREFERENCES,
+ Manifest.permission.WRITE_SYSTEM_PREFERENCES
+ })
+ @Override
+ public void setPreferenceValue(SetValueRequest request, ISetValueCallback callback) {
+ setPreferenceValue_enforcePermission();
+ onSetPreferenceValue(request, new OutcomeReceiver<>() {
+ @Override
+ public void onResult(SetValueResult result) {
+ try {
+ callback.onSuccess(result);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(@NonNull Exception error) {
+ try {
+ callback.onFailure();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ });
+ }
+ };
+ }
+
+ /**
+ * Retrieve the metadata for all exposed settings preferences within this application. This
+ * data should be a snapshot of their state at the time of this method being called.
+ * @param request object to specify request parameters
+ * @param callback object to receive result or failure of request
+ */
+ public abstract void onGetAllPreferenceMetadata(
+ @NonNull MetadataRequest request,
+ @NonNull OutcomeReceiver<MetadataResult, Exception> callback);
+
+ /**
+ * Retrieve the current value of the requested settings preference. If this value is not exposed
+ * or cannot be obtained for some reason, the corresponding result code will be set on the
+ * result object.
+ * @param request object to specify request parameters
+ * @param callback object to receive result or failure of request
+ */
+ public abstract void onGetPreferenceValue(
+ @NonNull GetValueRequest request,
+ @NonNull OutcomeReceiver<GetValueResult, Exception> callback);
+
+ /**
+ * Set the value within the request to the target settings preference. If this value cannot
+ * be written for some reason, the corresponding result code will be set on the result object.
+ * @param request object to specify request parameters
+ * @param callback object to receive result or failure of request
+ */
+ public abstract void onSetPreferenceValue(
+ @NonNull SetValueRequest request,
+ @NonNull OutcomeReceiver<SetValueResult, Exception> callback);
+}
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java b/core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java
new file mode 100644
index 000000000000..39995a47fcbe
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import static android.service.settings.preferences.SettingsPreferenceService.ACTION_PREFERENCE_SERVICE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.flags.Flags;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Client class responsible for binding to and interacting with an instance of
+ * {@link SettingsPreferenceService}.
+ * <p>This is a convenience class to handle the lifecycle of the service connection.
+ * <p>This client will only interact with one instance at a time,
+ * so if the caller requires multiple instances (multiple applications that provide settings), then
+ * the caller must create multiple client classes, one for each instance required. To find all
+ * available services, a caller may query {@link android.content.pm.PackageManager} for applications
+ * that provide the intent action {@link SettingsPreferenceService#ACTION_PREFERENCE_SERVICE} that
+ * are also system applications ({@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}).
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public class SettingsPreferenceServiceClient implements AutoCloseable {
+
+ private final Context mContext;
+ private final Intent mServiceIntent;
+ private final ServiceConnection mServiceConnection;
+ private final boolean mSystemOnly;
+ private ISettingsPreferenceService mRemoteService;
+
+ /**
+ * Construct a client for binding to a {@link SettingsPreferenceService} provided by the
+ * application corresponding to the provided package name.
+ * @param packageName - package name for which this client will initiate a service binding
+ */
+ public SettingsPreferenceServiceClient(@NonNull Context context,
+ @NonNull String packageName) {
+ this(context, packageName, true, null);
+ }
+
+ /**
+ * @hide Only to be called directly by test
+ */
+ @TestApi
+ public SettingsPreferenceServiceClient(@NonNull Context context,
+ @NonNull String packageName,
+ boolean systemOnly,
+ @Nullable ServiceConnection connectionListener) {
+ mContext = context.getApplicationContext();
+ mServiceIntent = new Intent(ACTION_PREFERENCE_SERVICE).setPackage(packageName);
+ mSystemOnly = systemOnly;
+ mServiceConnection = createServiceConnection(connectionListener);
+ }
+
+ /**
+ * Initiate binding to service.
+ * <p>If no service exists for the package provided or the package is not for a system
+ * application, no binding will occur.
+ */
+ public void start() {
+ PackageManager pm = mContext.getPackageManager();
+ PackageManager.ResolveInfoFlags flags;
+ if (mSystemOnly) {
+ flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY);
+ } else {
+ flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL);
+ }
+ List<ResolveInfo> infos = pm.queryIntentServices(mServiceIntent, flags);
+ if (infos.size() == 1) {
+ mContext.bindService(mServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ /**
+ * If there is an active service binding, unbind from that service.
+ */
+ public void stop() {
+ if (mRemoteService != null) {
+ mRemoteService = null;
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ /**
+ * Retrieve the metadata for all exposed settings preferences within the application.
+ * @param request object to specify request parameters
+ * @param executor {@link Executor} on which to invoke the receiver
+ * @param receiver callback to receive the result or failure
+ */
+ public void getAllPreferenceMetadata(
+ @NonNull MetadataRequest request,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<MetadataResult, Exception> receiver) {
+ if (mRemoteService == null) {
+ executor.execute(() ->
+ receiver.onError(new IllegalStateException("Service not ready")));
+ return;
+ }
+ try {
+ mRemoteService.getAllPreferenceMetadata(request, new IMetadataCallback.Stub() {
+ @Override
+ public void onSuccess(MetadataResult result) {
+ executor.execute(() -> receiver.onResult(result));
+ }
+
+ @Override
+ public void onFailure() {
+ executor.execute(() -> receiver.onError(
+ new IllegalStateException("Service call failure")));
+ }
+ });
+ } catch (RemoteException | RuntimeException e) {
+ executor.execute(() -> receiver.onError(e));
+ }
+ }
+
+ /**
+ * Retrieve the current value of the requested settings preference.
+ * @param request object to specify request parameters
+ * @param executor {@link Executor} on which to invoke the receiver
+ * @param receiver callback to receive the result or failure
+ */
+ public void getPreferenceValue(@NonNull GetValueRequest request,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<GetValueResult, Exception> receiver) {
+ if (mRemoteService == null) {
+ executor.execute(() ->
+ receiver.onError(new IllegalStateException("Service not ready")));
+ return;
+ }
+ try {
+ mRemoteService.getPreferenceValue(request, new IGetValueCallback.Stub() {
+ @Override
+ public void onSuccess(GetValueResult result) {
+ executor.execute(() -> receiver.onResult(result));
+ }
+
+ @Override
+ public void onFailure() {
+ executor.execute(() -> receiver.onError(
+ new IllegalStateException("Service call failure")));
+ }
+ });
+ } catch (RemoteException | RuntimeException e) {
+ executor.execute(() -> receiver.onError(e));
+ }
+ }
+
+ /**
+ * Set the value on the target settings preference.
+ * @param request object to specify request parameters
+ * @param executor {@link Executor} on which to invoke the receiver
+ * @param receiver callback to receive the result or failure
+ */
+ public void setPreferenceValue(@NonNull SetValueRequest request,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<SetValueResult, Exception> receiver) {
+ if (mRemoteService == null) {
+ executor.execute(() ->
+ receiver.onError(new IllegalStateException("Service not ready")));
+ return;
+ }
+ try {
+ mRemoteService.setPreferenceValue(request, new ISetValueCallback.Stub() {
+ @Override
+ public void onSuccess(SetValueResult result) {
+ executor.execute(() -> receiver.onResult(result));
+ }
+
+ @Override
+ public void onFailure() {
+ executor.execute(() -> receiver.onError(
+ new IllegalStateException("Service call failure")));
+ }
+ });
+ } catch (RemoteException | RuntimeException e) {
+ executor.execute(() -> receiver.onError(e));
+ }
+ }
+
+ @NonNull
+ private ServiceConnection createServiceConnection(@Nullable ServiceConnection listener) {
+ return new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mRemoteService = getPreferenceServiceInterface(service);
+ if (listener != null) {
+ listener.onServiceConnected(name, service);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mRemoteService = null;
+ if (listener != null) {
+ listener.onServiceDisconnected(name);
+ }
+ }
+ };
+ }
+
+ @NonNull
+ private ISettingsPreferenceService getPreferenceServiceInterface(@NonNull IBinder service) {
+ return ISettingsPreferenceService.Stub.asInterface(service);
+ }
+
+ /**
+ * This client handles a resource, thus is it important to appropriately close that resource
+ * when it is no longer needed.
+ * <p>This method is provided by {@link AutoCloseable} and calling it
+ * will unbind any service binding.
+ */
+ @Override
+ public void close() {
+ stop();
+ }
+}
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
new file mode 100644
index 000000000000..f056e34a0dd2
--- /dev/null
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.settings.preferences;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This objects represents a value that can be used for a particular settings preference.
+ * <p>The data type for the value will correspond to {@link #getType}. For possible types, see
+ * constants below, such as {@link #TYPE_BOOLEAN} and {@link #TYPE_STRING}.
+ * Depending on the type, the corresponding getter will contain its value. All other getters will
+ * return default values (boolean returns false, String returns null) so they should not be used.
+ * <p>See documentation on the constants for which getter method should be used.
+ */
+@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
+public final class SettingsPreferenceValue implements Parcelable {
+
+ @Type
+ private final int mType;
+ private final boolean mBooleanValue;
+ private final long mLongValue;
+ private final double mDoubleValue;
+ @Nullable
+ private final String mStringValue;
+
+ /**
+ * Returns the type indicator for Preference value.
+ */
+ @Type
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the boolean value for Preference if type is {@link #TYPE_BOOLEAN}.
+ */
+ public boolean getBooleanValue() {
+ return mBooleanValue;
+ }
+
+ /**
+ * Returns the long value for Preference if type is {@link #TYPE_LONG}.
+ */
+ public long getLongValue() {
+ return mLongValue;
+ }
+
+ /**
+ * Returns the double value for Preference if type is {@link #TYPE_DOUBLE}.
+ */
+ public double getDoubleValue() {
+ return mDoubleValue;
+ }
+
+ /**
+ * Returns the string value for Preference if type is {@link #TYPE_STRING}.
+ */
+ @Nullable
+ public String getStringValue() {
+ return mStringValue;
+ }
+
+ /** @hide */
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_BOOLEAN,
+ TYPE_LONG,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /** Value is of type boolean. Access via {@link #getBooleanValue}. */
+ public static final int TYPE_BOOLEAN = 0;
+ /** Value is of type long. Access via {@link #getLongValue()}. */
+ public static final int TYPE_LONG = 1;
+ /** Value is of type double. Access via {@link #getDoubleValue()}. */
+ public static final int TYPE_DOUBLE = 2;
+ /** Value is of type string. Access via {@link #getStringValue}. */
+ public static final int TYPE_STRING = 3;
+
+ private SettingsPreferenceValue(@NonNull Builder builder) {
+ mType = builder.mType;
+ mBooleanValue = builder.mBooleanValue;
+ mLongValue = builder.mLongValue;
+ mDoubleValue = builder.mDoubleValue;
+ mStringValue = builder.mStringValue;
+ }
+
+ private SettingsPreferenceValue(@NonNull Parcel in) {
+ mType = in.readInt();
+ mBooleanValue = in.readBoolean();
+ mLongValue = in.readLong();
+ mDoubleValue = in.readDouble();
+ mStringValue = in.readString8();
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeBoolean(mBooleanValue);
+ dest.writeLong(mLongValue);
+ dest.writeDouble(mDoubleValue);
+ dest.writeString8(mStringValue);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable Creator for {@link SettingsPreferenceValue}.
+ */
+ @NonNull
+ public static final Creator<SettingsPreferenceValue> CREATOR = new Creator<>() {
+ @Override
+ public SettingsPreferenceValue createFromParcel(@NonNull Parcel in) {
+ return new SettingsPreferenceValue(in);
+ }
+
+ @Override
+ public SettingsPreferenceValue[] newArray(int size) {
+ return new SettingsPreferenceValue[size];
+ }
+ };
+
+ /**
+ * Builder to construct {@link SettingsPreferenceValue}.
+ */
+ public static final class Builder {
+ @Type
+ private final int mType;
+ private boolean mBooleanValue;
+ private long mLongValue;
+ private double mDoubleValue;
+ private String mStringValue;
+
+ /**
+ * Create Builder instance.
+ * @param type type indicator for preference value
+ */
+ public Builder(@Type int type) {
+ mType = type;
+ }
+
+ /**
+ * Sets boolean value for Preference.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setBooleanValue(boolean booleanValue) {
+ mBooleanValue = booleanValue;
+ return this;
+ }
+
+ /**
+ * Sets long value for Preference.
+ */
+ @NonNull
+ public Builder setLongValue(long longValue) {
+ mLongValue = longValue;
+ return this;
+ }
+
+ /**
+ * Sets floating point value for Preference.
+ */
+ @NonNull
+ public Builder setDoubleValue(double doubleValue) {
+ mDoubleValue = doubleValue;
+ return this;
+ }
+
+ /**
+ * Sets string value for Preference.
+ */
+ @NonNull
+ public Builder setStringValue(@Nullable String stringValue) {
+ mStringValue = stringValue;
+ return this;
+ }
+
+ /**
+ * Constructs an immutable {@link SettingsPreferenceValue} object.
+ */
+ @NonNull
+ public SettingsPreferenceValue build() {
+ return new SettingsPreferenceValue(this);
+ }
+ }
+}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 2e660fc1f157..7d79fd3d44ea 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -1164,7 +1164,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
public boolean startRecognition(@RecognitionFlags int recognitionFlags) {
if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")");
synchronized (mLock) {
- return startRecognitionLocked(recognitionFlags, null /* data */) == STATUS_OK;
+ return startRecognitionLocked(recognitionFlags, /* data= */new byte[0]) == STATUS_OK;
}
}
@@ -1496,8 +1496,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
@GuardedBy("mLock")
- private int startRecognitionLocked(int recognitionFlags,
- @Nullable byte[] data) {
+ @SuppressWarnings("FlaggedApi") // RecognitionConfig.Builder is available internally.
+ private int startRecognitionLocked(int recognitionFlags, @NonNull byte[] data) {
if (DBG) {
Slog.d(TAG, "startRecognition("
+ recognitionFlags
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index f76e6cee13f0..bcdd4775c164 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -28,6 +28,6 @@ oneway interface IWallpaperService {
void attach(IWallpaperConnection connection,
IBinder windowToken, int windowType, boolean isPreview,
int reqWidth, int reqHeight, in Rect padding, int displayId, int which,
- in WallpaperInfo info, in @nullable WallpaperDescription description);
+ in WallpaperInfo info, in WallpaperDescription description);
void detach(IBinder windowToken);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 131fdc895841..2061abac248e 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,6 +17,7 @@
package android.service.wallpaper;
import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
+import static android.app.Flags.liveWallpaperContentHandling;
import static android.app.WallpaperManager.COMMAND_FREEZE;
import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.app.WallpaperManager.SetWallpaperFlags;
@@ -2624,7 +2625,7 @@ public abstract class WallpaperService extends Service {
private void doAttachEngine() {
Trace.beginSection("WPMS.onCreateEngine");
Engine engine;
- if (mDescription != null) {
+ if (liveWallpaperContentHandling()) {
engine = onCreateEngine(mDescription);
} else {
engine = onCreateEngine();
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index bce51f297aff..1df3b4332754 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -37,6 +37,7 @@ import android.telephony.TelephonyManager.EmergencyCallbackModeType;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.MediaQualityStatus;
+import android.telephony.satellite.NtnSignalStrength;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
@@ -1706,6 +1707,11 @@ public class PhoneStateListener {
@NetworkRegistrationInfo.ServiceType int[] availableServices) {
// not supported on the deprecated interface - Use TelephonyCallback instead
}
+
+ public final void onCarrierRoamingNtnSignalStrengthChanged(
+ @NonNull NtnSignalStrength ntnSignalStrength) {
+ // not supported on the deprecated interface - Use TelephonyCallback instead
+ }
}
private void log(String s) {
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 64a5533cbe69..0d1dc4611343 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -30,6 +30,7 @@ import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.MediaQualityStatus;
import android.telephony.ims.MediaThreshold;
+import android.telephony.satellite.NtnSignalStrength;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -695,6 +696,15 @@ public class TelephonyCallback {
public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44;
/**
+ * Event for listening to carrier roaming non-terrestrial network signal strength changes.
+ *
+ * @see CarrierRoamingNtnModeListener
+ *
+ * @hide
+ */
+ public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45;
+
+ /**
* @hide
*/
@IntDef(prefix = {"EVENT_"}, value = {
@@ -741,7 +751,8 @@ public class TelephonyCallback {
EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED,
EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED,
EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED,
- EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED
+ EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED,
+ EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface TelephonyEvent {
@@ -1805,6 +1816,14 @@ public class TelephonyCallback {
*/
default void onCarrierRoamingNtnAvailableServicesChanged(
@NetworkRegistrationInfo.ServiceType List<Integer> availableServices) {}
+
+ /**
+ * Callback invoked when carrier roaming non-terrestrial network signal strength changes.
+ *
+ * @param ntnSignalStrength non-terrestrial network signal strength.
+ */
+ default void onCarrierRoamingNtnSignalStrengthChanged(
+ @NonNull NtnSignalStrength ntnSignalStrength) {}
}
/**
@@ -2270,5 +2289,18 @@ public class TelephonyCallback {
Binder.withCleanCallingIdentity(() -> mExecutor.execute(
() -> listener.onCarrierRoamingNtnAvailableServicesChanged(ServiceList)));
}
+
+ public void onCarrierRoamingNtnSignalStrengthChanged(
+ @NonNull NtnSignalStrength ntnSignalStrength) {
+ if (!Flags.carrierRoamingNbIotNtn()) return;
+
+ CarrierRoamingNtnModeListener listener =
+ (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+ () -> listener.onCarrierRoamingNtnSignalStrengthChanged(ntnSignalStrength)));
+
+ }
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 1dab2cf75594..90b0bb34c145 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -47,6 +47,7 @@ import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.MediaQualityStatus;
+import android.telephony.satellite.NtnSignalStrength;
import android.telephony.satellite.SatelliteStateChangeListener;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1137,6 +1138,23 @@ public class TelephonyRegistryManager {
}
/**
+ * Notify external listeners that carrier roaming non-terrestrial network
+ * signal strength changed.
+ * @param subId subscription ID.
+ * @param ntnSignalStrength non-terrestrial network signal strength.
+ * @hide
+ */
+ public final void notifyCarrierRoamingNtnSignalStrengthChanged(int subId,
+ @NonNull NtnSignalStrength ntnSignalStrength) {
+ try {
+ sRegistry.notifyCarrierRoamingNtnSignalStrengthChanged(subId, ntnSignalStrength);
+ } catch (RemoteException ex) {
+ // system server crash
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Processes potential event changes from the provided {@link TelephonyCallback}.
*
* @param telephonyCallback callback for monitoring callback changes to the telephony state.
@@ -1293,6 +1311,7 @@ public class TelephonyRegistryManager {
eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED);
eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED);
eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED);
}
return eventList;
}
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index e830d89d3116..f43f172d7d5b 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -163,10 +163,12 @@ flag {
}
flag {
- name: "typeface_redesign"
+ name: "typeface_redesign_readonly"
namespace: "text"
description: "Decouple variation settings, weight and style information from Typeface class"
bug: "361260253"
+ # This feature does not support runtime flag switch which leads crash in System UI.
+ is_fixed_read_only: true
}
flag {
@@ -202,3 +204,10 @@ flag {
description: "Deprecate the Paint#elegantTextHeight API and stick it to true"
bug: "349519475"
}
+
+flag {
+ name: "vertical_text_layout"
+ namespace: "text"
+ description: "Make Paint class work for vertical layout text."
+ bug: "355296926"
+}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 8358b9a51adb..1dd9d46fdfb7 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -75,8 +75,7 @@ import java.net.UnknownHostException;
@android.ravenwood.annotation.RavenwoodClassLoadHook(
"com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded")
// Uncomment the following annotation to switch to the Java substitution version.
-//@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
-// "com.android.platform.test.ravenwood.nativesubstitution.Log_host")
+@android.ravenwood.annotation.RavenwoodRedirectionClass("Log_host")
public final class Log {
/** @hide */
@IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE})
@@ -250,6 +249,7 @@ public final class Log {
* tag limit of concern after this API level.
*/
@FastNative
+ @android.ravenwood.annotation.RavenwoodRedirect
public static native boolean isLoggable(@Nullable String tag, @Level int level);
/**
@@ -425,6 +425,7 @@ public final class Log {
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodRedirect
public static native int println_native(int bufID, int priority, String tag, String msg);
/**
@@ -452,6 +453,7 @@ public final class Log {
* Return the maximum payload the log daemon accepts without truncation.
* @return LOGGER_ENTRY_MAX_PAYLOAD.
*/
+ @android.ravenwood.annotation.RavenwoodRedirect
private static native int logger_entry_max_payload_native();
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 910e644f7b6f..a1a9fc697271 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1549,8 +1549,9 @@ public final class Display {
// Although we only care about the HDR/SDR ratio changing, that can also come in the
// form of the larger DISPLAY_CHANGED event
mGlobal.registerDisplayListener(toRegister, executor,
- DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED
- | DisplayManagerGlobal.EVENT_DISPLAY_CHANGED,
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
ActivityThread.currentPackageName());
}
@@ -1785,7 +1786,12 @@ public final class Display {
* {@code getWindowManager()} or {@code getSystemService(Context.WINDOW_SERVICE)}), the
* returned metrics provide the size of the current app window. As a result, in
* multi-window mode, the returned size can be smaller than the size of the device
- * screen.
+ * screen. System decoration handling may vary depending on API level:
+ * <ul>
+ * <li>API level 35 and above, the window size will be returned.
+ * <li>API level 34 and below, the window size minus system decoration areas and
+ * display cutout is returned.
+ * </ul>
* <li>If metrics are requested from a non-activity context (for example, the application
* context, where the WindowManager is accessed by
* {@code getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}), the
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8f112f338a00..4ff04d5c1fa6 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -447,7 +447,6 @@ public final class DisplayInfo implements Parcelable {
&& Objects.equals(displayCutout, other.displayCutout)
&& rotation == other.rotation
&& modeId == other.modeId
- && renderFrameRate == other.renderFrameRate
&& hasArrSupport == other.hasArrSupport
&& Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)
&& defaultModeId == other.defaultModeId
@@ -705,6 +704,9 @@ public final class DisplayInfo implements Parcelable {
if (refreshRateOverride > 0) {
return refreshRateOverride;
}
+ if (renderFrameRate > 0) {
+ return renderFrameRate;
+ }
if (supportedModes.length == 0) {
return 0;
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 1fe06d474803..66d64d711e58 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -176,7 +176,7 @@ public class HapticFeedbackConstants {
/**
* The user is executing a swipe/drag-style gesture, such as pull-to-refresh, where the
- * gesture action is “eligible” at a certain threshold of movement, and can be cancelled by
+ * gesture action is "eligible" at a certain threshold of movement, and can be cancelled by
* moving back past the threshold. This constant indicates that the user's motion has just
* passed the threshold for the action to be activated on release.
*
@@ -186,7 +186,7 @@ public class HapticFeedbackConstants {
/**
* The user is executing a swipe/drag-style gesture, such as pull-to-refresh, where the
- * gesture action is “eligible” at a certain threshold of movement, and can be cancelled by
+ * gesture action is "eligible" at a certain threshold of movement, and can be cancelled by
* moving back past the threshold. This constant indicates that the user's motion has just
* re-crossed back "under" the threshold for the action to be activated, meaning the gesture is
* currently in a cancelled state.
diff --git a/core/java/android/view/HapticScrollFeedbackProvider.java b/core/java/android/view/HapticScrollFeedbackProvider.java
index 0001176220b5..c3fb855eb1ff 100644
--- a/core/java/android/view/HapticScrollFeedbackProvider.java
+++ b/core/java/android/view/HapticScrollFeedbackProvider.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.flags.Flags.dynamicViewRotaryHapticsConfiguration;
+
import android.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
@@ -41,13 +43,8 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider {
private final View mView;
private final ViewConfiguration mViewConfig;
- /**
- * Flag to disable the logic in this class if the View-based scroll haptics implementation is
- * enabled. If {@code false}, this class will continue to run despite the View's scroll
- * haptics implementation being enabled. This value should be set to {@code true} when this
- * class is directly used by the View class.
- */
- private final boolean mDisabledIfViewPlaysScrollHaptics;
+ /** Whether or not this provider is being used directly by the View class. */
+ private final boolean mIsFromView;
// Info about the cause of the latest scroll event.
@@ -65,17 +62,23 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider {
private boolean mHapticScrollFeedbackEnabled = false;
public HapticScrollFeedbackProvider(@NonNull View view) {
- this(view, ViewConfiguration.get(view.getContext()),
- /* disabledIfViewPlaysScrollHaptics= */ true);
+ this(view, ViewConfiguration.get(view.getContext()), /* isFromView= */ false);
}
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public HapticScrollFeedbackProvider(
- View view, ViewConfiguration viewConfig, boolean disabledIfViewPlaysScrollHaptics) {
+ View view, ViewConfiguration viewConfig, boolean isFromView) {
mView = view;
mViewConfig = viewConfig;
- mDisabledIfViewPlaysScrollHaptics = disabledIfViewPlaysScrollHaptics;
+ mIsFromView = isFromView;
+ if (dynamicViewRotaryHapticsConfiguration() && !isFromView) {
+ // Disable the View class's rotary scroll feedback logic if this provider is not being
+ // directly used by the View class. This is to avoid double rotary scroll feedback:
+ // one from the View class, and one from this provider instance (i.e. mute the View
+ // class's rotary feedback and enable this provider).
+ view.disableRotaryScrollFeedback();
+ }
}
@Override
@@ -151,7 +154,8 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider {
mAxis = axis;
mDeviceId = deviceId;
- if (mDisabledIfViewPlaysScrollHaptics
+ if (!dynamicViewRotaryHapticsConfiguration()
+ && !mIsFromView
&& (source == InputDevice.SOURCE_ROTARY_ENCODER)
&& mViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) {
mHapticScrollFeedbackEnabled = false;
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index b80146505a1b..19e0913fbc65 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.view.animation.BackGestureInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.ImeTracker;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
@@ -142,9 +143,15 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
// control has been cancelled by the system. This can happen in multi-window mode for
// example (i.e. split-screen or activity-embedding)
notifyHideIme();
- return;
+ } else {
+ startPostCommitAnim(/*hideIme*/ true);
+ }
+ if (Flags.refactorInsetsController()) {
+ // Unregister all IME back callbacks so that back events are sent to the next callback
+ // even while the hide animation is playing
+ mInsetsController.getHost().getInputMethodManager().getImeOnBackInvokedDispatcher()
+ .preliminaryClear();
}
- startPostCommitAnim(/*hideIme*/ true);
}
private void setPreCommitProgress(float progress) {
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 97148969e17f..2d2f79d76008 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.UiThread;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -150,6 +151,17 @@ public final class ImeFocusController {
if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
return InputMethodManager.DISPATCH_NOT_HANDLED;
}
+ if (Flags.refactorInsetsController() && event instanceof KeyEvent keyEvent
+ && keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ final var insetsController = mViewRootImpl.getInsetsController();
+ if (insetsController.getAnimationType(WindowInsets.Type.ime())
+ == InsetsController.ANIMATION_TYPE_HIDE
+ || insetsController.isPredictiveBackImeHideAnimInProgress()) {
+ // if there is an ongoing hide animation, the back event should not be dispatched
+ // to the IME.
+ return InputMethodManager.DISPATCH_NOT_HANDLED;
+ }
+ }
final InputMethodManager imm =
mViewRootImpl.mContext.getSystemService(InputMethodManager.class);
if (imm == null) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 25d2246424de..b0813f3a98f6 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1344,6 +1344,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean fromPredictiveBack) {
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+ if (Flags.refactorInsetsController() && !fromPredictiveBack && !visible
+ && (types & ime()) != 0 && (mRequestedVisibleTypes & ime()) != 0) {
+ // Clear IME back callbacks if a IME hide animation is requested
+ mHost.getInputMethodManager().getImeOnBackInvokedDispatcher().preliminaryClear();
+ }
// Basically, we accept the requested visibilities from the upstream callers...
setRequestedVisibleTypes(visible ? types : 0, types);
@@ -1905,7 +1910,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
mImeSourceConsumer.onWindowFocusLost();
}
- @VisibleForTesting
+ /** Returns the current {@link AnimationType} of an {@link InsetsType}. */
+ @VisibleForTesting(visibility = PACKAGE)
public @AnimationType int getAnimationType(@InsetsType int type) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
@@ -1921,6 +1927,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final @InsetsType int requestedVisibleTypes =
(mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
if (mRequestedVisibleTypes != requestedVisibleTypes) {
+ if (Flags.refactorInsetsController() && (mRequestedVisibleTypes & ime()) == 0
+ && (requestedVisibleTypes & ime()) != 0) {
+ // In case the IME back callbacks have been preliminarily cleared before, let's
+ // reregister them. This can happen if an IME hide animation was interrupted and the
+ // IME is requested to be shown again.
+ getHost().getInputMethodManager().getImeOnBackInvokedDispatcher()
+ .undoPreliminaryClear();
+ }
ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)",
requestedVisibleTypes, mRequestedVisibleTypes);
mRequestedVisibleTypes = requestedVisibleTypes;
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 78773529294a..acbd95bf6810 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -212,8 +212,7 @@ public class InsetsSourceControl implements Parcelable {
&& mInitiallyVisible == that.mInitiallyVisible
&& mSurfacePosition.equals(that.mSurfacePosition)
&& mInsetsHint.equals(that.mInsetsHint)
- && mSkipAnimationOnce == that.mSkipAnimationOnce
- && Objects.equals(mImeStatsToken, that.mImeStatsToken);
+ && mSkipAnimationOnce == that.mSkipAnimationOnce;
}
@Override
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index dddc408ed9db..38e4e2760d25 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -935,7 +935,6 @@ public class KeyEvent extends InputEvent implements Parcelable {
*/
public static final int KEYCODE_MACRO_4 = 316;
/** Key code constant: To open emoji picker */
- @FlaggedApi(Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE)
public static final int KEYCODE_EMOJI_PICKER = 317;
/**
* Key code constant: To take a screenshot
@@ -944,15 +943,80 @@ public class KeyEvent extends InputEvent implements Parcelable {
* unlike {@code KEYCODE_SYSRQ} which is sent to the app first and only if the app
* doesn't handle it, the framework handles it (to take a screenshot).
*/
- @FlaggedApi(Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE)
public static final int KEYCODE_SCREENSHOT = 318;
+ /** Key code constant: To start dictate to an input field */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_DICTATE = 319;
+ /**
+ * Key code constant: AC New
+ *
+ * e.g. To create a new instance of a window, open a new tab, etc.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_NEW = 320;
+ /**
+ * Key code constant: AC Close
+ *
+ * e.g. To close current instance of the application window, close the current tab, etc.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_CLOSE = 321;
+ /** Key code constant: To toggle 'Do Not Disturb' mode */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_DO_NOT_DISTURB = 322;
+ /** Key code constant: To Print */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_PRINT = 323;
+ /** Key code constant: To Lock the screen */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_LOCK = 324;
+ /** Key code constant: To toggle fullscreen mode (on the current application) */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_FULLSCREEN = 325;
+ /** Key code constant: F13 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F13 = 326;
+ /** Key code constant: F14 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F14 = 327;
+ /** Key code constant: F15 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F15 = 328;
+ /** Key code constant: F16 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F16 = 329;
+ /** Key code constant: F17 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F17 = 330;
+ /** Key code constant: F18 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F18 = 331;
+ /** Key code constant: F19 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F19 = 332;
+ /** Key code constant: F20 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F20 = 333;
+ /** Key code constant: F21 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F21 = 334;
+ /** Key code constant: F22 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F22 = 335;
+ /** Key code constant: F23 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F23 = 336;
+ /** Key code constant: F24 key. */
+ @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ public static final int KEYCODE_F24 = 337;
/**
* Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
* @hide
*/
@TestApi
- public static final int LAST_KEYCODE = KEYCODE_SCREENSHOT;
+ @SuppressWarnings("FlaggedApi")
+ public static final int LAST_KEYCODE = KEYCODE_F24;
/** @hide */
@IntDef(prefix = {"KEYCODE_"}, value = {
@@ -1275,6 +1339,25 @@ public class KeyEvent extends InputEvent implements Parcelable {
KEYCODE_MACRO_4,
KEYCODE_EMOJI_PICKER,
KEYCODE_SCREENSHOT,
+ KEYCODE_DICTATE,
+ KEYCODE_NEW,
+ KEYCODE_CLOSE,
+ KEYCODE_DO_NOT_DISTURB,
+ KEYCODE_PRINT,
+ KEYCODE_LOCK,
+ KEYCODE_FULLSCREEN,
+ KEYCODE_F13,
+ KEYCODE_F14,
+ KEYCODE_F15,
+ KEYCODE_F16,
+ KEYCODE_F17,
+ KEYCODE_F18,
+ KEYCODE_F19,
+ KEYCODE_F20,
+ KEYCODE_F21,
+ KEYCODE_F22,
+ KEYCODE_F23,
+ KEYCODE_F24,
})
@Retention(RetentionPolicy.SOURCE)
@interface KeyCode {}
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
index 59c2598f00f0..5e1eadae0953 100644
--- a/core/java/android/view/RoundScrollbarRenderer.java
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -35,7 +35,9 @@ import android.view.flags.Flags;
* @hide
*/
public class RoundScrollbarRenderer {
- private static final String BLUECHIP_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled";
+ /** @hide */
+ public static final String BLUECHIP_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled";
+
// The range of the scrollbar position represented as an angle in degrees.
private static final float SCROLLBAR_ANGLE_RANGE = 28.8f;
private static final float MAX_SCROLLBAR_ANGLE_SWIPE = 26.3f; // 90%
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b8b22e283175..206c73756088 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -312,6 +312,7 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeNotifyShutdown();
private static native void nativeSetLuts(long transactionObj, long nativeObject,
float[] buffers, int[] slots, int[] dimensions, int[] sizes, int[] samplingKeys);
+ private static native void nativeEnableDebugLogCallPoints(long transactionObj);
/**
* Transforms that can be applied to buffers as they are displayed to a window.
@@ -2988,7 +2989,6 @@ public final class SurfaceControl implements Parcelable {
private void apply(boolean sync, boolean oneWay) {
applyResizedSurfaces();
notifyReparentedSurfaces();
- nativeApplyTransaction(mNativeObject, sync, oneWay);
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
@@ -2997,6 +2997,7 @@ public final class SurfaceControl implements Parcelable {
if (mCalls != null) {
mCalls.clear();
}
+ nativeApplyTransaction(mNativeObject, sync, oneWay);
}
/**
@@ -4605,7 +4606,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * TODO(b/366484871): To be removed once we have some logging in native
* This is called when BlastBufferQueue.mergeWithNextTransaction() is called from java, and
* for the purposes of logging that path.
*/
@@ -4616,6 +4616,7 @@ public final class SurfaceControl implements Parcelable {
if (mCalls != null) {
mCalls.clear();
}
+ nativeEnableDebugLogCallPoints(mNativeObject);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e49eec69fff5..049189f8af8d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -30,9 +30,11 @@ import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+import static android.view.accessibility.Flags.FLAG_DEPRECATE_ACCESSIBILITY_ANNOUNCEMENT_APIS;
import static android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION;
import static android.view.accessibility.Flags.removeChildHoverCheckForTouchExploration;
import static android.view.accessibility.Flags.supplementalDescription;
+import static android.view.accessibility.Flags.supportMultipleLabeledby;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
@@ -42,6 +44,7 @@ import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_H
import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.calculateBoundsInParentFromBoundsInScreen;
import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.flags.Flags.toolkitFrameRateAnimationBugfix25q1;
@@ -62,6 +65,7 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS;
+import static com.android.window.flags.Flags.FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW;
import static java.lang.Math.max;
@@ -88,6 +92,8 @@ import android.annotation.TestApi;
import android.annotation.UiContext;
import android.annotation.UiThread;
import android.app.PendingIntent;
+import android.app.jank.AppJankStats;
+import android.app.jank.JankTracker;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AutofillOptions;
import android.content.ClipData;
@@ -970,6 +976,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static boolean sAlwaysRemeasureExactly = false;
/**
+ * When true calculates the bounds in parent from bounds in screen relative to its parents.
+ * This addresses the deprecated API (setBoundsInParent) in Compose, which causes empty
+ * getBoundsInParent call for Compose apps.
+ */
+ private static boolean sCalculateBoundsInParentFromBoundsInScreenFlagValue = false;
+
+ /**
* When true makes it possible to use onMeasure caches also when the force layout flag is
* enabled. This helps avoiding multiple measures in the same frame with the same dimensions.
*/
@@ -2561,6 +2574,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
+ sCalculateBoundsInParentFromBoundsInScreenFlagValue =
+ calculateBoundsInParentFromBoundsInScreen();
sUseMeasureCacheDuringForceLayoutFlagValue = enableUseMeasureCacheDuringForceLayout();
}
@@ -5538,10 +5553,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Flag indicating that this drag will result in the caller activity's task to be hidden for the
- * duration of the drag, this means that the source activity will not receive drag events for
- * the current drag gesture. Only the current voice interaction service may use this flag.
- * @hide
+ * duration of the drag, which means that the source activity will not receive drag events for
+ * the current drag gesture. Only the current
+ * {@link android.service.voice.VoiceInteractionService} may use this flag.
*/
+ @FlaggedApi(FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW)
public static final int DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START = 1 << 14;
/**
@@ -8941,44 +8957,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT}
- * {@link AccessibilityEvent} to suggest that an accessibility service announce the
- * specified text to its users.
- * <p>
- * Note: The event generated with this API carries no semantic meaning, and is appropriate only
- * in exceptional situations. Apps can generally achieve correct behavior for accessibility by
- * accurately supplying the semantics of their UI.
- * They should not need to specify what exactly is announced to users.
+ * Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT} {@link
+ * AccessibilityEvent} to suggest that an accessibility service announce the specified text to
+ * its users.
*
- * <p>
- * In general, only announce transitions and don't generate a confirmation message for simple
- * actions like a button press. Label your controls concisely and precisely instead, and for
- * significant UI changes like window changes, use
- * {@link android.app.Activity#setTitle(CharSequence)} and
- * {@link #setAccessibilityPaneTitle(CharSequence)}.
+ * <p>Note: The event generated with this API carries no semantic meaning, and accessibility
+ * services may choose to ignore it. Apps that accurately supply accessibility with the
+ * semantics of their UI should not need to specify what exactly is announced.
*
- * <p>
- * Use {@link #setAccessibilityLiveRegion(int)} to inform the user of changes to critical
+ * <p>In general, do not attempt to generate announcements as confirmation message for simple
+ * actions like a button press. Label your controls concisely and precisely instead.
+ *
+ * <p>To convey significant UI changes like window changes, use {@link
+ * android.app.Activity#setTitle(CharSequence)} and {@link
+ * #setAccessibilityPaneTitle(CharSequence)}.
+ *
+ * <p>Use {@link #setAccessibilityLiveRegion(int)} to inform the user of changes to critical
* views within the user interface. These should still be used sparingly as they may generate
* announcements every time a View is updated.
*
- * <p>
- * For notifying users about errors, such as in a login screen with text that displays an
- * "incorrect password" notification, that view should send an AccessibilityEvent of type
- * {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set
- * {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose
- * error-setting methods that support accessibility automatically. For example, instead of
- * explicitly sending this event when using a TextView, use
- * {@link android.widget.TextView#setError(CharSequence)}.
- *
- * <p>
- * Use {@link #setStateDescription(CharSequence)} to convey state changes to views within the
+ * <p>Use {@link #setStateDescription(CharSequence)} to convey state changes to views within the
* user interface. While a live region may send different types of events generated by the view,
* state description will send {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events of
* type {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_STATE_DESCRIPTION}.
*
+ * <p>For notifying users about errors, such as in a login screen with text that displays an
+ * "incorrect password" notification, set {@link AccessibilityNodeInfo#setError(CharSequence)}
+ * and dispatch an {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event with a change
+ * type of {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR}, instead. Some widgets may
+ * expose methods that convey error states to accessibility automatically, such as {@link
+ * android.widget.TextView#setError(CharSequence)}, which manages these accessibility semantics
+ * and event dispatch for callers.
+ *
+ * @deprecated Use one of the methods described in the documentation above to semantically
+ * describe UI instead of using an announcement, as accessibility services may choose to
+ * ignore events dispatched with this method.
* @param text The announcement text.
*/
+ @FlaggedApi(FLAG_DEPRECATE_ACCESSIBILITY_ANNOUNCEMENT_APIS)
+ @Deprecated
public void announceForAccessibility(CharSequence text) {
if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
AccessibilityEvent event = AccessibilityEvent.obtain(
@@ -9806,7 +9823,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
structure.setChildCount(1);
final ViewStructure root = structure.newChild(0);
if (info != null) {
- populateVirtualStructure(root, provider, info, forAutofill);
+ populateVirtualStructure(root, provider, info, null, forAutofill);
info.recycle();
} else {
Log.w(AUTOFILL_LOG_TAG, "AccessibilityNodeInfo is null.");
@@ -11105,11 +11122,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private void populateVirtualStructure(ViewStructure structure,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
- boolean forAutofill) {
+ @Nullable AccessibilityNodeInfo parentInfo, boolean forAutofill) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
null, null, info.getViewIdResourceName());
Rect rect = structure.getTempRect();
- info.getBoundsInParent(rect);
+ // The bounds in parent for Jetpack Compose views aren't set as setBoundsInParent is
+ // deprecated, and only setBoundsInScreen is called.
+ // The bounds in parent can be calculated by diff'ing the child view's bounds in screen with
+ // the parent's.
+ if (sCalculateBoundsInParentFromBoundsInScreenFlagValue) {
+ getBoundsInParent(info, parentInfo, rect);
+ } else {
+ info.getBoundsInParent(rect);
+ }
structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
structure.setVisibility(VISIBLE);
structure.setEnabled(info.isEnabled());
@@ -11193,13 +11218,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
if (cinfo != null) {
ViewStructure child = structure.newChild(i);
- populateVirtualStructure(child, provider, cinfo, forAutofill);
+ populateVirtualStructure(child, provider, cinfo, info, forAutofill);
cinfo.recycle();
}
}
}
}
+ private void getBoundsInParent(@NonNull AccessibilityNodeInfo info,
+ @Nullable AccessibilityNodeInfo parentInfo, @NonNull Rect rect) {
+ info.getBoundsInParent(rect);
+ // Fallback to calculate bounds in parent by diffing the bounds in
+ // screen if it's all 0.
+ if ((rect.left | rect.top | rect.right | rect.bottom) == 0) {
+ if (parentInfo != null) {
+ Rect parentBoundsInScreen = parentInfo.getBoundsInScreen();
+ Rect boundsInScreen = info.getBoundsInScreen();
+ rect.set(boundsInScreen.left - parentBoundsInScreen.left,
+ boundsInScreen.top - parentBoundsInScreen.top,
+ boundsInScreen.right - parentBoundsInScreen.left,
+ boundsInScreen.bottom - parentBoundsInScreen.top);
+ } else {
+ info.getBoundsInScreen(rect);
+ }
+ }
+ }
+
/**
* Dispatch creation of {@link ViewStructure} down the hierarchy. The default
* implementation calls {@link #onProvideStructure} and
@@ -11363,7 +11407,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
View label = rootView.findLabelForView(this, mID);
if (label != null) {
- info.setLabeledBy(label);
+ if (supportMultipleLabeledby()) {
+ info.addLabeledBy(label);
+ } else {
+ info.setLabeledBy(label);
+ }
}
if ((mAttachInfo.mAccessibilityFetchFlags
@@ -16706,9 +16754,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED;
}
}
- final boolean processForRotaryScrollHaptics =
- isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0);
- if (processForRotaryScrollHaptics) {
+ if (isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0)) {
mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT;
mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT;
}
@@ -16725,7 +16771,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Process scroll haptics after `onGenericMotionEvent`, since that's where scrolling usually
// happens. Some views may return false from `onGenericMotionEvent` even if they have done
// scrolling, so disregard the return value when processing for scroll haptics.
- if (processForRotaryScrollHaptics) {
+ // Check for `PFLAG4_ROTARY_HAPTICS_ENABLED` again, because the View implementation may
+ // call `disableRotaryScrollFeedback` in `onGenericMotionEvent`, which could change the
+ // value of `PFLAG4_ROTARY_HAPTICS_ENABLED`.
+ if (isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0)) {
if ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT) != 0) {
doRotaryProgressForScrollHaptics(event);
} else {
@@ -18668,7 +18717,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private HapticScrollFeedbackProvider getScrollFeedbackProvider() {
if (mScrollFeedbackProvider == null) {
mScrollFeedbackProvider = new HapticScrollFeedbackProvider(this,
- ViewConfiguration.get(mContext), /* disabledIfViewPlaysScrollHaptics= */ false);
+ ViewConfiguration.get(mContext), /* isFromView= */ true);
}
return mScrollFeedbackProvider;
}
@@ -18698,6 +18747,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Disables the rotary scroll feedback implementation of the View class.
+ *
+ * <p>Note that this does NOT disable all rotary scroll feedback; it just disables the logic
+ * implemented within the View class. The child implementation of the View may implement its own
+ * rotary scroll feedback logic or use {@link ScrollFeedbackProvider} to generate rotary scroll
+ * feedback.
+ */
+ void disableRotaryScrollFeedback() {
+ // Force set PFLAG4_ROTARY_HAPTICS_DETERMINED to avoid recalculating
+ // PFLAG4_ROTARY_HAPTICS_ENABLED under any circumstance.
+ mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED;
+ mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_ENABLED;
+ }
+
+ /**
* This is called in response to an internal scroll in this view (i.e., the
* view scrolled its own contents). This is typically as a result of
* {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been
@@ -23838,12 +23902,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
draw(canvas);
}
- }
- // For VRR to vote the preferred frame rate
- if (sToolkitSetFrameRateReadOnlyFlagValue
- && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
- votePreferredFrameRate();
+ // For VRR to vote the preferred frame rate
+ if (sToolkitSetFrameRateReadOnlyFlagValue
+ && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
+ votePreferredFrameRate();
+ }
}
} finally {
renderNode.endRecording();
@@ -34376,4 +34440,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
boolean getSelfRequestedFrameRateFlag() {
return (mPrivateFlags4 & PFLAG4_SELF_REQUESTED_FRAME_RATE) != 0;
}
+
+ /**
+ * Called from apps when they want to report jank stats to the system.
+ * @param appJankStats the stats that will be merged with the stats collected by the system.
+ */
+ @FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void reportAppJankStats(@NonNull AppJankStats appJankStats) {
+ getRootView().reportAppJankStats(appJankStats);
+ }
+
+ /**
+ * Called by widgets to get a reference to JankTracker in order to update states.
+ * @hide
+ */
+ public @Nullable JankTracker getJankTracker() {
+ return getRootView().getJankTracker();
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3ce6870bf2ca..75d2da1b70e4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -185,7 +185,6 @@ import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.hardware.SyncFence;
-import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.input.InputManagerGlobal;
@@ -1816,9 +1815,9 @@ public final class ViewRootImpl implements ViewParent,
.registerDisplayListener(
mDisplayListener,
mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED,
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
mBasePackageName);
if (forceInvertColor()) {
@@ -9952,11 +9951,13 @@ public final class ViewRootImpl implements ViewParent,
return false;
}
- if (!mIsDrawing) {
- destroyHardwareRenderer();
- } else {
- Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
- " window=" + this + ", title=" + mWindowAttributes.getTitle());
+ if (!com.android.graphics.hwui.flags.Flags.removeVriSketchyDestroy()) {
+ if (!mIsDrawing) {
+ destroyHardwareRenderer();
+ } else {
+ Log.e(mTag, "Attempting to destroy the window while drawing!\n"
+ + " window=" + this + ", title=" + mWindowAttributes.getTitle());
+ }
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
@@ -9977,9 +9978,9 @@ public final class ViewRootImpl implements ViewParent,
dispatchDetachedFromWindow();
}
- if (mAdded && !mFirst) {
- destroyHardwareRenderer();
+ destroyHardwareRenderer();
+ if (mAdded && !mFirst) {
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5b39f62db261..b4b0687eb498 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1426,6 +1426,31 @@ public interface WindowManager extends ViewManager {
"android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE";
/**
+ * Activity-level {@link android.content.pm.PackageManager.Property PackageManager.Property}
+ * that specifies whether this activity can declare or request
+ * {@link android.R.attr#screenOrientation fixed orientation},
+ * {@link android.R.attr#minAspectRatio max aspect ratio},
+ * {@link android.R.attr#maxAspectRatio min aspect ratio}
+ * {@link android.R.attr#resizeableActivity unresizable} on large screen devices with the
+ * ignore orientation request display setting enabled since Android 16 (API level 36) or higher.
+ *
+ * <p>The default value is {@code false}.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * &lt;activity&gt;
+ * &lt;property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
+ * android:value="true"/&gt;
+ * &lt;/activity&gt;
+ * </pre>
+ * @hide
+ */
+ // TODO(b/357141415): Make this public API.
+ String PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY =
+ "android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY";
+
+ /**
* @hide
*/
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 8bcc9de118e2..12af692a4556 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -107,8 +107,8 @@ public final class WindowMetrics {
* and display cutout areas depending on the calling context and target SDK level. Please refer
* to {@link Display#getSize(Point)} for details.
* <p>
- * The value reported by {@link Display#getSize(Point)} excluding system decoration areas can be
- * obtained by using:
+ * The following code snippet shows how to get the bounds excluding navigation bars and display
+ * cutout:
* <pre class="prettyprint">
* final WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
* // Gets all excluding insets
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index c690787e4a33..0dfaf4149ce5 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -563,10 +563,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
/**
* Represents the event of an application making an announcement.
- * <p>
- * In general, follow the practices described in
- * {@link View#announceForAccessibility(CharSequence)}.
+ *
+ * @deprecated Use one of the semantic alternative methods described in the documentation of
+ * {@link View#announceForAccessibility(CharSequence)} instead of using this event, as
+ * accessibility services may choose to ignore dispatch of this event type.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_ACCESSIBILITY_ANNOUNCEMENT_APIS)
+ @Deprecated
public static final int TYPE_ANNOUNCEMENT = 1 << 14;
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5e5f33ef41f8..0204517e869a 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -857,8 +857,10 @@ public class AccessibilityNodeInfo implements Parcelable {
* <p>
* The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
* string as a key for {@link Bundle#getParcelableArray(String, Class)}. The
- * {@link android.graphics.RectF} will be null for characters that either do not exist or are
- * off the screen.
+ * {@link android.graphics.RectF} will be {@code null} for characters that either do not exist
+ * or are off the screen.
+ * <p>
+ * Note that character locations returned are modified by changes in display magnification.
*
* {@see #refreshWithExtraData(String, Bundle)}
*/
@@ -866,6 +868,36 @@ public class AccessibilityNodeInfo implements Parcelable {
"android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
/**
+ * Key used to request and locate extra data for text character location in
+ * window coordinates. This key requests that an array of
+ * {@link android.graphics.RectF}s be added to the extras. This request is made
+ * with {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by
+ * this request are two integers:
+ * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
+ * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index
+ * must be valid inside the CharSequence returned by {@link #getText()}, and
+ * the length must be positive.
+ * <p>
+ * Providers may advertise that they support text characters in window coordinates using
+ * {@link #setAvailableExtraData(List)}. Services may check if an implementation supports text
+ * characters in window coordinates with {@link #getAvailableExtraData()}.
+ * <p>
+ * The data can be retrieved from the {@code Bundle} returned by
+ * {@link #getExtras()} using this string as a key for
+ * {@link Bundle#getParcelableArray(String, Class)}. The
+ * {@link android.graphics.RectF} will be {@code null} for characters that either do
+ * not exist or are outside of the window bounds.
+ * <p>
+ * Note that character locations in window bounds are not modified by
+ * changes in display magnification.
+ *
+ * {@see #refreshWithExtraData(String, Bundle)}
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_CHARACTER_IN_WINDOW_API)
+ public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY =
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY";
+
+ /**
* Integer argument specifying the start index of the requested text location data. Must be
* valid inside the CharSequence returned by {@link #getText()}.
*
@@ -3834,8 +3866,14 @@ public class AccessibilityNodeInfo implements Parcelable {
* Sets the view for which the view represented by this info serves as a
* label for accessibility purposes.
*
+ * @deprecated Use {@link #addLabeledBy(View)} on the labeled node instead,
+ * since {@link #getLabeledByList()} and {@link #getLabeledBy()} on the
+ * labeled node are not automatically populated when this method is used.
+ *
* @param labeled The view for which this info serves as a label.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_ANI_LABEL_FOR_APIS)
+ @Deprecated
public void setLabelFor(View labeled) {
setLabelFor(labeled, AccessibilityNodeProvider.HOST_VIEW_ID);
}
@@ -3856,9 +3894,15 @@ public class AccessibilityNodeInfo implements Parcelable {
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
*
+ * @deprecated Use {@link #addLabeledBy(View)} on the labeled node instead,
+ * since {@link #getLabeledByList()} and {@link #getLabeledBy()} on the
+ * labeled node are not automatically populated when this method is used.
+ *
* @param root The root whose virtual descendant serves as a label.
* @param virtualDescendantId The id of the virtual descendant.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_ANI_LABEL_FOR_APIS)
+ @Deprecated
public void setLabelFor(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId = (root != null)
@@ -3870,8 +3914,14 @@ public class AccessibilityNodeInfo implements Parcelable {
* Gets the node info for which the view represented by this info serves as
* a label for accessibility purposes.
*
+ * @deprecated Use {@link #getLabeledByList()} on the labeled node instead,
+ * since calling {@link #addLabeledBy(View)} or {@link #addLabeledBy(View, int)}
+ * on the labeled node do not automatically provide that node from this method.
+ *
* @return The labeled info.
*/
+ @FlaggedApi(Flags.FLAG_DEPRECATE_ANI_LABEL_FOR_APIS)
+ @Deprecated
public AccessibilityNodeInfo getLabelFor() {
enforceSealed();
return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabelForId);
@@ -4008,8 +4058,12 @@ public class AccessibilityNodeInfo implements Parcelable {
* Sets the view which serves as the label of the view represented by
* this info for accessibility purposes.
*
+ * @deprecated Use {@link #addLabeledBy(View)} or {@link #removeLabeledBy(View)} instead.
+ *
* @param label The view that labels this node's source.
*/
+ @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+ @Deprecated
public void setLabeledBy(View label) {
setLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
}
@@ -4030,9 +4084,14 @@ public class AccessibilityNodeInfo implements Parcelable {
* This class is made immutable before being delivered to an AccessibilityService.
* </p>
*
+ * @deprecated Use {@link #addLabeledBy(View, int)} or {@link #removeLabeledBy(View, int)}
+ * instead.
+ *
* @param root The root whose virtual descendant labels this node's source.
* @param virtualDescendantId The id of the virtual descendant.
*/
+ @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+ @Deprecated
public void setLabeledBy(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId = (root != null)
@@ -4054,8 +4113,12 @@ public class AccessibilityNodeInfo implements Parcelable {
* Gets the node info which serves as the label of the view represented by
* this info for accessibility purposes.
*
+ * @deprecated Use {@link #getLabeledByList()} instead.
+ *
* @return The label.
*/
+ @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+ @Deprecated
public AccessibilityNodeInfo getLabeledBy() {
enforceSealed();
return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabeledById);
@@ -5460,26 +5523,6 @@ public class AccessibilityNodeInfo implements Parcelable {
}
}
- private static String getExpandedStateSymbolicName(int state) {
- if (Flags.a11yExpansionStateApi()) {
- switch (state) {
- case EXPANDED_STATE_UNDEFINED:
- return "EXPANDED_STATE_UNDEFINED";
- case EXPANDED_STATE_COLLAPSED:
- return "EXPANDED_STATE_COLLAPSED";
- case EXPANDED_STATE_PARTIAL:
- return "EXPANDED_STATE_PARTIAL";
- case EXPANDED_STATE_FULL:
- return "EXPANDED_STATE_FULL";
- default:
- throw new IllegalArgumentException("Unknown expanded state: " + state);
- }
- } else {
- // TODO(b/362782158) Remove when flag is removed.
- return "";
- }
- }
-
private static boolean canPerformRequestOverConnection(int connectionId,
int windowId, long accessibilityNodeId) {
final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -5573,20 +5616,12 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; maxTextLength: ").append(mMaxTextLength);
builder.append("; stateDescription: ").append(mStateDescription);
builder.append("; contentDescription: ").append(mContentDescription);
- if (Flags.supplementalDescription()) {
- builder.append("; supplementalDescription: ").append(mSupplementalDescription);
- }
builder.append("; tooltipText: ").append(mTooltipText);
builder.append("; containerTitle: ").append(mContainerTitle);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
builder.append("; uniqueId: ").append(mUniqueId);
- builder.append("; expandedState: ").append(getExpandedStateSymbolicName(mExpandedState));
-
builder.append("; checkable: ").append(isCheckable());
builder.append("; checked: ").append(isChecked());
- if (Flags.a11yIsRequiredApi()) {
- builder.append("; required: ").append(isFieldRequired());
- }
builder.append("; focusable: ").append(isFocusable());
builder.append("; focused: ").append(isFocused());
builder.append("; selected: ").append(isSelected());
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index c07da410c7f9..8a006fa5b509 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -85,6 +85,20 @@ flag {
flag {
namespace: "accessibility"
+ name: "deprecate_accessibility_announcement_apis"
+ description: "Controls the deprecation of platform APIs related to disruptive accessibility announcements"
+ bug: "376727542"
+}
+
+flag {
+ namespace: "accessibility"
+ name: "deprecate_ani_label_for_apis"
+ description: "Controls the deprecation of AccessibilityNodeInfo labelFor apis"
+ bug: "333783827"
+}
+
+flag {
+ namespace: "accessibility"
name: "fix_merged_content_change_event_v2"
description: "Fixes event type and source of content change event merged in ViewRootImpl"
bug: "277305460"
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 0ab51e45a951..905f350ca6c5 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -316,6 +316,35 @@ public class AutofillFeatureFlags {
// END AUTOFILL PCC CLASSIFICATION FLAGS
+ // START AUTOFILL REMOVE PRE_TRIGGER FLAGS
+
+ /**
+ * Whether pre-trigger flow is disabled.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_IMPROVE_FILL_DIALOG_ENABLED = "improve_fill_dialog";
+
+ /**
+ * Minimum amount of time (in milliseconds) to wait after IME animation finishes, and before
+ * starting fill dialog animation.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS =
+ "fill_dialog_min_wait_after_animation_end_ms";
+
+ /**
+ * Sets a value of timeout in milliseconds, measured after animation end, during which fill
+ * dialog can be shown. If we are at time > animation_end_time + this timeout, fill dialog
+ * wouldn't be shown.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_FILL_DIALOG_TIMEOUT_MS = "fill_dialog_timeout_ms";
+
+ // END AUTOFILL REMOVE PRE_TRIGGER FLAGS
+
/**
* Define the max input length for autofill to show suggesiton UI
*
@@ -366,6 +395,17 @@ public class AutofillFeatureFlags {
DEFAULT_AFAA_SHOULD_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE = true;
// END AUTOFILL FOR ALL APPS DEFAULTS
+ // START AUTOFILL REMOVE PRE_TRIGGER FLAGS DEFAULTS
+ // Default for whether the pre trigger removal is enabled.
+ /** @hide */
+ public static final boolean DEFAULT_IMPROVE_FILL_DIALOG_ENABLED = true;
+ // Default for whether the pre trigger removal is enabled.
+ /** @hide */
+ public static final long DEFAULT_FILL_DIALOG_TIMEOUT_MS = 300; // 300 ms
+ /** @hide */
+ public static final long DEFAULT_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS = 0; // 0 ms
+ // END AUTOFILL REMOVE PRE_TRIGGER FLAGS DEFAULTS
+
/**
* @hide
*/
@@ -611,4 +651,48 @@ public class AutofillFeatureFlags {
}
// END AUTOFILL PCC CLASSIFICATION FUNCTIONS
+
+
+ // START AUTOFILL REMOVE PRE_TRIGGER
+ /**
+ * Whether Autofill Pre Trigger Removal is enabled.
+ *
+ * @hide
+ */
+ public static boolean isImproveFillDialogEnabled() {
+ // TODO(b/266379948): Add condition for checking whether device has PCC first
+
+ return DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_IMPROVE_FILL_DIALOG_ENABLED,
+ DEFAULT_IMPROVE_FILL_DIALOG_ENABLED);
+ }
+
+ /**
+ * Whether Autofill Pre Trigger Removal is enabled.
+ *
+ * @hide
+ */
+ public static long getFillDialogTimeoutMs() {
+ // TODO(b/266379948): Add condition for checking whether device has PCC first
+
+ return DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_FILL_DIALOG_TIMEOUT_MS,
+ DEFAULT_FILL_DIALOG_TIMEOUT_MS);
+ }
+
+ /**
+ * Whether Autofill Pre Trigger Removal is enabled.
+ *
+ * @hide
+ */
+ public static long getFillDialogMinWaitAfterImeAnimationtEndMs() {
+ // TODO(b/266379948): Add condition for checking whether device has PCC first
+
+ return DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS,
+ DEFAULT_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS);
+ }
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1a45939f65b6..52c5af8889ec 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -25,12 +25,14 @@ import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
+import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1607,7 +1609,12 @@ public final class AutofillManager {
* the virtual view in the host view.
*
* @throws IllegalArgumentException if the {@code infos} was empty
+ *
+ * @deprecated This function will not do anything. Showing fill dialog is now fully controlled
+ * by the framework and the autofill provider.
*/
+ @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+ @Deprecated
public void notifyVirtualViewsReady(
@NonNull View view, @NonNull SparseArray<VirtualViewFillInfo> infos) {
Objects.requireNonNull(infos);
@@ -4034,8 +4041,13 @@ public final class AutofillManager {
* receiving a focus event. The autofill suggestions shown will include content for
* related views as well.
* @return {@code true} if the autofill dialog is being shown
+ *
+ * @deprecated This function will not do anything. Showing fill dialog is now fully controlled
+ * by the framework and the autofill provider.
*/
// TODO(b/210926084): Consider whether to include the one-time show logic within this method.
+ @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+ @Deprecated
public boolean showAutofillDialog(@NonNull View view) {
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, view.getAutofillId())) {
@@ -4073,7 +4085,12 @@ public final class AutofillManager {
* suggestions.
* @param virtualId id identifying the virtual view inside the host view.
* @return {@code true} if the autofill dialog is being shown
+ *
+ * @deprecated This function will not do anything. Showing fill dialog is now fully controlled
+ * by the framework and the autofill provider.
*/
+ @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
+ @Deprecated
public boolean showAutofillDialog(@NonNull View view, int virtualId) {
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index 658aa29a3cc6..b180e58cbe49 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -23,3 +23,10 @@ flag {
bug: "331830899"
is_fixed_read_only: true
}
+
+flag {
+ namespace: "wear_frameworks"
+ name: "dynamic_view_rotary_haptics_configuration"
+ description: "Whether ScrollFeedbackProvider dynamically disables View-based rotary haptics."
+ bug: "377998870 "
+}
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index 2ca62a0725df..dd32d57bd650 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -221,6 +221,7 @@ public interface ImeTracker {
PHASE_WM_INVOKING_IME_REQUESTED_LISTENER,
PHASE_CLIENT_ALREADY_HIDDEN,
PHASE_CLIENT_VIEW_HANDLER_AVAILABLE,
+ PHASE_SERVER_UPDATE_CLIENT_VISIBILITY,
})
@Retention(RetentionPolicy.SOURCE)
@interface Phase {}
@@ -430,6 +431,11 @@ public interface ImeTracker {
* continue without.
*/
int PHASE_CLIENT_VIEW_HANDLER_AVAILABLE = ImeProtoEnums.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE;
+ /**
+ * ImeInsetsSourceProvider sets the reported visibility of the caller/client window (either the
+ * app or the RemoteInsetsControlTarget).
+ */
+ int PHASE_SERVER_UPDATE_CLIENT_VISIBILITY = ImeProtoEnums.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY;
/**
* Called when an IME request is started.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7dc77b175c79..6026e60e5b59 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2471,6 +2471,11 @@ public final class InputMethodManager {
return;
}
+ if (Flags.refactorInsetsController()) {
+ showSoftInput(rootView, statsToken, flags, resultReceiver, reason);
+ return;
+ }
+
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
// Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
@@ -3667,6 +3672,14 @@ public final class InputMethodManager {
}
/**
+ * Returns the ImeOnBackInvokedDispatcher.
+ * @hide
+ */
+ public ImeOnBackInvokedDispatcher getImeOnBackInvokedDispatcher() {
+ return mImeDispatcher;
+ }
+
+ /**
* Check the next served view if needs to start input.
*/
@GuardedBy("mH")
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index be91cfb9ebf8..a67ae7c96a54 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -17,8 +17,10 @@
package android.view.inputmethod;
import android.annotation.AnyThread;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -87,8 +89,17 @@ public final class InputMethodSubtype implements Parcelable {
private final boolean mIsAsciiCapable;
private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
+ /** The subtype name resource identifier. */
private final int mSubtypeNameResId;
+ /** The untranslatable name of the subtype. */
+ @NonNull
private final CharSequence mSubtypeNameOverride;
+ /** The layout label string resource identifier. */
+ @StringRes
+ private final int mLayoutLabelResId;
+ /** The non-localized layout label. */
+ @NonNull
+ private final CharSequence mLayoutLabelNonLocalized;
private final String mPkLanguageTag;
private final String mPkLayoutType;
private final int mSubtypeId;
@@ -176,6 +187,7 @@ public final class InputMethodSubtype implements Parcelable {
mSubtypeNameResId = subtypeNameResId;
return this;
}
+ /** The subtype name resource identifier. */
private int mSubtypeNameResId = 0;
/**
@@ -191,9 +203,56 @@ public final class InputMethodSubtype implements Parcelable {
mSubtypeNameOverride = nameOverride;
return this;
}
+ /** The untranslatable name of the subtype. */
+ @NonNull
private CharSequence mSubtypeNameOverride = "";
/**
+ * Sets the layout label string resource identifier.
+ *
+ * @param layoutLabelResId the layout label string resource identifier.
+ *
+ * @see #getLayoutDisplayName
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @NonNull
+ public InputMethodSubtypeBuilder setLayoutLabelResource(
+ @StringRes int layoutLabelResId) {
+ if (!Flags.imeSwitcherRevampApi()) {
+ return this;
+ }
+ mLayoutLabelResId = layoutLabelResId;
+ return this;
+ }
+ /** The layout label string resource identifier. */
+ @StringRes
+ private int mLayoutLabelResId = 0;
+
+ /**
+ * Sets the non-localized layout label. This is used as the layout display name if the
+ * {@link #getLayoutLabelResource layoutLabelResource} is not set ({@code 0}).
+ *
+ * @param layoutLabelNonLocalized the non-localized layout label.
+ *
+ * @see #getLayoutDisplayName
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @NonNull
+ public InputMethodSubtypeBuilder setLayoutLabelNonLocalized(
+ @NonNull CharSequence layoutLabelNonLocalized) {
+ if (!Flags.imeSwitcherRevampApi()) {
+ return this;
+ }
+ Objects.requireNonNull(layoutLabelNonLocalized,
+ "layoutLabelNonLocalized cannot be null");
+ mLayoutLabelNonLocalized = layoutLabelNonLocalized;
+ return this;
+ }
+ /** The non-localized layout label. */
+ @NonNull
+ private CharSequence mLayoutLabelNonLocalized = "";
+
+ /**
* Sets the physical keyboard hint information, such as language and layout.
*
* The system can use the hint information to automatically configure the physical keyboard
@@ -350,6 +409,8 @@ public final class InputMethodSubtype implements Parcelable {
private InputMethodSubtype(InputMethodSubtypeBuilder builder) {
mSubtypeNameResId = builder.mSubtypeNameResId;
mSubtypeNameOverride = builder.mSubtypeNameOverride;
+ mLayoutLabelResId = builder.mLayoutLabelResId;
+ mLayoutLabelNonLocalized = builder.mLayoutLabelNonLocalized;
mPkLanguageTag = builder.mPkLanguageTag;
mPkLayoutType = builder.mPkLayoutType;
mSubtypeIconResId = builder.mSubtypeIconResId;
@@ -376,6 +437,9 @@ public final class InputMethodSubtype implements Parcelable {
mSubtypeNameResId = source.readInt();
CharSequence cs = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mSubtypeNameOverride = cs != null ? cs : "";
+ mLayoutLabelResId = source.readInt();
+ cs = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mLayoutLabelNonLocalized = cs != null ? cs : "";
s = source.readString8();
mPkLanguageTag = s != null ? s : "";
s = source.readString8();
@@ -412,6 +476,24 @@ public final class InputMethodSubtype implements Parcelable {
}
/**
+ * Returns the layout label string resource identifier.
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @StringRes
+ public int getLayoutLabelResource() {
+ return mLayoutLabelResId;
+ }
+
+ /**
+ * Returns the non-localized layout label.
+ */
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ @NonNull
+ public CharSequence getLayoutLabelNonLocalized() {
+ return mLayoutLabelNonLocalized;
+ }
+
+ /**
* Returns the physical keyboard BCP-47 language tag.
*
* @attr ref android.R.styleable#InputMethod_Subtype_physicalKeyboardHintLanguageTag
@@ -643,9 +725,47 @@ public final class InputMethodSubtype implements Parcelable {
try {
return String.format(subtypeNameString, replacementString);
} catch (IllegalFormatException e) {
- Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
+ Slog.w(TAG, "Found illegal format in subtype name(" + subtypeName + "): " + e);
+ return "";
+ }
+ }
+
+ /**
+ * Returns the layout display name.
+ *
+ * <p>If {@code layoutLabelResource} is non-zero (specified through
+ * {@link InputMethodSubtypeBuilder#setLayoutLabelResource setLayoutLabelResource}), the
+ * text generated from that resource will be returned. The localized string resource of the
+ * label should be capitalized for inclusion in UI lists.
+ *
+ * <p>If {@code layoutLabelResource} is zero, the framework returns the non-localized
+ * layout label, if specified through
+ * {@link InputMethodSubtypeBuilder#setLayoutLabelNonLocalized setLayoutLabelNonLocalized}.
+ *
+ * @param context The context used for getting the
+ * {@link android.content.pm.PackageManager PackageManager}.
+ * @param imeAppInfo The {@link ApplicationInfo} of the input method.
+ * @return the layout display name.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_IME_SWITCHER_REVAMP_API)
+ public CharSequence getLayoutDisplayName(@NonNull Context context,
+ @NonNull ApplicationInfo imeAppInfo) {
+ if (!Flags.imeSwitcherRevampApi()) {
+ return "";
+ }
+ Objects.requireNonNull(context, "context cannot be null");
+ Objects.requireNonNull(imeAppInfo, "imeAppInfo cannot be null");
+ if (mLayoutLabelResId == 0) {
+ return mLayoutLabelNonLocalized;
+ }
+
+ final CharSequence subtypeLayoutName = context.getPackageManager().getText(
+ imeAppInfo.packageName, mLayoutLabelResId, imeAppInfo);
+ if (TextUtils.isEmpty(subtypeLayoutName)) {
return "";
}
+ return subtypeLayoutName;
}
@Nullable
@@ -778,6 +898,8 @@ public final class InputMethodSubtype implements Parcelable {
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(mSubtypeNameResId);
TextUtils.writeToParcel(mSubtypeNameOverride, dest, parcelableFlags);
+ dest.writeInt(mLayoutLabelResId);
+ TextUtils.writeToParcel(mLayoutLabelNonLocalized, dest, parcelableFlags);
dest.writeString8(mPkLanguageTag);
dest.writeString8(mPkLayoutType);
dest.writeInt(mSubtypeIconResId);
@@ -794,6 +916,7 @@ public final class InputMethodSubtype implements Parcelable {
void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + "mSubtypeNameOverride=" + mSubtypeNameOverride
+ + " mLayoutLabelNonLocalized=" + mLayoutLabelNonLocalized
+ " mPkLanguageTag=" + mPkLanguageTag
+ " mPkLayoutType=" + mPkLayoutType
+ " mSubtypeId=" + mSubtypeId
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index aa4927ee9b9c..edd9d6cff799 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -158,3 +158,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "writing_tools"
+ namespace: "input_method"
+ description: "Writing tools API"
+ bug: "373788889"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 877fa74138fe..1baf3b82ca50 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -20,6 +20,8 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
@@ -552,6 +554,23 @@ public class WebChromeClient {
* Parameters used in the {@link #onShowFileChooser} method.
*/
public static abstract class FileChooserParams {
+ /**
+ * Enable File System Access for webview.
+ *
+ * File System Access JS APIs window.showOpenFileChooser(), showDirectoryChooser(), and
+ * showSaveFilePicker() will invoke #onFileChooser(). Additional MODE_OPEN_FOLDER will be
+ * returned by #getMode(), #getIntent() will return ACTION_OPEN_DOCUMENT,
+ * ACTION_OPEN_DOCUMENT_TREE, and ACTION_CREATE_DOCUMENT rather than the current
+ * ACTION_GET_CONTENT, and new function #getPermissionMode() will be available.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS)
+ @SystemApi
+ public static final long ENABLE_FILE_SYSTEM_ACCESS = 364980165L;
+
/** @hide */
@IntDef(prefix = { "MODE_" }, value = {
MODE_OPEN,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ef941da0e32d..d7750bd412a3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -26,6 +26,9 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDER
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY;
+import static android.view.accessibility.Flags.FLAG_A11Y_CHARACTER_IN_WINDOW_API;
+import static android.view.accessibility.Flags.a11yCharacterInWindowApi;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import static android.view.inputmethod.EditorInfo.STYLUS_HANDWRITING_ENABLED_ANDROIDX_EXTRAS_KEY;
import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
@@ -492,6 +495,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/** Accessibility action start id for "smart" actions. @hide */
static final int ACCESSIBILITY_ACTION_SMART_START_ID = 0x10001000;
+ // Stable extra data keys supported by TextView.
+ private static final List<String> ACCESSIBILITY_EXTRA_DATA_KEYS = List.of(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ );
+
+ // Flagged and stable extra data keys supported by TextView.
+ @FlaggedApi(FLAG_A11Y_CHARACTER_IN_WINDOW_API)
+ private static final List<String> ACCESSIBILITY_EXTRA_DATA_KEYS_FLAGGED = List.of(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY
+ );
+
/**
* @hide
*/
@@ -14207,10 +14224,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
- info.setAvailableExtraData(Arrays.asList(
- EXTRA_DATA_RENDERING_INFO_KEY,
- EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
- ));
+ if (a11yCharacterInWindowApi()) {
+ info.setAvailableExtraData(ACCESSIBILITY_EXTRA_DATA_KEYS_FLAGGED);
+ } else {
+ info.setAvailableExtraData(ACCESSIBILITY_EXTRA_DATA_KEYS);
+ }
info.setTextSelectable(isTextSelectable() || isTextEditable());
} else {
info.setAvailableExtraData(Arrays.asList(
@@ -14275,7 +14293,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void addExtraDataToAccessibilityNodeInfo(
AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
- if (arguments != null && extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+ boolean isCharacterLocationKey = extraDataKey.equals(
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
+ boolean isCharacterLocationInWindowKey = (a11yCharacterInWindowApi() && extraDataKey.equals(
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_IN_WINDOW_KEY));
+ if (arguments != null && (isCharacterLocationKey || isCharacterLocationInWindowKey)) {
int positionInfoStartIndex = arguments.getInt(
EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
int positionInfoLength = arguments.getInt(
@@ -14297,7 +14319,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
RectF bounds = cursorAnchorInfo
.getCharacterBounds(positionInfoStartIndex + i);
if (bounds != null) {
- mapRectFromViewToScreenCoords(bounds, true);
+ if (isCharacterLocationKey) {
+ mapRectFromViewToScreenCoords(bounds, true);
+ } else if (isCharacterLocationInWindowKey) {
+ mapRectFromViewToWindowCoords(bounds, true);
+ }
boundingRects[i] = bounds;
}
}
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index a5be58b7b183..16eb43700aef 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -16,8 +16,11 @@
package android.window;
+import static android.window.BackEvent.EDGE_NONE;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.window.flags.Flags.predictiveBackTimestampApi;
+import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,6 +63,12 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL
@Nullable
private Runnable mBackInvokedFinishRunnable;
private FlingAnimation mBackInvokedFlingAnim;
+ private final SpringForce mGestureSpringForce = new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_MEDIUM)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY);
+ private final SpringForce mButtonSpringForce = new SpringForce()
+ .setStiffness(500)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY);
private final DynamicAnimation.OnAnimationEndListener mOnAnimationEndListener =
(animation, canceled, value, velocity) -> {
if (mBackCancelledFinishRunnable != null) invokeBackCancelledRunnable();
@@ -109,9 +118,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL
public BackProgressAnimator() {
mSpring = new SpringAnimation(this, PROGRESS_PROP);
mSpring.addUpdateListener(this);
- mSpring.setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_MEDIUM)
- .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY));
+ mSpring.setSpring(mGestureSpringForce);
}
/**
@@ -123,6 +130,11 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL
if (!mBackAnimationInProgress) {
return;
}
+ if (predictiveBackSwipeEdgeNoneApi()) {
+ if (event.getSwipeEdge() == EDGE_NONE) {
+ return;
+ }
+ }
mLastBackEvent = event;
if (mSpring == null) {
return;
@@ -143,7 +155,17 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL
mBackAnimationInProgress = true;
updateProgressValue(/* progress */ 0, /* velocity */ 0,
/* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS);
- onBackProgressed(event);
+ if (predictiveBackSwipeEdgeNoneApi()) {
+ if (event.getSwipeEdge() == EDGE_NONE) {
+ mSpring.setSpring(mButtonSpringForce);
+ mSpring.animateToFinalPosition(SCALE_FACTOR);
+ } else {
+ mSpring.setSpring(mGestureSpringForce);
+ onBackProgressed(event);
+ }
+ } else {
+ onBackProgressed(event);
+ }
}
/**
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index dae87ddcb1bd..7a01ad340c56 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -54,6 +54,7 @@ public enum DesktopModeFlags {
Flags::enableDesktopWindowingWallpaperActivity, true),
ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
+ ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
@@ -75,7 +76,8 @@ public enum DesktopModeFlags {
Flags::enableDesktopAppLaunchAlttabTransitions, false),
ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS(
Flags::enableDesktopAppLaunchTransitions, false),
- ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false);
+ ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false),
+ ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true);
private static final String TAG = "DesktopModeFlagsUtil";
// Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index bd01899a649b..c67b9cac250b 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -203,6 +203,34 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
mImeCallbacks.remove(callback);
}
+ /**
+ * Unregisters all callbacks on the receiving dispatcher but keeps a reference of the callbacks
+ * in case the clearance is reverted in
+ * {@link ImeOnBackInvokedDispatcher#undoPreliminaryClear()}.
+ */
+ public void preliminaryClear() {
+ // Unregister previously registered callbacks if there's any.
+ if (getReceivingDispatcher() != null) {
+ for (ImeOnBackInvokedCallback callback : mImeCallbacks) {
+ getReceivingDispatcher().unregisterOnBackInvokedCallback(callback);
+ }
+ }
+ }
+
+ /**
+ * Reregisters all callbacks on the receiving dispatcher that have previously been cleared by
+ * calling {@link ImeOnBackInvokedDispatcher#preliminaryClear()}. This can happen if an IME hide
+ * animation is interrupted causing the IME to reappear.
+ */
+ public void undoPreliminaryClear() {
+ if (getReceivingDispatcher() != null) {
+ for (ImeOnBackInvokedCallback callback : mImeCallbacks) {
+ getReceivingDispatcher().registerOnBackInvokedCallbackUnchecked(callback,
+ callback.mPriority);
+ }
+ }
+ }
+
/** Clears all registered callbacks on the instance. */
public void clear() {
// Unregister previously registered callbacks if there's any.
diff --git a/core/java/android/window/OnBackInvokedCallbackInfo.java b/core/java/android/window/OnBackInvokedCallbackInfo.java
index bb5fe96fdec1..44c7bd9ec612 100644
--- a/core/java/android/window/OnBackInvokedCallbackInfo.java
+++ b/core/java/android/window/OnBackInvokedCallbackInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;
+
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,19 +31,23 @@ public final class OnBackInvokedCallbackInfo implements Parcelable {
private final IOnBackInvokedCallback mCallback;
private @OnBackInvokedDispatcher.Priority int mPriority;
private final boolean mIsAnimationCallback;
+ private final @SystemOverrideOnBackInvokedCallback.OverrideBehavior int mOverrideBehavior;
public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback,
int priority,
- boolean isAnimationCallback) {
+ boolean isAnimationCallback,
+ int overrideBehavior) {
mCallback = callback;
mPriority = priority;
mIsAnimationCallback = isAnimationCallback;
+ mOverrideBehavior = overrideBehavior;
}
private OnBackInvokedCallbackInfo(@NonNull Parcel in) {
mCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
mPriority = in.readInt();
mIsAnimationCallback = in.readBoolean();
+ mOverrideBehavior = in.readInt();
}
@Override
@@ -54,6 +60,7 @@ public final class OnBackInvokedCallbackInfo implements Parcelable {
dest.writeStrongInterface(mCallback);
dest.writeInt(mPriority);
dest.writeBoolean(mIsAnimationCallback);
+ dest.writeInt(mOverrideBehavior);
}
public static final Creator<OnBackInvokedCallbackInfo> CREATOR =
@@ -70,7 +77,8 @@ public final class OnBackInvokedCallbackInfo implements Parcelable {
};
public boolean isSystemCallback() {
- return mPriority == OnBackInvokedDispatcher.PRIORITY_SYSTEM;
+ return mPriority == OnBackInvokedDispatcher.PRIORITY_SYSTEM
+ || mOverrideBehavior != OVERRIDE_UNDEFINED;
}
@NonNull
@@ -87,12 +95,18 @@ public final class OnBackInvokedCallbackInfo implements Parcelable {
return mIsAnimationCallback;
}
+ @SystemOverrideOnBackInvokedCallback.OverrideBehavior
+ public int getOverrideBehavior() {
+ return mOverrideBehavior;
+ }
+
@Override
public String toString() {
return "OnBackInvokedCallbackInfo{"
+ "mCallback=" + mCallback
+ ", mPriority=" + mPriority
+ ", mIsAnimationCallback=" + mIsAnimationCallback
+ + ", mOverrideBehavior=" + mOverrideBehavior
+ '}';
}
}
diff --git a/core/java/android/window/SystemOnBackInvokedCallbacks.java b/core/java/android/window/SystemOnBackInvokedCallbacks.java
new file mode 100644
index 000000000000..f67520b1de96
--- /dev/null
+++ b/core/java/android/window/SystemOnBackInvokedCallbacks.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.util.ArrayMap;
+
+import com.android.window.flags.Flags;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Utility class providing {@link OnBackInvokedCallback}s to override the default behavior when
+ * system back is invoked. e.g. {@link Activity#finish}
+ *
+ * <p>By registering these callbacks with the {@link OnBackInvokedDispatcher}, the system can
+ * trigger specific behaviors and play corresponding ahead-of-time animations when the back
+ * gesture is invoked.
+ *
+ * <p>For example, to trigger the {@link Activity#moveTaskToBack} behavior:
+ * <pre>
+ * OnBackInvokedDispatcher dispatcher = activity.getOnBackInvokedDispatcher();
+ * dispatcher.registerOnBackInvokedCallback(
+ * OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ * SystemOnBackInvokedCallbacks.moveTaskToBackCallback(activity));
+ * </pre>
+ */
+@SuppressWarnings("SingularCallback")
+@FlaggedApi(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_OVERRIDE_CALLBACK)
+public final class SystemOnBackInvokedCallbacks {
+ private static final OverrideCallbackFactory<Activity> sMoveTaskToBackFactory = new
+ MoveTaskToBackCallbackFactory();
+ private static final OverrideCallbackFactory<Activity> sFinishAndRemoveTaskFactory = new
+ FinishAndRemoveTaskCallbackFactory();
+
+ private SystemOnBackInvokedCallbacks() {
+ throw new UnsupportedOperationException("This is a utility class and cannot be "
+ + "instantiated");
+ }
+
+ /**
+ * <p>Get a callback to triggers {@link Activity#moveTaskToBack(boolean)} on the associated
+ * {@link Activity}, moving the task containing the activity to the background. The system
+ * will play the corresponding transition animation, regardless of whether the activity
+ * is the root activity of the task.</p>
+ *
+ * @param activity The associated {@link Activity}
+ * @see Activity#moveTaskToBack(boolean)
+ */
+ @FlaggedApi(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_OVERRIDE_CALLBACK)
+ @NonNull
+ public static OnBackInvokedCallback moveTaskToBackCallback(@NonNull Activity activity) {
+ return sMoveTaskToBackFactory.getOverrideCallback(activity);
+ }
+
+ /**
+ * <p>Get a callback to triggers {@link Activity#finishAndRemoveTask()} on the associated
+ * {@link Activity}. If the activity is the root activity of its task, the entire task
+ * will be removed from the recents task. The activity will be finished in all cases.
+ * The system will play the corresponding transition animation.</p>
+ *
+ * @param activity The associated {@link Activity}
+ * @see Activity#finishAndRemoveTask()
+ */
+ @FlaggedApi(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_OVERRIDE_CALLBACK)
+ @NonNull
+ public static OnBackInvokedCallback finishAndRemoveTaskCallback(@NonNull Activity activity) {
+ return sFinishAndRemoveTaskFactory.getOverrideCallback(activity);
+ }
+
+ /**
+ * Abstract factory for creating system override {@link SystemOverrideOnBackInvokedCallback}
+ * instances.
+ *
+ * <p>Concrete implementations of this factory are responsible for creating callbacks that
+ * override the default system back navigation behavior. These callbacks should be used
+ * exclusively for system overrides and should never be invoked directly.</p>
+ */
+ private abstract static class OverrideCallbackFactory<TYPE> {
+ private final ArrayMap<WeakReference<TYPE>,
+ WeakReference<SystemOverrideOnBackInvokedCallback>> mObjectMap = new ArrayMap<>();
+
+ protected abstract SystemOverrideOnBackInvokedCallback createCallback(
+ @NonNull TYPE context);
+
+ @NonNull SystemOverrideOnBackInvokedCallback getOverrideCallback(@NonNull TYPE object) {
+ if (object == null) {
+ throw new NullPointerException("Input object cannot be null");
+ }
+ synchronized (mObjectMap) {
+ WeakReference<SystemOverrideOnBackInvokedCallback> callback = null;
+ for (int i = mObjectMap.size() - 1; i >= 0; --i) {
+ final WeakReference<TYPE> next = mObjectMap.keyAt(i);
+ if (next.get() == object) {
+ callback = mObjectMap.get(next);
+ break;
+ }
+ }
+ if (callback != null) {
+ return callback.get();
+ }
+ final SystemOverrideOnBackInvokedCallback contextCallback = createCallback(object);
+ if (contextCallback != null) {
+ mObjectMap.put(new WeakReference<>(object),
+ new WeakReference<>(contextCallback));
+ }
+ return contextCallback;
+ }
+ }
+ }
+
+ private static class MoveTaskToBackCallbackFactory extends OverrideCallbackFactory<Activity> {
+ @Override
+ protected SystemOverrideOnBackInvokedCallback createCallback(Activity activity) {
+ final WeakReference<Activity> activityRef = new WeakReference<>(activity);
+ return new SystemOverrideOnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ if (activityRef.get() != null) {
+ activityRef.get().moveTaskToBack(true /* nonRoot */);
+ }
+ }
+
+ @Override
+ public int overrideBehavior() {
+ return OVERRIDE_MOVE_TASK_TO_BACK;
+ }
+ };
+ }
+ }
+
+ private static class FinishAndRemoveTaskCallbackFactory extends
+ OverrideCallbackFactory<Activity> {
+ @Override
+ protected SystemOverrideOnBackInvokedCallback createCallback(Activity activity) {
+ final WeakReference<Activity> activityRef = new WeakReference<>(activity);
+ return new SystemOverrideOnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ if (activityRef.get() != null) {
+ activityRef.get().finishAndRemoveTask();
+ }
+ }
+
+ @Override
+ public int overrideBehavior() {
+ return OVERRIDE_FINISH_AND_REMOVE_TASK;
+ }
+ };
+ }
+ }
+}
diff --git a/core/java/android/window/SystemOverrideOnBackInvokedCallback.java b/core/java/android/window/SystemOverrideOnBackInvokedCallback.java
new file mode 100644
index 000000000000..3360a199682e
--- /dev/null
+++ b/core/java/android/window/SystemOverrideOnBackInvokedCallback.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Non-default ahead-of-time system OnBackInvokedCallback.
+ * @hide
+ */
+public interface SystemOverrideOnBackInvokedCallback extends OnBackInvokedCallback {
+ /**
+ * No override request
+ */
+ int OVERRIDE_UNDEFINED = 0;
+
+ /**
+ * Navigating back will bring the task to back
+ */
+ int OVERRIDE_MOVE_TASK_TO_BACK = 1;
+
+ /**
+ * Navigating back will finish activity, and remove the task if this activity is root activity.
+ */
+ int OVERRIDE_FINISH_AND_REMOVE_TASK = 2;
+
+ /** @hide */
+ @IntDef({
+ OVERRIDE_UNDEFINED,
+ OVERRIDE_MOVE_TASK_TO_BACK,
+ OVERRIDE_FINISH_AND_REMOVE_TASK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface OverrideBehavior {
+ }
+
+ /**
+ * @return Override type of this callback.
+ */
+ @OverrideBehavior
+ default int overrideBehavior() {
+ return OVERRIDE_UNDEFINED;
+ }
+}
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 8bb4c526b20d..61fc6226f822 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -17,6 +17,7 @@
package android.window;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TransitionType;
import android.annotation.IntDef;
@@ -189,6 +190,8 @@ public final class TransitionFilter implements Parcelable {
public Boolean mCustomAnimation = null;
public IBinder mTaskFragmentToken = null;
+ public int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
public Requirement() {
}
@@ -206,6 +209,7 @@ public final class TransitionFilter implements Parcelable {
final int customAnimRaw = in.readInt();
mCustomAnimation = customAnimRaw == 0 ? null : Boolean.valueOf(customAnimRaw == 2);
mTaskFragmentToken = in.readStrongBinder();
+ mWindowingMode = in.readInt();
}
/** Go through changes and find if at-least one change matches this filter */
@@ -270,6 +274,12 @@ public final class TransitionFilter implements Parcelable {
continue;
}
}
+ if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ if (change.getTaskInfo() == null
+ || change.getTaskInfo().getWindowingMode() != mWindowingMode) {
+ continue;
+ }
+ }
return true;
}
return false;
@@ -322,6 +332,7 @@ public final class TransitionFilter implements Parcelable {
int customAnimRaw = mCustomAnimation == null ? 0 : (mCustomAnimation ? 2 : 1);
dest.writeInt(customAnimRaw);
dest.writeStrongBinder(mTaskFragmentToken);
+ dest.writeInt(mWindowingMode);
}
@NonNull
@@ -369,6 +380,8 @@ public final class TransitionFilter implements Parcelable {
if (mTaskFragmentToken != null) {
out.append(" taskFragmentToken=").append(mTaskFragmentToken);
}
+ out.append(" windowingMode="
+ + WindowConfiguration.windowingModeToString(mWindowingMode));
out.append("}");
return out.toString();
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 14505f527195..0f2dd10d7f47 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -169,8 +169,11 @@ public final class TransitionInfo implements Parcelable {
/** This change represents its start configuration for the duration of the animation. */
public static final int FLAG_CONFIG_AT_END = 1 << 22;
+ /** This change represents one of a Task Display Area. */
+ public static final int FLAG_IS_TASK_DISPLAY_AREA = 1 << 23;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 23;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 24;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -205,6 +208,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_MOVED_TO_TOP,
FLAG_SYNC,
FLAG_CONFIG_AT_END,
+ FLAG_IS_TASK_DISPLAY_AREA,
FLAG_FIRST_CUSTOM
}, flag = true)
public @interface ChangeFlags {}
@@ -553,6 +557,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_MOVED_TO_TOP) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("MOVE_TO_TOP");
}
+ if ((flags & FLAG_IS_TASK_DISPLAY_AREA) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_TASK_DISPLAY_AREA");
+ }
return sb.toString();
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index c9d458f22463..0ea4bb41d3a4 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -16,6 +16,9 @@
package android.window;
+import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;
+
+import static com.android.window.flags.Flags.predictiveBackSystemOverrideCallback;
import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
import static com.android.window.flags.Flags.predictiveBackTimestampApi;
@@ -201,6 +204,15 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
return;
}
+ if (predictiveBackPrioritySystemNavigationObserver()
+ && predictiveBackSystemOverrideCallback()) {
+ if (priority == PRIORITY_SYSTEM_NAVIGATION_OBSERVER
+ && callback instanceof SystemOverrideOnBackInvokedCallback) {
+ Log.e(TAG, "System override callbacks cannot be registered to "
+ + "NAVIGATION_OBSERVER");
+ return;
+ }
+ }
if (predictiveBackPrioritySystemNavigationObserver()) {
if (priority == PRIORITY_SYSTEM_NAVIGATION_OBSERVER) {
registerSystemNavigationObserverCallback(callback);
@@ -365,7 +377,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
public void tryInvokeSystemNavigationObserverCallback() {
OnBackInvokedCallback topCallback = getTopCallback();
Integer callbackPriority = mAllCallbacks.getOrDefault(topCallback, null);
- if (callbackPriority != null && callbackPriority == PRIORITY_SYSTEM) {
+ final boolean isSystemOverride = topCallback instanceof SystemOverrideOnBackInvokedCallback;
+ if ((callbackPriority != null && callbackPriority == PRIORITY_SYSTEM) || isSystemOverride) {
invokeSystemNavigationObserverCallback();
}
}
@@ -384,14 +397,22 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
OnBackInvokedCallbackInfo callbackInfo = null;
if (callback != null) {
int priority = mAllCallbacks.get(callback);
+ int overrideAnimation = OVERRIDE_UNDEFINED;
+ if (callback instanceof SystemOverrideOnBackInvokedCallback) {
+ overrideAnimation = ((SystemOverrideOnBackInvokedCallback) callback)
+ .overrideBehavior();
+ }
+ final boolean isSystemCallback = priority == PRIORITY_SYSTEM
+ || overrideAnimation != OVERRIDE_UNDEFINED;
final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(callback,
mTouchTracker, mProgressAnimator, mHandler, this::callOnKeyPreIme,
this::invokeSystemNavigationObserverCallback,
- /*isSystemCallback*/ priority == PRIORITY_SYSTEM);
+ isSystemCallback /*isSystemCallback*/);
callbackInfo = new OnBackInvokedCallbackInfo(
iCallback,
priority,
- callback instanceof OnBackAnimationCallback);
+ callback instanceof OnBackAnimationCallback,
+ overrideAnimation);
}
mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
} catch (RemoteException e) {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 731d10048d7c..f474b34ac390 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -96,6 +96,16 @@ flag {
}
flag {
+ name: "enable_accessible_custom_headers"
+ namespace: "lse_desktop_experience"
+ description: "Enables a11y-friendly custom header input handling"
+ bug: "339302584"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_app_header_with_task_density"
namespace: "lse_desktop_experience"
description: "Matches the App Header density to that of the app window, instead of SysUI's"
@@ -343,6 +353,16 @@ flag {
}
flag {
+ name: "enable_desktop_system_dialogs_transitions"
+ namespace: "lse_desktop_experience"
+ description: "Enables custom transitions for system dialogs in Desktop Mode."
+ bug: "335638193"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_move_to_next_display_shortcut"
namespace: "lse_desktop_experience"
description: "Add new keyboard shortcut of moving a task into next display"
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index fd5de91c80ca..b2f125dd2821 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -16,13 +16,6 @@ flag {
}
flag {
- name: "bal_show_toasts"
- namespace: "responsible_apis"
- description: "Enable toasts to indicate (potential) BAL blocking."
- bug: "308059069"
-}
-
-flag {
name: "bal_show_toasts_blocked"
namespace: "responsible_apis"
description: "Enable toasts to indicate actual BAL blocking."
@@ -64,14 +57,6 @@ flag {
bug: "339720406"
}
-# replaced by bal_strict_mode_ro
-flag {
- name: "bal_strict_mode"
- namespace: "responsible_apis"
- description: "Strict mode flag"
- bug: "324089586"
-}
-
flag {
name: "bal_strict_mode_ro"
namespace: "responsible_apis"
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 460df3103488..392c307de7ba 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -13,14 +13,6 @@ flag {
flag {
namespace: "window_surfaces"
- name: "explicit_refresh_rate_hints"
- description: "Performance related hints during transitions"
- is_fixed_read_only: true
- bug: "300019131"
-}
-
-flag {
- namespace: "window_surfaces"
name: "delete_capture_display"
description: "Delete uses of ScreenCapture#captureDisplay"
is_fixed_read_only: true
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 3a035087be7f..4f924a82c9cc 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -49,6 +49,17 @@ flag {
}
flag {
+ name: "respect_animation_clip"
+ namespace: "windowing_frontend"
+ description: "Fix missing clip transformation of animation"
+ bug: "376601866"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "edge_to_edge_by_default"
namespace: "windowing_frontend"
description: "Make app go edge-to-edge by default when targeting SDK 35 or greater"
@@ -65,6 +76,14 @@ flag {
}
flag {
+ name: "disable_opt_out_edge_to_edge"
+ namespace: "windowing_frontend"
+ description: "Deprecate and disable windowOptOutEdgeToEdgeEnforcement"
+ bug: "377864165"
+ is_fixed_read_only: true
+}
+
+flag {
name: "keyguard_going_away_timeout"
namespace: "windowing_frontend"
description: "Allow a maximum of 10 seconds with keyguardGoingAway=true before force-resetting"
@@ -141,12 +160,21 @@ flag {
}
flag {
- name: "delegate_unhandled_drags"
- is_exported: true
- namespace: "multitasking"
- description: "Enables delegating unhandled drags to SystemUI"
- bug: "320797628"
- is_fixed_read_only: true
+ name: "delegate_unhandled_drags"
+ is_exported: true
+ namespace: "multitasking"
+ description: "Enables delegating unhandled drags to SystemUI"
+ bug: "320797628"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "supports_drag_assistant_to_multiwindow"
+ is_exported: true
+ namespace: "multitasking"
+ description: "Enables support for dragging the assistant into multiwindow"
+ bug: "371206207"
+ is_fixed_read_only: true
}
flag {
@@ -249,6 +277,16 @@ flag {
}
flag {
+ name: "system_ui_post_animation_end"
+ namespace: "windowing_frontend"
+ description: "Run AnimatorListener#onAnimationEnd on next frame for SystemUI"
+ bug: "300035126"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "system_ui_immersive_confirmation_dialog"
namespace: "windowing_frontend"
description: "Enable the implementation of the immersive confirmation dialog on system UI side by default"
@@ -350,6 +388,17 @@ flag {
}
flag {
+ name: "defer_predictive_animation_if_no_snapshot"
+ namespace: "windowing_frontend"
+ description: "If no snapshot for previous window, start animation until the client has draw."
+ bug: "374621014"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "disallow_app_progress_embedded_window"
namespace: "windowing_frontend"
description: "Pilfer pointers when app transfer input gesture to embedded window."
@@ -358,4 +407,19 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "predictive_back_system_override_callback"
+ namespace: "windowing_frontend"
+ description: "Provide pre-make predictive back API extension"
+ is_fixed_read_only: true
+ bug: "362938401"
+}
+
+flag {
+ name: "predictive_back_three_button_nav"
+ namespace: "systemui"
+ description: "Enable Predictive Back Animation for 3-button-nav"
+ bug: "373544911"
+}
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index 44dceb9b7edb..4a49bb6720ef 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -63,6 +63,8 @@ public final class ShortcutConstants {
* quickly tapping screen 2 times with two fingers as preferred shortcut.
* {@code QUICK_SETTINGS} for displaying specifying the accessibility services or features which
* choose Quick Settings as preferred shortcut.
+ * {@code KEY_GESTURE} for shortcuts which are directly from key gestures and should be
+ * activated always.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
@@ -73,6 +75,7 @@ public final class ShortcutConstants {
UserShortcutType.TWOFINGER_DOUBLETAP,
UserShortcutType.QUICK_SETTINGS,
UserShortcutType.GESTURE,
+ UserShortcutType.KEY_GESTURE,
UserShortcutType.ALL
})
public @interface UserShortcutType {
@@ -84,8 +87,10 @@ public final class ShortcutConstants {
int TWOFINGER_DOUBLETAP = 1 << 3;
int QUICK_SETTINGS = 1 << 4;
int GESTURE = 1 << 5;
+ int KEY_GESTURE = 1 << 6;
// LINT.ThenChange(:shortcut_type_array)
- int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE;
+ int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE
+ | KEY_GESTURE;
}
/**
@@ -99,7 +104,8 @@ public final class ShortcutConstants {
UserShortcutType.TRIPLETAP,
UserShortcutType.TWOFINGER_DOUBLETAP,
UserShortcutType.QUICK_SETTINGS,
- UserShortcutType.GESTURE
+ UserShortcutType.GESTURE,
+ UserShortcutType.KEY_GESTURE
// LINT.ThenChange(:shortcut_type_intdef)
};
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 2e0ff3db6c50..14ca0f8cae69 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -27,6 +27,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.SERVIC
import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
@@ -187,6 +188,7 @@ public final class ShortcutUtils {
case TWOFINGER_DOUBLETAP ->
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
case QUICK_SETTINGS -> Settings.Secure.ACCESSIBILITY_QS_TARGETS;
+ case KEY_GESTURE -> Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS;
default -> throw new IllegalArgumentException(
"Unsupported user shortcut type: " + type);
};
@@ -209,6 +211,7 @@ public final class ShortcutUtils {
TRIPLETAP;
case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED ->
TWOFINGER_DOUBLETAP;
+ case Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS -> KEY_GESTURE;
default -> throw new IllegalArgumentException(
"Unsupported user shortcut key: " + key);
};
diff --git a/core/java/com/android/internal/app/AppLocaleCollector.java b/core/java/com/android/internal/app/AppLocaleCollector.java
index 56f633fbc6c9..ca1fc0a6d41f 100644
--- a/core/java/com/android/internal/app/AppLocaleCollector.java
+++ b/core/java/com/android/internal/app/AppLocaleCollector.java
@@ -41,7 +41,7 @@ import java.util.Set;
import java.util.stream.Collectors;
/** The Locale data collector for per-app language. */
-public class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
+public class AppLocaleCollector implements LocaleCollectorBase {
private static final String TAG = AppLocaleCollector.class.getSimpleName();
private final Context mContext;
private final String mAppPackageName;
@@ -167,8 +167,8 @@ public class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollecto
}
@Override
- public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
- HashSet<String> langTagsToIgnore = new HashSet<>();
+ public Set<String> getIgnoredLocaleList(boolean translatedOnly) {
+ Set<String> langTagsToIgnore = new HashSet<>();
if (mAppCurrentLocale != null) {
langTagsToIgnore.add(mAppCurrentLocale.getLocale().toLanguageTag());
diff --git a/core/java/com/android/internal/app/LocaleCollectorBase.java b/core/java/com/android/internal/app/LocaleCollectorBase.java
new file mode 100644
index 000000000000..f83907771074
--- /dev/null
+++ b/core/java/com/android/internal/app/LocaleCollectorBase.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.app;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The interface which provides the locale list.
+ */
+public interface LocaleCollectorBase {
+
+ /** Gets the ignored locale list. */
+ Set<String> getIgnoredLocaleList(boolean translatedOnly);
+
+ /** Gets the supported locale list. */
+ Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
+ boolean translatedOnly, boolean isForCountryMode);
+
+ /** Indicates if the class work for specific package. */
+ boolean hasSpecificPackageName();
+}
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index ef4acd1cdfcb..ffffefa64758 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -44,7 +44,10 @@ import java.util.Set;
* <p>It shows suggestions at the top, then the rest of the locales.
* Allows the user to search for locales using both their native name and their name in the
* default locale.</p>
+ *
+ * @deprecated use SettingLib's widget instead of customized UIs.
*/
+@Deprecated
public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener {
private static final String TAG = LocalePickerWithRegion.class.getSimpleName();
private static final String PARENT_FRAGMENT_NAME = "localeListEditor";
@@ -78,21 +81,6 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
default void onParentLocaleSelected(LocaleStore.LocaleInfo locale) {}
}
- /**
- * The interface which provides the locale list.
- */
- interface LocaleCollectorBase {
- /** Gets the ignored locale list. */
- HashSet<String> getIgnoredLocaleList(boolean translatedOnly);
-
- /** Gets the supported locale list. */
- Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
- boolean translatedOnly, boolean isForCountryMode);
-
- /** Indicates if the class work for specific package. */
- boolean hasSpecificPackageName();
- }
-
private static LocalePickerWithRegion createNumberingSystemPicker(
LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
boolean translatedOnly, OnActionExpandListener onActionExpandListener,
diff --git a/core/java/com/android/internal/app/SystemLocaleCollector.java b/core/java/com/android/internal/app/SystemLocaleCollector.java
index 416f510b3230..c7931cbfcc5d 100644
--- a/core/java/com/android/internal/app/SystemLocaleCollector.java
+++ b/core/java/com/android/internal/app/SystemLocaleCollector.java
@@ -24,7 +24,7 @@ import java.util.HashSet;
import java.util.Set;
/** The Locale data collector for System language. */
-class SystemLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
+public class SystemLocaleCollector implements LocaleCollectorBase {
private final Context mContext;
private LocaleList mExplicitLocales;
@@ -32,14 +32,14 @@ class SystemLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBas
this(context, null);
}
- SystemLocaleCollector(Context context, LocaleList explicitLocales) {
+ public SystemLocaleCollector(Context context, LocaleList explicitLocales) {
mContext = context;
mExplicitLocales = explicitLocales;
}
@Override
- public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
- HashSet<String> ignoreList = new HashSet<>();
+ public Set<String> getIgnoredLocaleList(boolean translatedOnly) {
+ Set<String> ignoreList = new HashSet<>();
if (!translatedOnly) {
final LocaleList userLocales = LocalePicker.getLocales();
final String[] langTags = userLocales.toLanguageTags().split(",");
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 364db06976a0..19f8889996dc 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -22,6 +22,7 @@ import android.os.Build;
* Platform private class for determining the type of Android build installed.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidBuildClassifier {
public boolean isDebuggableBuild() {
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index f61157175f9d..f714098e8bc4 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -42,6 +42,7 @@ import java.util.function.Function;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ChangeReporter {
private static final String TAG = "CompatChangeReporter";
private static final Function<Integer, Set<ChangeReport>> NEW_CHANGE_REPORT_SET =
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index 182dba71d0d7..8fd914aecf55 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -28,6 +28,7 @@ import java.util.Set;
* Parcelable containing compat config overrides for a given application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityChangeConfig implements Parcelable {
private final ChangeConfig mChangeConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 03fe4551c249..505fd2319a6b 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -25,6 +25,7 @@ import android.os.Parcelable;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CompatibilityChangeInfo implements Parcelable {
private final long mChangeId;
private final @Nullable String mName;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
index 9a02b7b7aae9..32206c9950dd 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -28,6 +28,7 @@ import java.util.Map;
* Parcelable containing compat config overrides for a given application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverrideConfig implements Parcelable {
public final Map<Long, PackageOverride> overrides;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
index 8652bb6d05e4..998b48a8a76e 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
@@ -26,6 +26,7 @@ import java.util.Map;
* Parcelable containing compat config overrides by application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesByPackageConfig implements Parcelable {
public final Map<String, CompatibilityOverrideConfig> packageNameToOverrides;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
index b408d6440070..c0e2217d509e 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
@@ -29,6 +29,7 @@ import java.util.Map;
* IDs.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesToRemoveByPackageConfig implements Parcelable {
public final Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
index e85afefdc39a..10461ec0b4c6 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
@@ -30,6 +30,7 @@ import java.util.Set;
* <p>This class is separate from CompatibilityOverrideConfig since we only need change IDs.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesToRemoveConfig implements Parcelable {
public final Set<Long> changeIds;
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index e408be2ab471..f018c3a830bd 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -27,6 +27,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* This class contains all the possible override allowed states.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class OverrideAllowedState implements Parcelable {
@IntDef({
ALLOWED,
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 21fbf9d03c71..a50dbb0223b7 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -600,8 +600,8 @@ public class BrightnessSynchronizer {
final ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(BRIGHTNESS_URI, false,
createBrightnessContentObserver(handler), UserHandle.USER_ALL);
- mDisplayManager.registerDisplayListener(mListener, handler,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+ mDisplayManager.registerDisplayListener(mListener, handler, /* eventFlags */ 0,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS);
mIsObserving = true;
}
}
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index ca6c54dc0285..0c2fd4bbd7ae 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -147,8 +147,9 @@ public class DisplayResolutionTracker {
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener) {
manager.registerDisplayListener(listener, handler,
- DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED,
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
ActivityThread.currentPackageName());
}
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index de3edeb22a40..15736ed3f4e1 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -18,14 +18,13 @@ package com.android.internal.os;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import dalvik.annotation.optimization.CriticalNative;
-
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -255,8 +254,8 @@ public class KernelSingleUidTimeReader {
* the delta in the supplied array container.
*/
public void addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs,
- LongArrayMultiStateCounter.LongArrayContainer deltaContainer) {
- mInjector.addDelta(uid, counter, timestampMs, deltaContainer);
+ long[] delta) {
+ mInjector.addDelta(uid, counter, timestampMs, delta);
}
@VisibleForTesting
@@ -274,15 +273,13 @@ public class KernelSingleUidTimeReader {
* The delta is also returned via the optional deltaOut parameter.
*/
public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs,
- LongArrayMultiStateCounter.LongArrayContainer deltaOut) {
- return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs,
- deltaOut != null ? deltaOut.mNativeObject : 0);
+ long[] deltaOut) {
+ return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs, deltaOut);
}
- @CriticalNative
private static native boolean addDeltaFromBpf(int uid,
long longArrayMultiStateCounterNativePointer, long timestampMs,
- long longArrayContainerNativePointer);
+ @Nullable long[] deltaOut);
/**
* Used for testing.
@@ -291,14 +288,14 @@ public class KernelSingleUidTimeReader {
*/
public boolean addDeltaForTest(int uid, LongArrayMultiStateCounter counter,
long timestampMs, long[][] timeInFreqDataNanos,
- LongArrayMultiStateCounter.LongArrayContainer deltaOut) {
+ long[] deltaOut) {
return addDeltaForTest(uid, counter.mNativeObject, timestampMs, timeInFreqDataNanos,
- deltaOut != null ? deltaOut.mNativeObject : 0);
+ deltaOut);
}
private static native boolean addDeltaForTest(int uid,
long longArrayMultiStateCounterNativePointer, long timestampMs,
- long[][] timeInFreqDataNanos, long longArrayContainerNativePointer);
+ long[][] timeInFreqDataNanos, long[] deltaOut);
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 489721fbc10e..2931bd2c83dd 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -30,9 +30,6 @@ import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
-import java.util.Arrays;
-import java.util.concurrent.atomic.AtomicReference;
-
/**
* Performs per-state counting of multi-element values over time. The class' behavior is illustrated
* by this example:
@@ -44,15 +41,14 @@ import java.util.concurrent.atomic.AtomicReference;
* counter.setState(1, 1000);
*
* // At 3000 ms, the tracked values are updated to {30, 300}
- * arrayContainer.setValues(new long[]{{30, 300}};
- * counter.updateValues(arrayContainer, 3000);
+ * counter.updateValues(arrayContainer, new long[]{{30, 300}, 3000);
*
* // The values are distributed between states 0 and 1 according to the time
* // spent in those respective states. In this specific case, 1000 and 2000 ms.
- * counter.getValues(arrayContainer, 0);
- * // arrayContainer now has values {10, 100}
- * counter.getValues(arrayContainer, 1);
- * // arrayContainer now has values {20, 200}
+ * counter.getCounts(array, 0);
+ * // array now has values {10, 100}
+ * counter.getCounts(array, 1);
+ * // array now has values {20, 200}
* </pre>
*
* The tracked values are expected to increase monotonically.
@@ -60,112 +56,9 @@ import java.util.concurrent.atomic.AtomicReference;
* @hide
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("LongArrayMultiStateCounter_host")
+@RavenwoodRedirectionClass("LongArrayMultiStateCounter_ravenwood")
public final class LongArrayMultiStateCounter implements Parcelable {
-
- /**
- * Container for a native equivalent of a long[].
- */
- @RavenwoodKeepWholeClass
- @RavenwoodRedirectionClass("LongArrayContainer_host")
- public static class LongArrayContainer {
- private static NativeAllocationRegistry sRegistry;
-
- // Visible to other objects in this package so that it can be passed to @CriticalNative
- // methods.
- final long mNativeObject;
- private final int mLength;
-
- public LongArrayContainer(int length) {
- mLength = length;
- mNativeObject = native_init(length);
- registerNativeAllocation();
- }
-
- @RavenwoodReplace
- private void registerNativeAllocation() {
- if (sRegistry == null) {
- synchronized (LongArrayMultiStateCounter.class) {
- if (sRegistry == null) {
- sRegistry = NativeAllocationRegistry.createMalloced(
- LongArrayContainer.class.getClassLoader(), native_getReleaseFunc());
- }
- }
- }
- sRegistry.registerNativeAllocation(this, mNativeObject);
- }
-
- private void registerNativeAllocation$ravenwood() {
- // No-op under ravenwood
- }
-
- /**
- * Copies the supplied values into the underlying native array.
- */
- public void setValues(long[] array) {
- if (array.length != mLength) {
- throw new IllegalArgumentException(
- "Invalid array length: " + array.length + ", expected: " + mLength);
- }
- native_setValues(mNativeObject, array);
- }
-
- /**
- * Copies the underlying native array values to the supplied array.
- */
- public void getValues(long[] array) {
- if (array.length != mLength) {
- throw new IllegalArgumentException(
- "Invalid array length: " + array.length + ", expected: " + mLength);
- }
- native_getValues(mNativeObject, array);
- }
-
- /**
- * Combines contained values into a smaller array by aggregating them
- * according to an index map.
- */
- public boolean combineValues(long[] array, int[] indexMap) {
- if (indexMap.length != mLength) {
- throw new IllegalArgumentException(
- "Wrong index map size " + indexMap.length + ", expected " + mLength);
- }
- return native_combineValues(mNativeObject, array, indexMap);
- }
-
- @Override
- public String toString() {
- final long[] array = new long[mLength];
- getValues(array);
- return Arrays.toString(array);
- }
-
- @CriticalNative
- @RavenwoodRedirect
- private static native long native_init(int length);
-
- @CriticalNative
- @RavenwoodRedirect
- private static native long native_getReleaseFunc();
-
- @FastNative
- @RavenwoodRedirect
- private static native void native_setValues(long nativeObject, long[] array);
-
- @FastNative
- @RavenwoodRedirect
- private static native void native_getValues(long nativeObject, long[] array);
-
- @FastNative
- @RavenwoodRedirect
- private static native boolean native_combineValues(long nativeObject, long[] array,
- int[] indexMap);
- }
-
private static volatile NativeAllocationRegistry sRegistry;
- private static final AtomicReference<LongArrayContainer> sTmpArrayContainer =
- new AtomicReference<>();
-
private final int mStateCount;
private final int mLength;
@@ -257,41 +150,14 @@ public final class LongArrayMultiStateCounter implements Parcelable {
throw new IllegalArgumentException(
"Invalid array length: " + values.length + ", expected: " + mLength);
}
- LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
- if (container == null || container.mLength != values.length) {
- container = new LongArrayContainer(values.length);
- }
- container.setValues(values);
- native_setValues(mNativeObject, state, container.mNativeObject);
- sTmpArrayContainer.set(container);
- }
-
- /**
- * Sets the new values. The delta between the previously set values and these values
- * is distributed among the state according to the time the object spent in those states
- * since the previous call to updateValues.
- */
- public void updateValues(long[] values, long timestampMs) {
- LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
- if (container == null || container.mLength != values.length) {
- container = new LongArrayContainer(values.length);
- }
- container.setValues(values);
- updateValues(container, timestampMs);
- sTmpArrayContainer.set(container);
+ native_setValues(mNativeObject, state, values);
}
/**
* Adds the supplied values to the current accumulated values in the counter.
*/
public void incrementValues(long[] values, long timestampMs) {
- LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
- if (container == null || container.mLength != values.length) {
- container = new LongArrayContainer(values.length);
- }
- container.setValues(values);
- native_incrementValues(mNativeObject, container.mNativeObject, timestampMs);
- sTmpArrayContainer.set(container);
+ native_incrementValues(mNativeObject, values, timestampMs);
}
/**
@@ -299,24 +165,23 @@ public final class LongArrayMultiStateCounter implements Parcelable {
* is distributed among the state according to the time the object spent in those states
* since the previous call to updateValues.
*/
- public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) {
- if (longArrayContainer.mLength != mLength) {
+ public void updateValues(long[] values, long timestampMs) {
+ if (values.length != mLength) {
throw new IllegalArgumentException(
- "Invalid array length: " + longArrayContainer.mLength + ", expected: "
- + mLength);
+ "Invalid array length: " + values.length + ", expected: " + mLength);
}
- native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs);
+ native_updateValues(mNativeObject, values, timestampMs);
}
/**
* Adds the supplied values to the current accumulated values in the counter.
*/
- public void addCounts(LongArrayContainer counts) {
- if (counts.mLength != mLength) {
+ public void addCounts(long[] counts) {
+ if (counts.length != mLength) {
throw new IllegalArgumentException(
- "Invalid array length: " + counts.mLength + ", expected: " + mLength);
+ "Invalid array length: " + counts.length + ", expected: " + mLength);
}
- native_addCounts(mNativeObject, counts.mNativeObject);
+ native_addCounts(mNativeObject, counts);
}
/**
@@ -330,29 +195,15 @@ public final class LongArrayMultiStateCounter implements Parcelable {
* Populates the array with the accumulated counts for the specified state.
*/
public void getCounts(long[] counts, int state) {
- LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
- if (container == null || container.mLength != counts.length) {
- container = new LongArrayContainer(counts.length);
- }
- getCounts(container, state);
- container.getValues(counts);
- sTmpArrayContainer.set(container);
- }
-
- /**
- * Populates longArrayContainer with the accumulated counts for the specified state.
- */
- public void getCounts(LongArrayContainer longArrayContainer, int state) {
if (state < 0 || state >= mStateCount) {
throw new IllegalArgumentException(
"State: " + state + ", outside the range: [0-" + mStateCount + "]");
}
- if (longArrayContainer.mLength != mLength) {
+ if (counts.length != mLength) {
throw new IllegalArgumentException(
- "Invalid array length: " + longArrayContainer.mLength
- + ", expected: " + mLength);
+ "Invalid array length: " + counts.length + ", expected: " + mLength);
}
- native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state);
+ native_getCounts(mNativeObject, counts, state);
}
@Override
@@ -370,18 +221,17 @@ public final class LongArrayMultiStateCounter implements Parcelable {
return 0;
}
- public static final Creator<LongArrayMultiStateCounter> CREATOR =
- new Creator<LongArrayMultiStateCounter>() {
- @Override
- public LongArrayMultiStateCounter createFromParcel(Parcel in) {
- return new LongArrayMultiStateCounter(in);
- }
+ public static final Creator<LongArrayMultiStateCounter> CREATOR = new Creator<>() {
+ @Override
+ public LongArrayMultiStateCounter createFromParcel(Parcel in) {
+ return new LongArrayMultiStateCounter(in);
+ }
- @Override
- public LongArrayMultiStateCounter[] newArray(int size) {
- return new LongArrayMultiStateCounter[size];
- }
- };
+ @Override
+ public LongArrayMultiStateCounter[] newArray(int size) {
+ return new LongArrayMultiStateCounter[size];
+ }
+ };
@CriticalNative
@@ -406,34 +256,31 @@ public final class LongArrayMultiStateCounter implements Parcelable {
private static native void native_copyStatesFrom(long nativeObjectTarget,
long nativeObjectSource);
- @CriticalNative
+ @FastNative
@RavenwoodRedirect
- private static native void native_setValues(long nativeObject, int state,
- long longArrayContainerNativeObject);
+ private static native void native_setValues(long nativeObject, int state, long[] values);
- @CriticalNative
+ @FastNative
@RavenwoodRedirect
- private static native void native_updateValues(long nativeObject,
- long longArrayContainerNativeObject, long timestampMs);
+ private static native void native_updateValues(long nativeObject, long[] values,
+ long timestampMs);
- @CriticalNative
+ @FastNative
@RavenwoodRedirect
- private static native void native_incrementValues(long nativeObject,
- long longArrayContainerNativeObject, long timestampMs);
+ private static native void native_incrementValues(long nativeObject, long[] values,
+ long timestampMs);
- @CriticalNative
+ @FastNative
@RavenwoodRedirect
- private static native void native_addCounts(long nativeObject,
- long longArrayContainerNativeObject);
+ private static native void native_addCounts(long nativeObject, long[] counts);
@CriticalNative
@RavenwoodRedirect
private static native void native_reset(long nativeObject);
- @CriticalNative
+ @FastNative
@RavenwoodRedirect
- private static native void native_getCounts(long nativeObject,
- long longArrayContainerNativeObject, int state);
+ private static native void native_getCounts(long nativeObject, long[] counts, int state);
@FastNative
@RavenwoodRedirect
diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
index 9ce8ea8e16ef..7030d8e84b70 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
@@ -18,6 +18,7 @@ package com.android.internal.os;
import android.os.BadParcelableException;
import android.os.Parcel;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.util.Arrays;
import java.util.HashMap;
@@ -25,7 +26,8 @@ import java.util.HashMap;
/**
* Native implementation substitutions for the LongArrayMultiStateCounter class.
*/
-public class LongArrayMultiStateCounter_host {
+@RavenwoodKeepWholeClass
+class LongArrayMultiStateCounter_ravenwood {
/**
* A reimplementation of {@link LongArrayMultiStateCounter}, only in
@@ -286,15 +288,12 @@ public class LongArrayMultiStateCounter_host {
return getInstance(instanceId).mArrayLength;
}
- public static void native_setValues(long instanceId, int state, long containerInstanceId) {
- getInstance(instanceId).setValue(state,
- LongArrayContainer_host.getInstance(containerInstanceId));
+ public static void native_setValues(long instanceId, int state, long[] values) {
+ getInstance(instanceId).setValue(state, values);
}
- public static void native_updateValues(long instanceId, long containerInstanceId,
- long timestampMs) {
- getInstance(instanceId).updateValue(
- LongArrayContainer_host.getInstance(containerInstanceId), timestampMs);
+ public static void native_updateValues(long instanceId, long[] values, long timestampMs) {
+ getInstance(instanceId).updateValue(values, timestampMs);
}
public static void native_setState(long instanceId, int state, long timestampMs) {
@@ -305,19 +304,16 @@ public class LongArrayMultiStateCounter_host {
getInstance(targetInstanceId).copyStatesFrom(getInstance(sourceInstanceId));
}
- public static void native_incrementValues(long instanceId, long containerInstanceId,
- long timestampMs) {
- getInstance(instanceId).incrementValues(
- LongArrayContainer_host.getInstance(containerInstanceId), timestampMs);
+ public static void native_incrementValues(long instanceId, long[] delta, long timestampMs) {
+ getInstance(instanceId).incrementValues(delta, timestampMs);
}
- public static void native_addCounts(long instanceId, long containerInstanceId) {
- getInstance(instanceId).addCounts(LongArrayContainer_host.getInstance(containerInstanceId));
+ public static void native_addCounts(long instanceId, long[] counts) {
+ getInstance(instanceId).addCounts(counts);
}
- public static void native_getCounts(long instanceId, long containerInstanceId, int state) {
- getInstance(instanceId).getValues(LongArrayContainer_host.getInstance(containerInstanceId),
- state);
+ public static void native_getCounts(long instanceId, long[] counts, int state) {
+ getInstance(instanceId).getValues(counts, state);
}
public static void native_reset(long instanceId) {
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index c386a86f5906..ee855d58c874 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -60,7 +60,7 @@ import libcore.util.NativeAllocationRegistry;
* @hide
*/
@RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("LongMultiStateCounter_host")
+@RavenwoodRedirectionClass("LongMultiStateCounter_ravenwood")
public final class LongMultiStateCounter implements Parcelable {
private static NativeAllocationRegistry sRegistry;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java b/core/java/com/android/internal/os/LongMultiStateCounter_ravenwood.java
index 1d95aa143549..42db37f1f82f 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter_ravenwood.java
@@ -18,13 +18,15 @@ package com.android.internal.os;
import android.os.BadParcelableException;
import android.os.Parcel;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.util.HashMap;
/**
* Native implementation substitutions for the LongMultiStateCounter class.
*/
-public class LongMultiStateCounter_host {
+@RavenwoodKeepWholeClass
+class LongMultiStateCounter_ravenwood {
/**
* A reimplementation of {@link com.android.internal.os.LongMultiStateCounter}, only in
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index 70b7953ed364..c953d88c9482 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -214,13 +214,30 @@ public class AconfigFlags {
* @param parser XML parser object currently parsing an element
* @return true if the element is disabled because of its feature flag
*/
+ public boolean skipCurrentElement(@Nullable ParsingPackage pkg, @NonNull XmlPullParser parser) {
+ return skipCurrentElement(pkg, parser, /* allowNoNamespace= */ false);
+ }
+
+ /**
+ * Check if the element in {@code parser} should be skipped because of the feature flag.
+ * @param pkg The package being parsed
+ * @param parser XML parser object currently parsing an element
+ * @param allowNoNamespace Whether to allow namespace null
+ * @return true if the element is disabled because of its feature flag
+ */
public boolean skipCurrentElement(
- @NonNull ParsingPackage pkg,
- @NonNull XmlResourceParser parser) {
+ @Nullable ParsingPackage pkg,
+ @NonNull XmlPullParser parser,
+ boolean allowNoNamespace
+ ) {
if (!Flags.manifestFlagging()) {
return false;
}
String featureFlag = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "featureFlag");
+ // If allow no namespace, make another attempt to parse feature flag with null namespace.
+ if (featureFlag == null && allowNoNamespace) {
+ featureFlag = parser.getAttributeValue(null, "featureFlag");
+ }
if (featureFlag == null) {
return false;
}
@@ -242,7 +259,7 @@ public class AconfigFlags {
+ " behind feature flag " + featureFlag + " = " + flagValue);
shouldSkip = true;
}
- if (android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
+ if (pkg != null && android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
pkg.addFeatureFlag(featureFlag, flagValue);
}
return shouldSkip;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bd746d5ecf04..270cf085b06f 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -46,9 +46,13 @@ import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.WindowConfiguration;
+import android.app.jank.AppJankStats;
+import android.app.jank.JankTracker;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
@@ -283,6 +287,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private final WearGestureInterceptionDetector mWearGestureInterceptionDetector;
+ @Nullable
+ private AppJankStatsCallback mAppJankStatsCallback;
+
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
@@ -2336,6 +2343,38 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
+ public interface AppJankStatsCallback {
+ /**
+ * Called when app jank stats are being reported to the platform or when a widget needs
+ * to obtain a reference to the JankTracker instance to update states.
+ */
+ JankTracker getAppJankTracker();
+ }
+
+ public void setAppJankStatsCallback(AppJankStatsCallback
+ jankStatsReportedCallback) {
+ mAppJankStatsCallback = jankStatsReportedCallback;
+ }
+
+ @Override
+ @FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void reportAppJankStats(@NonNull AppJankStats appJankStats) {
+ if (mAppJankStatsCallback != null) {
+ JankTracker jankTracker = mAppJankStatsCallback.getAppJankTracker();
+ if (jankTracker != null) {
+ jankTracker.mergeAppJankStats(appJankStats);
+ }
+ }
+ }
+
+ @Override
+ public @Nullable JankTracker getJankTracker() {
+ if (mAppJankStatsCallback != null) {
+ return mAppJankStatsCallback.getAppJankTracker();
+ }
+ return null;
+ }
+
@Override
public String toString() {
return super.toString() + "[" + getTitleSuffix(mWindow.getAttributes()) + "]";
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 30b160ab161b..3303d875c427 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -15,6 +15,12 @@
*/
package com.android.internal.ravenwood;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
@@ -28,19 +34,9 @@ import android.ravenwood.annotation.RavenwoodReplace;
public final class RavenwoodEnvironment {
public static final String TAG = "RavenwoodEnvironment";
- private static final RavenwoodEnvironment sInstance;
- private static final Workaround sWorkaround;
-
- private RavenwoodEnvironment() {
- }
-
- static {
- sInstance = new RavenwoodEnvironment();
- sWorkaround = new Workaround();
- ensureRavenwoodInitialized();
- }
+ private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment();
- public static RuntimeException notSupportedOnDevice() {
+ private static RuntimeException notSupportedOnDevice() {
return new UnsupportedOperationException("This method can only be used on Ravenwood");
}
@@ -52,15 +48,6 @@ public final class RavenwoodEnvironment {
}
/**
- * Initialize the ravenwood environment if it hasn't happened already, if running on Ravenwood.
- *
- * No-op if called on the device side.
- */
- @RavenwoodRedirect
- public static void ensureRavenwoodInitialized() {
- }
-
- /**
* USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment.
*
* <p>Using this allows code to behave differently on a real device and on Ravenwood, but
@@ -91,18 +78,6 @@ public final class RavenwoodEnvironment {
}
/**
- * See {@link Workaround}. It's only usable on Ravenwood.
- */
- @RavenwoodReplace
- public static Workaround workaround() {
- throw notSupportedOnDevice();
- }
-
- private static Workaround workaround$ravenwood() {
- return sWorkaround;
- }
-
- /**
* @return the "ravenwood-runtime" directory.
*/
@RavenwoodRedirect
@@ -110,19 +85,19 @@ public final class RavenwoodEnvironment {
throw notSupportedOnDevice();
}
- /**
- * A set of APIs used to work around missing features on Ravenwood. Ideally, this class should
- * be empty, and all its APIs should be able to be implemented properly.
- */
- public static class Workaround {
- Workaround() {
- }
+ /** @hide */
+ public static class CompatIdsForTest {
+ // Enabled by default
+ @ChangeId
+ public static final long TEST_COMPAT_ID_1 = 368131859L;
+
+ @Disabled
+ @ChangeId public static final long TEST_COMPAT_ID_2 = 368131701L;
+
+ @EnabledAfter(targetSdkVersion = S)
+ @ChangeId public static final long TEST_COMPAT_ID_3 = 368131659L;
- /**
- * @return whether the app's target SDK level is at least Q.
- */
- public boolean isTargetSdkAtLeastQ() {
- return true;
- }
+ @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
+ @ChangeId public static final long TEST_COMPAT_ID_4 = 368132057L;
}
}
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index 1938cdb0ba84..40161023eae4 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -40,9 +40,6 @@ public class StatusBarIcon implements Parcelable {
public enum Type {
// Notification: the sender avatar for important conversations
PeopleAvatar,
- // Notification: the monochrome version of the app icon if available; otherwise fall back to
- // the small icon
- MaybeMonochromeAppIcon,
// Notification: the small icon from the notification
NotifSmallIcon,
// The wi-fi, cellular or battery icon.
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index b5c87868af12..0e85e046e1b6 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -27,6 +27,7 @@ import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.satellite.NtnSignalStrength;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
@@ -85,4 +86,5 @@ oneway interface IPhoneStateListener {
void onCarrierRoamingNtnModeChanged(in boolean active);
void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible);
void onCarrierRoamingNtnAvailableServicesChanged(in int[] availableServices);
+ void onCarrierRoamingNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 1c76a6cd4bba..0f268d5de62b 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -29,6 +29,7 @@ import android.telephony.ims.ImsReasonInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.satellite.NtnSignalStrength;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
@@ -125,8 +126,10 @@ interface ITelephonyRegistry {
void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active);
void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible);
void notifyCarrierRoamingNtnAvailableServicesChanged(int subId, in int[] availableServices);
+ void notifyCarrierRoamingNtnSignalStrengthChanged(int subId, in NtnSignalStrength ntnSignalStrength);
void addSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg, String featureId);
void removeSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg);
void notifySatelliteStateChanged(boolean isEnabled);
+
}
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 30deb499594c..fb6937c94a3e 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -63,6 +63,7 @@ public final class NotificationProgressDrawable extends Drawable {
private final ArrayList<Part> mParts = new ArrayList<>();
+ private final RectF mSegRectF = new RectF();
private final Rect mPointRect = new Rect();
private final RectF mPointRectF = new RectF();
@@ -198,22 +199,42 @@ public final class NotificationProgressDrawable extends Drawable {
mState.mSegSegGap, x + segWidth, totalWidth);
final float end = x + segWidth - endOffset;
- // Transparent is not allowed (and also is the default in the data), so use that
- // as a sentinel to be replaced by default
- mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
- : mState.mStrokeColor);
- mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
- : mState.mFadedStrokeColor);
-
- // Leave space for the rounded line cap which extends beyond start/end.
- final float capWidth = mStrokePaint.getStrokeWidth() / 2F;
-
- canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY,
- segment.mDashed ? mDashedStrokePaint : mStrokePaint);
-
// Advance the current position to account for the segment's fraction of the total
// width (ignoring offset and padding)
x += segWidth;
+
+ // No space left to draw the segment
+ if (start > end) continue;
+
+ if (segment.mDashed) {
+ // No caps when the segment is dashed.
+
+ mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
+ : mState.mFadedStrokeColor);
+ canvas.drawLine(start, centerY, end, centerY, mDashedStrokePaint);
+ } else if (end - start < mState.mStrokeWidth) {
+ // Not enough segment length to draw the caps
+
+ final float rad = (end - start) / 2F;
+ final float capWidth = mStrokePaint.getStrokeWidth() / 2F;
+
+ mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
+ : mState.mStrokeColor);
+
+ mSegRectF.set(start, centerY - capWidth, end, centerY + capWidth);
+ canvas.drawRoundRect(mSegRectF, rad, rad, mFillPaint);
+ } else {
+ // Leave space for the rounded line cap which extends beyond start/end.
+ final float capWidth = mStrokePaint.getStrokeWidth() / 2F;
+
+ // Transparent is not allowed (and also is the default in the data), so use that
+ // as a sentinel to be replaced by default
+ mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
+ : mState.mStrokeColor);
+
+ canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY,
+ mStrokePaint);
+ }
} else if (part instanceof Point point) {
final float pointWidth = 2 * pointRadius;
float start = x - pointRadius;
@@ -232,7 +253,7 @@ public final class NotificationProgressDrawable extends Drawable {
} else {
// TODO: b/367804171 - actually use a vector asset for the default point
// rather than drawing it as a box?
- mPointRectF.set(mPointRect);
+ mPointRectF.set(start, centerY - pointRadius, end, centerY + pointRadius);
final float inset = mState.mPointRectInset;
final float cornerRadius = mState.mPointRectCornerRadius;
mPointRectF.inset(inset, inset);
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index adcc0f64b598..5fc61b00e331 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -22,11 +22,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -35,8 +31,6 @@ import android.util.AttributeSet;
import android.view.RemotableViewMethod;
import android.widget.RemoteViews;
-import com.android.internal.R;
-
/**
* An image view that holds the icon displayed at the start of a notification row.
* This can generally either display the "small icon" of a notification set via
@@ -48,7 +42,6 @@ public class NotificationRowIconView extends CachingIconView {
private NotificationIconProvider mIconProvider;
private boolean mApplyCircularCrop = false;
- private boolean mShouldShowAppIcon = false;
private Drawable mAppIcon = null;
// Padding, background and colors set on the view prior to being overridden when showing the app
@@ -77,17 +70,6 @@ public class NotificationRowIconView extends CachingIconView {
super(context, attrs, defStyleAttr, defStyleRes);
}
- @Override
- protected void onFinishInflate() {
- // If showing the app icon, we don't need background or padding.
- if (Flags.notificationsUseAppIcon()) {
- setPadding(0, 0, 0, 0);
- setBackground(null);
- }
-
- super.onFinishInflate();
- }
-
/**
* Sets the icon provider for this view. This is used to determine whether we should show the
* app icon instead of the small icon, and to fetch the app icon if needed.
@@ -153,37 +135,12 @@ public class NotificationRowIconView extends CachingIconView {
return super.setImageIconAsync(icon);
}
- /** Whether the icon represents the app icon (instead of the small icon). */
- @RemotableViewMethod
- public void setShouldShowAppIcon(boolean shouldShowAppIcon) {
- if (Flags.notificationsUseAppIconInRow()) {
- if (mShouldShowAppIcon == shouldShowAppIcon) {
- return; // no change
- }
-
- mShouldShowAppIcon = shouldShowAppIcon;
- if (mShouldShowAppIcon) {
- adjustViewForAppIcon();
- } else {
- // Restore original padding and background if needed
- restoreViewForSmallIcon();
- }
- }
- }
-
/**
* Override padding and background from the view to display the app icon.
*/
private void adjustViewForAppIcon() {
removePadding();
-
- if (Flags.notificationsUseAppIconInRow()) {
- addWhiteBackground();
- } else {
- // No need to set the background for notification redesign, since the icon
- // factory already does that for us.
- removeBackground();
- }
+ removeBackground();
}
/**
@@ -221,21 +178,6 @@ public class NotificationRowIconView extends CachingIconView {
setBackground(null);
}
- private void addWhiteBackground() {
- if (mOriginalBackground == null) {
- mOriginalBackground = getBackground();
- }
-
- // Make the background white in case the icon itself doesn't have one.
- ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
- PorterDuff.Mode.SRC_ATOP);
-
- if (mOriginalBackground == null) {
- setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
- }
- getBackground().mutate().setColorFilter(colorFilter);
- }
-
private void restoreBackground() {
// NOTE: This will not work if the original background was null, but that's better than
// accidentally clearing the background. We expect that there's generally going to be one
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index b6383d9f0754..38685b652c50 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -530,8 +530,26 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
int rootViewTopOnWindow = mTmpCoords[1];
int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
- mCoordsOnWindow.set(
- Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
+ // In some cases, app can have specific Window for Android UI components such as EditText.
+ // In this case, Window bounds != App bounds. Hence, instead of ensuring non-negative
+ // PopupWindow coords, app bounds should be used to limit the coords. For instance,
+ // ____ <- |
+ // | | |W1 & App bounds
+ // |___| |
+ // |W2 | | W2 has smaller bounds and contain EditText where PopupWindow will be opened.
+ // ---- <-|
+ // Here, we'll open PopupWindow upwards, but as PopupWindow is anchored based on W2, it
+ // will have negative Y coords. This negative Y is safe to use because it's still within app
+ // bounds. However, if it gets out of app bounds, we should clamp it to 0.
+ Rect appBounds = mContext
+ .getResources().getConfiguration().windowConfiguration.getAppBounds();
+ mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen);
+ if (rootViewLeftOnScreen + mCoordsOnWindow.x < appBounds.left) {
+ mCoordsOnWindow.x = 0;
+ }
+ if (rootViewTopOnScreen + mCoordsOnWindow.y < appBounds.top) {
+ mCoordsOnWindow.y = 0;
+ }
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 212df02b1bd3..0761a244b9f2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -15,12 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.Theme;
-import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
@@ -28,7 +30,11 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchCancelModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchDownModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchUpModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
@@ -49,12 +55,12 @@ public class CoreDocument {
ArrayList<Operation> mOperations;
- RootLayoutComponent mRootLayoutComponent = null;
+ @Nullable RootLayoutComponent mRootLayoutComponent = null;
RemoteComposeState mRemoteComposeState = new RemoteComposeState();
- TimeVariables mTimeVariables = new TimeVariables();
+ @NonNull TimeVariables mTimeVariables = new TimeVariables();
// Semantic version of the document
- Version mVersion = new Version(0, 1, 0);
+ @NonNull Version mVersion = new Version(0, 1, 0);
String mContentDescription; // text description of the document (used for accessibility)
@@ -72,6 +78,8 @@ public class CoreDocument {
private final HashMap<Long, IntegerExpression> mIntegerExpressions = new HashMap<>();
+ private HashSet<Component> mAppliedTouchOperations = new HashSet<>();
+
private int mLastId = 1; // last component id when inflating the file
public String getContentDescription() {
@@ -272,6 +280,7 @@ public class CoreDocument {
*
* @return list of click areas in document coordinates
*/
+ @NonNull
public Set<ClickAreaRepresentation> getClickAreas() {
return mClickAreas;
}
@@ -281,6 +290,7 @@ public class CoreDocument {
*
* @return returns the root component if it exists, null otherwise
*/
+ @Nullable
public RootLayoutComponent getRootLayoutComponent() {
return mRootLayoutComponent;
}
@@ -298,6 +308,7 @@ public class CoreDocument {
* @param id component id
* @return the component if it exists, null otherwise
*/
+ @Nullable
public Component getComponent(int id) {
if (mRootLayoutComponent != null) {
return mRootLayoutComponent.getComponent(id);
@@ -310,6 +321,7 @@ public class CoreDocument {
*
* @return a standardized string representation of the component hierarchy
*/
+ @NonNull
public String displayHierarchy() {
StringSerializer serializer = new StringSerializer();
for (Operation op : mOperations) {
@@ -329,7 +341,8 @@ public class CoreDocument {
* @param targetId the id of the value to update with the expression
* @param context the current context
*/
- public void evaluateIntExpression(long expressionId, int targetId, RemoteContext context) {
+ public void evaluateIntExpression(
+ long expressionId, int targetId, @NonNull RemoteContext context) {
IntegerExpression expression = mIntegerExpressions.get(expressionId);
if (expression != null) {
int v = expression.evaluate(context);
@@ -337,22 +350,46 @@ public class CoreDocument {
}
}
+ // ============== Haptic support ==================
+ public interface HapticEngine {
+ void haptic(int type);
+ }
+
+ HapticEngine mHapticEngine;
+
+ public void setHapticEngine(HapticEngine engine) {
+ mHapticEngine = engine;
+ }
+
+ public void haptic(int type) {
+ if (mHapticEngine != null) {
+ mHapticEngine.haptic(type);
+ }
+ }
+
+ // ============== Haptic support ==================
+
+ public void appliedTouchOperation(Component operation) {
+ mAppliedTouchOperations.add(operation);
+ }
+
/** Callback interface for host actions */
public interface ActionCallback {
- // TODO: add payload support
- void onAction(String name);
+ void onAction(String name, Object value);
}
- HashSet<ActionCallback> mActionListeners = new HashSet<ActionCallback>();
+ @NonNull HashSet<ActionCallback> mActionListeners = new HashSet<ActionCallback>();
/**
* Warn action listeners for the given named action
*
* @param name the action name
+ * @param value a parameter to the action
*/
- public void runNamedAction(String name) {
+ public void runNamedAction(String name, Object value) {
+ // TODO: we might add an interface to group all valid parameter types
for (ActionCallback callback : mActionListeners) {
- callback.onAction(name);
+ callback.onAction(name, value);
}
}
@@ -374,8 +411,9 @@ public class CoreDocument {
void click(int id, String metadata);
}
- HashSet<ClickCallbacks> mClickListeners = new HashSet<>();
- HashSet<ClickAreaRepresentation> mClickAreas = new HashSet<>();
+ @NonNull HashSet<ClickCallbacks> mClickListeners = new HashSet<>();
+ @NonNull HashSet<TouchListener> mTouchListeners = new HashSet<>();
+ @NonNull HashSet<ClickAreaRepresentation> mClickAreas = new HashSet<>();
static class Version {
public final int major;
@@ -456,7 +494,7 @@ public class CoreDocument {
}
/** Load operations from the given buffer */
- public void initFromBuffer(RemoteComposeBuffer buffer) {
+ public void initFromBuffer(@NonNull RemoteComposeBuffer buffer) {
mOperations = new ArrayList<Operation>();
buffer.inflateFromBuffer(mOperations);
for (Operation op : mOperations) {
@@ -484,12 +522,16 @@ public class CoreDocument {
* @param operations flat list of operations
* @return nested list of operations / components
*/
- private ArrayList<Operation> inflateComponents(ArrayList<Operation> operations) {
+ @NonNull
+ private ArrayList<Operation> inflateComponents(@NonNull ArrayList<Operation> operations) {
Component currentComponent = null;
ArrayList<Component> components = new ArrayList<>();
ArrayList<Operation> finalOperationsList = new ArrayList<>();
ArrayList<Operation> ops = finalOperationsList;
ClickModifierOperation currentClickModifier = null;
+ TouchDownModifierOperation currentTouchDownModifier = null;
+ TouchUpModifierOperation currentTouchUpModifier = null;
+ TouchCancelModifierOperation currentTouchCancelModifier = null;
LoopOperation currentLoop = null;
mLastId = -1;
@@ -519,10 +561,30 @@ public class CoreDocument {
// TODO: refactor to add container <- component...
currentClickModifier = (ClickModifierOperation) o;
ops = currentClickModifier.getList();
- } else if (o instanceof ClickModifierEnd) {
+ } else if (o instanceof TouchDownModifierOperation) {
+ currentTouchDownModifier = (TouchDownModifierOperation) o;
+ ops = currentTouchDownModifier.getList();
+ } else if (o instanceof TouchUpModifierOperation) {
+ currentTouchUpModifier = (TouchUpModifierOperation) o;
+ ops = currentTouchUpModifier.getList();
+ } else if (o instanceof TouchCancelModifierOperation) {
+ currentTouchCancelModifier = (TouchCancelModifierOperation) o;
+ ops = currentTouchCancelModifier.getList();
+ } else if (o instanceof OperationsListEnd) {
ops = currentComponent.getList();
- ops.add(currentClickModifier);
- currentClickModifier = null;
+ if (currentClickModifier != null) {
+ ops.add(currentClickModifier);
+ currentClickModifier = null;
+ } else if (currentTouchDownModifier != null) {
+ ops.add(currentTouchDownModifier);
+ currentTouchDownModifier = null;
+ } else if (currentTouchUpModifier != null) {
+ ops.add(currentTouchUpModifier);
+ currentTouchUpModifier = null;
+ } else if (currentTouchCancelModifier != null) {
+ ops.add(currentTouchCancelModifier);
+ currentTouchCancelModifier = null;
+ }
} else if (o instanceof LoopOperation) {
currentLoop = (LoopOperation) o;
ops = currentLoop.getList();
@@ -541,9 +603,9 @@ public class CoreDocument {
return ops;
}
- private HashMap<Integer, Component> mComponentMap = new HashMap<Integer, Component>();
+ @NonNull private HashMap<Integer, Component> mComponentMap = new HashMap<Integer, Component>();
- private void registerVariables(RemoteContext context, ArrayList<Operation> list) {
+ private void registerVariables(RemoteContext context, @NonNull ArrayList<Operation> list) {
for (Operation op : list) {
if (op instanceof VariableSupport) {
((VariableSupport) op).updateVariables(context);
@@ -578,7 +640,7 @@ public class CoreDocument {
* Called when an initialization is needed, allowing the document to eg load resources / cache
* them.
*/
- public void initializeContext(RemoteContext context) {
+ public void initializeContext(@NonNull RemoteContext context) {
mRemoteComposeState.reset();
mRemoteComposeState.setContext(context);
mClickAreas.clear();
@@ -651,6 +713,15 @@ public class CoreDocument {
}
/**
+ * Called by commands to listen to touch events
+ *
+ * @param listener
+ */
+ public void addTouchListener(TouchListener listener) {
+ mTouchListeners.add(listener);
+ }
+
+ /**
* Add a click listener. This will get called when a click is detected on the document
*
* @param callback called when a click area has been hit, passing the click are id and metadata.
@@ -664,6 +735,7 @@ public class CoreDocument {
*
* @return set of click listeners
*/
+ @NonNull
public HashSet<CoreDocument.ClickCallbacks> getClickListeners() {
return mClickListeners;
}
@@ -700,12 +772,98 @@ public class CoreDocument {
}
/** Warn click listeners when a click area is activated */
- private void warnClickListeners(ClickAreaRepresentation clickArea) {
+ private void warnClickListeners(@NonNull ClickAreaRepresentation clickArea) {
for (ClickCallbacks listener : mClickListeners) {
listener.click(clickArea.mId, clickArea.mMetadata);
}
}
+ /**
+ * Returns true if the document has touch listeners
+ *
+ * @return true if the document needs to react to touch events
+ */
+ public boolean hasTouchListener() {
+ boolean hasComponentsTouchListeners =
+ mRootLayoutComponent != null && mRootLayoutComponent.hasTouchListeners();
+ return hasComponentsTouchListeners || !mTouchListeners.isEmpty();
+ }
+
+ // TODO support velocity estimate support, support regions
+ /**
+ * Support touch drag events on commands supporting touch
+ *
+ * @param x position of touch
+ * @param y position of touch
+ */
+ public boolean touchDrag(RemoteContext context, float x, float y) {
+ context.loadFloat(RemoteContext.ID_TOUCH_POS_X, x);
+ context.loadFloat(RemoteContext.ID_TOUCH_POS_Y, y);
+ for (TouchListener clickArea : mTouchListeners) {
+ clickArea.touchDrag(context, x, y);
+ }
+ if (!mTouchListeners.isEmpty()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Support touch down events on commands supporting touch
+ *
+ * @param x position of touch
+ * @param y position of touch
+ */
+ public void touchDown(RemoteContext context, float x, float y) {
+ context.loadFloat(RemoteContext.ID_TOUCH_POS_X, x);
+ context.loadFloat(RemoteContext.ID_TOUCH_POS_Y, y);
+ for (TouchListener clickArea : mTouchListeners) {
+ clickArea.touchDown(context, x, y);
+ }
+ if (mRootLayoutComponent != null) {
+ mRootLayoutComponent.onTouchDown(context, this, x, y);
+ }
+ mRepaintNext = 1;
+ }
+
+ /**
+ * Support touch up events on commands supporting touch
+ *
+ * @param x position of touch
+ * @param y position of touch
+ */
+ public void touchUp(RemoteContext context, float x, float y, float dx, float dy) {
+ context.loadFloat(RemoteContext.ID_TOUCH_POS_X, x);
+ context.loadFloat(RemoteContext.ID_TOUCH_POS_Y, y);
+ for (TouchListener clickArea : mTouchListeners) {
+ clickArea.touchUp(context, x, y, dx, dy);
+ }
+ if (mRootLayoutComponent != null) {
+ for (Component component : mAppliedTouchOperations) {
+ component.onTouchUp(context, this, x, y, true);
+ }
+ mAppliedTouchOperations.clear();
+ }
+ mRepaintNext = 1;
+ }
+
+ /**
+ * Support touch cancel events on commands supporting touch
+ *
+ * @param x position of touch
+ * @param y position of touch
+ */
+ public void touchCancel(RemoteContext context, float x, float y, float dx, float dy) {
+ if (mRootLayoutComponent != null) {
+ for (Component component : mAppliedTouchOperations) {
+ component.onTouchCancel(context, this, x, y, true);
+ }
+ mAppliedTouchOperations.clear();
+ }
+ mRepaintNext = 1;
+ }
+
+ @NonNull
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -721,12 +879,22 @@ public class CoreDocument {
*
* @return array of named colors or null
*/
+ @Nullable
public String[] getNamedColors() {
+ return getNamedVariables(NamedVariable.COLOR_TYPE);
+ }
+
+ /**
+ * Gets the names of all named Variables.
+ *
+ * @return array of named variables or null
+ */
+ public String[] getNamedVariables(int type) {
int count = 0;
for (Operation op : mOperations) {
if (op instanceof NamedVariable) {
NamedVariable n = (NamedVariable) op;
- if (n.mVarType == NamedVariable.COLOR_TYPE) {
+ if (n.mVarType == type) {
count++;
}
}
@@ -739,7 +907,7 @@ public class CoreDocument {
for (Operation op : mOperations) {
if (op instanceof NamedVariable) {
NamedVariable n = (NamedVariable) op;
- if (n.mVarType == NamedVariable.COLOR_TYPE) {
+ if (n.mVarType == type) {
ret[i++] = n.mVarName;
}
}
@@ -770,10 +938,9 @@ public class CoreDocument {
* @param context the provided PaintContext
* @param theme the theme we want to use for this document.
*/
- public void paint(RemoteContext context, int theme) {
+ public void paint(@NonNull RemoteContext context, int theme) {
context.getPaintContext().clearNeedsRepaint();
context.mMode = RemoteContext.ContextMode.UNSET;
-
// current theme starts as UNSPECIFIED, until a Theme setter
// operation gets executed and modify it.
context.setTheme(Theme.UNSPECIFIED);
@@ -807,6 +974,7 @@ public class CoreDocument {
}
// TODO -- this should be specifically about applying animation, not paint
mRootLayoutComponent.paint(context.getPaintContext());
+ context.mPaintContext.reset();
// TODO -- should be able to remove this
mRootLayoutComponent.updateVariables(context);
if (DEBUG) {
@@ -843,6 +1011,7 @@ public class CoreDocument {
}
}
+ @NonNull
public String[] getStats() {
ArrayList<String> ret = new ArrayList<>();
WireBuffer buffer = new WireBuffer();
@@ -875,7 +1044,7 @@ public class CoreDocument {
return ret.toArray(new String[0]);
}
- private int sizeOfComponent(Operation com, WireBuffer tmp) {
+ private int sizeOfComponent(@NonNull Operation com, @NonNull WireBuffer tmp) {
tmp.reset(100);
com.write(tmp);
int size = tmp.getSize();
@@ -883,7 +1052,8 @@ public class CoreDocument {
return size;
}
- private int addChildren(Component base, HashMap<String, int[]> map, WireBuffer tmp) {
+ private int addChildren(
+ @NonNull Component base, @NonNull HashMap<String, int[]> map, @NonNull WireBuffer tmp) {
int count = base.mList.size();
for (Operation mOperation : base.mList) {
Class<? extends Operation> c = mOperation.getClass();
@@ -903,6 +1073,7 @@ public class CoreDocument {
return count;
}
+ @NonNull
public String toNestedString() {
StringBuilder ret = new StringBuilder();
for (Operation mOperation : mOperations) {
@@ -915,7 +1086,8 @@ public class CoreDocument {
return ret.toString();
}
- private void toNestedString(Component base, StringBuilder ret, String indent) {
+ private void toNestedString(
+ @NonNull Component base, @NonNull StringBuilder ret, String indent) {
for (Operation mOperation : base.mList) {
ret.append(mOperation.toString());
ret.append("\n");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
index 9f565a2915fb..f1885f942ee1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.Nullable;
+
/** Base interface for RemoteCompose operations */
public interface Operation {
@@ -29,5 +31,6 @@ public interface Operation {
void apply(RemoteContext context);
/** Debug utility to display an operation + indentation */
+ @Nullable
String deepToString(String indent);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index acebe0761b79..53c45fac826c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
@@ -65,15 +67,19 @@ import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
-import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.OperationsListEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchCancelModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchDownModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchUpModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
@@ -85,15 +91,19 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostNamedActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.OffsetModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueFloatChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerExpressionChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueStringChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
@@ -165,6 +175,7 @@ public class Operations {
public static final int DATA_MAP_LOOKUP = 154;
public static final int TEXT_MEASURE = 155;
public static final int TEXT_LENGTH = 156;
+ public static final int TOUCH_EXPRESSION = 157;
///////////////////////////////////////// ======================
@@ -194,8 +205,16 @@ public class Operations {
public static final int MODIFIER_ROUNDED_CLIP_RECT = 54;
public static final int MODIFIER_CLICK = 59;
+ public static final int MODIFIER_TOUCH_DOWN = 219;
+ public static final int MODIFIER_TOUCH_UP = 220;
+ public static final int MODIFIER_TOUCH_CANCEL = 225;
+
+ public static final int OPERATIONS_LIST_END = 214;
+
+ public static final int MODIFIER_OFFSET = 221;
+ public static final int MODIFIER_ZINDEX = 223;
+ public static final int MODIFIER_GRAPHICS_LAYER = 224;
- public static final int MODIFIER_CLICK_END = 214;
public static final int LOOP_START = 215;
public static final int LOOP_END = 216;
@@ -206,12 +225,13 @@ public class Operations {
public static final int VALUE_INTEGER_CHANGE_ACTION = 212;
public static final int VALUE_STRING_CHANGE_ACTION = 213;
public static final int VALUE_INTEGER_EXPRESSION_CHANGE_ACTION = 218;
+ public static final int VALUE_FLOAT_CHANGE_ACTION = 222;
public static final int ANIMATION_SPEC = 14;
public static final int COMPONENT_VALUE = 150;
- public static UniqueIntMap<CompanionOperation> map = new UniqueIntMap<>();
+ @NonNull public static UniqueIntMap<CompanionOperation> map = new UniqueIntMap<>();
static class UniqueIntMap<T> extends IntMap<T> {
@Override
@@ -289,8 +309,16 @@ public class Operations {
map.put(MODIFIER_ROUNDED_CLIP_RECT, RoundedClipRectModifierOperation::read);
map.put(MODIFIER_CLIP_RECT, ClipRectModifierOperation::read);
map.put(MODIFIER_CLICK, ClickModifierOperation::read);
- map.put(MODIFIER_CLICK_END, ClickModifierEnd::read);
+ map.put(MODIFIER_TOUCH_DOWN, TouchDownModifierOperation::read);
+ map.put(MODIFIER_TOUCH_UP, TouchUpModifierOperation::read);
+ map.put(MODIFIER_TOUCH_CANCEL, TouchCancelModifierOperation::read);
map.put(MODIFIER_VISIBILITY, ComponentVisibilityOperation::read);
+ map.put(MODIFIER_OFFSET, OffsetModifierOperation::read);
+ map.put(MODIFIER_ZINDEX, ZIndexModifierOperation::read);
+ map.put(MODIFIER_GRAPHICS_LAYER, GraphicsLayerModifierOperation::read);
+
+ map.put(OPERATIONS_LIST_END, OperationsListEnd::read);
+
map.put(HOST_ACTION, HostActionOperation::read);
map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
map.put(VALUE_INTEGER_CHANGE_ACTION, ValueIntegerChangeActionOperation::read);
@@ -298,6 +326,7 @@ public class Operations {
VALUE_INTEGER_EXPRESSION_CHANGE_ACTION,
ValueIntegerExpressionChangeActionOperation::read);
map.put(VALUE_STRING_CHANGE_ACTION, ValueStringChangeActionOperation::read);
+ map.put(VALUE_FLOAT_CHANGE_ACTION, ValueFloatChangeActionOperation::read);
map.put(LAYOUT_ROOT, RootLayoutComponent::read);
map.put(LAYOUT_CONTENT, LayoutComponentContent::read);
@@ -315,5 +344,6 @@ public class Operations {
map.put(DATA_MAP_LOOKUP, DataMapLookup::read);
map.put(TEXT_MEASURE, TextMeasure::read);
map.put(TEXT_LENGTH, TextLength::read);
+ map.put(TOUCH_EXPRESSION, TouchExpression::read);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 13d6f783a586..1a71afe068bd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -271,4 +271,24 @@ public abstract class PaintContext {
public void needsRepaint() {
mNeedsRepaint = true;
}
+
+ public abstract void startGraphicsLayer(int w, int h);
+
+ public abstract void setGraphicsLayer(
+ float scaleX,
+ float scaleY,
+ float rotationX,
+ float rotationY,
+ float rotationZ,
+ float shadowElevation,
+ float transformOriginX,
+ float transformOriginY,
+ float alpha,
+ int renderEffectId);
+
+ public abstract void endGraphicsLayer();
+
+ public boolean isVisualDebug() {
+ return mContext.isVisualDebug();
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
index 9b7b50f775b0..049e47744ce8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+
/**
* PaintOperation interface, used for operations aimed at painting (while any operation _can_ paint,
* this make it a little more explicit)
@@ -22,7 +24,7 @@ package com.android.internal.widget.remotecompose.core;
public abstract class PaintOperation implements Operation {
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
if (context.getMode() == RemoteContext.ContextMode.PAINT) {
PaintContext paintContext = context.getPaintContext();
if (paintContext != null) {
@@ -31,6 +33,7 @@ public abstract class PaintOperation implements Operation {
}
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Platform.java b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
index 6725e7e6ac2b..7fbcfae54bcb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Platform.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
@@ -15,16 +15,30 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.Nullable;
+
/** Services that are needed to be provided by the platform during encoding. */
public interface Platform {
+ @Nullable
byte[] imageToByteArray(Object image);
int getImageWidth(Object image);
int getImageHeight(Object image);
+ @Nullable
float[] pathToFloatArray(Object path);
+ enum LogCategory {
+ DEBUG,
+ INFO,
+ WARN,
+ ERROR,
+ TODO,
+ }
+
+ void log(LogCategory category, String message);
+
Platform None =
new Platform() {
@Override
@@ -46,5 +60,8 @@ public interface Platform {
public float[] pathToFloatArray(Object path) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void log(LogCategory category, String message) {}
};
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 5b5adc28a676..7d9439dd186b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
@@ -64,6 +67,7 @@ import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
@@ -81,8 +85,11 @@ import com.android.internal.widget.remotecompose.core.operations.layout.managers
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.OffsetModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
@@ -111,7 +118,7 @@ public class RemoteComposeBuffer {
public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE;
public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC;
WireBuffer mBuffer = new WireBuffer();
- Platform mPlatform = null;
+ @Nullable Platform mPlatform = null;
RemoteComposeState mRemoteComposeState;
private static final boolean DEBUG = false;
@@ -143,6 +150,7 @@ public class RemoteComposeBuffer {
return mLastComponentId;
}
+ @Nullable
public Platform getPlatform() {
return mPlatform;
}
@@ -172,7 +180,11 @@ public class RemoteComposeBuffer {
* @param capabilities bitmask indicating needed capabilities (unused for now)
*/
public void header(
- int width, int height, String contentDescription, float density, long capabilities) {
+ int width,
+ int height,
+ @Nullable String contentDescription,
+ float density,
+ long capabilities) {
Header.apply(mBuffer, width, height, density, capabilities);
int contentDescriptionId = 0;
if (contentDescription != null) {
@@ -219,7 +231,7 @@ public class RemoteComposeBuffer {
int dstTop,
int dstRight,
int dstBottom,
- String contentDescription) {
+ @Nullable String contentDescription) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -267,7 +279,7 @@ public class RemoteComposeBuffer {
*
* @param text the string to inject in the buffer
*/
- public int addText(String text) {
+ public int addText(@NonNull String text) {
int id = mRemoteComposeState.dataGetId(text);
if (id == -1) {
id = mRemoteComposeState.cacheData(text);
@@ -289,12 +301,12 @@ public class RemoteComposeBuffer {
*/
public void addClickArea(
int id,
- String contentDescription,
+ @Nullable String contentDescription,
float left,
float top,
float right,
float bottom,
- String metadata) {
+ @Nullable String metadata) {
int contentDescriptionId = 0;
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
@@ -380,7 +392,7 @@ public class RemoteComposeBuffer {
float top,
float right,
float bottom,
- String contentDescription) {
+ @Nullable String contentDescription) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -411,7 +423,7 @@ public class RemoteComposeBuffer {
float top,
float right,
float bottom,
- String contentDescription) {
+ @Nullable String contentDescription) {
int contentDescriptionId = 0;
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
@@ -445,7 +457,7 @@ public class RemoteComposeBuffer {
float dstBottom,
int scaleType,
float scaleFactor,
- String contentDescription) {
+ @Nullable String contentDescription) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -500,7 +512,7 @@ public class RemoteComposeBuffer {
* @param image drawScaledBitmap
* @return id of the image useful with
*/
- public int addBitmap(Object image, String name) {
+ public int addBitmap(Object image, @NonNull String name) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -521,7 +533,7 @@ public class RemoteComposeBuffer {
* @param id of the Bitmap
* @param name Name of the color
*/
- public void setBitmapName(int id, String name) {
+ public void setBitmapName(int id, @NonNull String name) {
NamedVariable.apply(mBuffer, id, NamedVariable.IMAGE_TYPE, name);
}
@@ -551,7 +563,7 @@ public class RemoteComposeBuffer {
float dstBottom,
int scaleType,
float scaleFactor,
- String contentDescription) {
+ @Nullable String contentDescription) {
int contentDescriptionId = 0;
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
@@ -669,7 +681,7 @@ public class RemoteComposeBuffer {
* @param hOffset The distance along the path to add to the text's starting position
* @param vOffset The distance above(-) or below(+) the path to position the text
*/
- public void addDrawTextOnPath(String text, Object path, float hOffset, float vOffset) {
+ public void addDrawTextOnPath(@NonNull String text, Object path, float hOffset, float vOffset) {
int pathId = mRemoteComposeState.dataGetId(path);
if (pathId == -1) { // never been seen before
pathId = addPathData(path);
@@ -692,7 +704,7 @@ public class RemoteComposeBuffer {
* @param rtl Draw RTTL
*/
public void addDrawTextRun(
- String text,
+ @NonNull String text,
int start,
int end,
int contextStart,
@@ -749,7 +761,8 @@ public class RemoteComposeBuffer {
* @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
* @param flags 1 = RTL
*/
- public void drawTextAnchored(String text, float x, float y, float panX, float panY, int flags) {
+ public void drawTextAnchored(
+ @NonNull String text, float x, float y, float panX, float panY, int flags) {
int textId = addText(text);
DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
}
@@ -760,7 +773,7 @@ public class RemoteComposeBuffer {
* @param text
* @return
*/
- public int createTextId(String text) {
+ public int createTextId(@NonNull String text) {
return addText(text);
}
@@ -891,7 +904,7 @@ public class RemoteComposeBuffer {
*
* @param paint
*/
- public void addPaint(PaintBundle paint) {
+ public void addPaint(@NonNull PaintBundle paint) {
PaintData.apply(mBuffer, paint);
}
@@ -912,7 +925,8 @@ public class RemoteComposeBuffer {
}
}
- public static void readNextOperation(WireBuffer buffer, ArrayList<Operation> operations) {
+ public static void readNextOperation(
+ @NonNull WireBuffer buffer, ArrayList<Operation> operations) {
int opId = buffer.readByte();
if (DEBUG) {
Utils.log(">> " + opId);
@@ -924,6 +938,7 @@ public class RemoteComposeBuffer {
operation.read(buffer, operations);
}
+ @NonNull
RemoteComposeBuffer copy() {
ArrayList<Operation> operations = new ArrayList<>();
inflateFromBuffer(operations);
@@ -935,33 +950,38 @@ public class RemoteComposeBuffer {
Theme.apply(mBuffer, theme);
}
+ @NonNull
static String version() {
return "v1.0";
}
- public static RemoteComposeBuffer fromFile(String path, RemoteComposeState remoteComposeState)
- throws IOException {
+ @NonNull
+ public static RemoteComposeBuffer fromFile(
+ @NonNull String path, RemoteComposeState remoteComposeState) throws IOException {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(new File(path), buffer);
return buffer;
}
- public RemoteComposeBuffer fromFile(File file, RemoteComposeState remoteComposeState)
+ @NonNull
+ public RemoteComposeBuffer fromFile(@NonNull File file, RemoteComposeState remoteComposeState)
throws IOException {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(file, buffer);
return buffer;
}
+ @NonNull
public static RemoteComposeBuffer fromInputStream(
- InputStream inputStream, RemoteComposeState remoteComposeState) {
+ @NonNull InputStream inputStream, RemoteComposeState remoteComposeState) {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(inputStream, buffer);
return buffer;
}
+ @NonNull
RemoteComposeBuffer copyFromOperations(
- ArrayList<Operation> operations, RemoteComposeBuffer buffer) {
+ @NonNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer) {
for (Operation operation : operations) {
operation.write(buffer.mBuffer);
@@ -975,7 +995,7 @@ public class RemoteComposeBuffer {
* @param buffer a RemoteComposeBuffer
* @param file a target file
*/
- public void write(RemoteComposeBuffer buffer, File file) {
+ public void write(@NonNull RemoteComposeBuffer buffer, @NonNull File file) {
try {
FileOutputStream fd = new FileOutputStream(file);
fd.write(buffer.mBuffer.getBuffer(), 0, buffer.mBuffer.getSize());
@@ -986,12 +1006,12 @@ public class RemoteComposeBuffer {
}
}
- static void read(File file, RemoteComposeBuffer buffer) throws IOException {
+ static void read(@NonNull File file, @NonNull RemoteComposeBuffer buffer) throws IOException {
FileInputStream fd = new FileInputStream(file);
read(fd, buffer);
}
- public static void read(InputStream fd, RemoteComposeBuffer buffer) {
+ public static void read(@NonNull InputStream fd, @NonNull RemoteComposeBuffer buffer) {
try {
byte[] bytes = readAllBytes(fd);
buffer.reset(bytes.length);
@@ -1002,7 +1022,7 @@ public class RemoteComposeBuffer {
}
}
- private static byte[] readAllBytes(InputStream is) throws IOException {
+ private static byte[] readAllBytes(@NonNull InputStream is) throws IOException {
byte[] buff = new byte[32 * 1024]; // moderate size buff to start
int red = 0;
while (true) {
@@ -1176,20 +1196,59 @@ public class RemoteComposeBuffer {
* @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
* @return NaN id of the result of the calculation
*/
- public float addAnimatedFloat(float... value) {
+ public float addAnimatedFloat(@NonNull float... value) {
int id = mRemoteComposeState.cacheData(value);
FloatExpression.apply(mBuffer, id, value, null);
return Utils.asNan(id);
}
/**
+ * Add a touch handle system
+ *
+ * @param value the default value
+ * @param min the minimum value
+ * @param max the maximum value
+ * @param velocityId the id for the velocity TODO support in v2
+ * @param exp The Float Expression
+ * @param touchMode the touch up handling behaviour
+ * @param touchSpec the touch up handling parameters
+ * @param easingSpec the easing parameter TODO support in v2
+ * @return id of the variable to be used controlled by touch handling
+ */
+ public float addTouchExpression(
+ float value,
+ float min,
+ float max,
+ float velocityId,
+ int touchEffects,
+ float[] exp,
+ int touchMode,
+ float[] touchSpec,
+ float[] easingSpec) {
+ int id = mRemoteComposeState.nextId();
+ TouchExpression.apply(
+ mBuffer,
+ id,
+ value,
+ min,
+ max,
+ velocityId,
+ touchEffects,
+ exp,
+ touchMode,
+ touchSpec,
+ easingSpec);
+ return Utils.asNan(id);
+ }
+
+ /**
* Add a float that is a computation based on variables. see packAnimation
*
* @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
* @param animation Array of floats that represents animation
* @return NaN id of the result of the calculation
*/
- public float addAnimatedFloat(float[] value, float[] animation) {
+ public float addAnimatedFloat(@NonNull float[] value, float[] animation) {
int id = mRemoteComposeState.cacheData(value);
FloatExpression.apply(mBuffer, id, value, animation);
return Utils.asNan(id);
@@ -1228,7 +1287,7 @@ public class RemoteComposeBuffer {
* @param values
* @return the id of the array, encoded as a float NaN
*/
- public float addFloatArray(float[] values) {
+ public float addFloatArray(@NonNull float[] values) {
int id = mRemoteComposeState.cacheData(values, NanMap.TYPE_ARRAY);
DataListFloat.apply(mBuffer, id, values);
return Utils.asNan(id);
@@ -1240,7 +1299,7 @@ public class RemoteComposeBuffer {
* @param values array of floats to be individually stored
* @return id of the list
*/
- public float addFloatList(float[] values) {
+ public float addFloatList(@NonNull float[] values) {
int[] listId = new int[values.length];
for (int i = 0; i < listId.length; i++) {
listId[i] = mRemoteComposeState.cacheFloat(values[i]);
@@ -1255,7 +1314,7 @@ public class RemoteComposeBuffer {
* @param listId array id to be stored
* @return id of the list
*/
- public float addList(int[] listId) {
+ public float addList(@NonNull int[] listId) {
int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
DataListIds.apply(mBuffer, id, listId);
return Utils.asNan(id);
@@ -1268,7 +1327,7 @@ public class RemoteComposeBuffer {
* @param values
* @return the id of the map, encoded as a float NaN
*/
- public float addFloatMap(String[] keys, float[] values) {
+ public float addFloatMap(@NonNull String[] keys, @NonNull float[] values) {
int[] listId = new int[values.length];
byte[] type = new byte[values.length];
for (int i = 0; i < listId.length; i++) {
@@ -1286,7 +1345,7 @@ public class RemoteComposeBuffer {
* @param listId
* @return the id of the map, encoded as a float NaN
*/
- public int addMap(String[] keys, byte[] types, int[] listId) {
+ public int addMap(@NonNull String[] keys, byte[] types, int[] listId) {
int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
DataMapIds.apply(mBuffer, id, keys, types, listId);
return id;
@@ -1331,7 +1390,7 @@ public class RemoteComposeBuffer {
* @param value array of values to calculate maximum 32
* @return the id as an integer
*/
- public int addIntegerExpression(int mask, int[] value) {
+ public int addIntegerExpression(int mask, @NonNull int[] value) {
int id = mRemoteComposeState.cacheData(value);
IntegerExpression.apply(mBuffer, id, mask, value);
return id;
@@ -1474,7 +1533,7 @@ public class RemoteComposeBuffer {
* @param id of the color
* @param name Name of the color
*/
- public void setColorName(int id, String name) {
+ public void setColorName(int id, @NonNull String name) {
NamedVariable.apply(mBuffer, id, NamedVariable.COLOR_TYPE, name);
}
@@ -1484,7 +1543,7 @@ public class RemoteComposeBuffer {
* @param id of the string
* @param name name of the string
*/
- public void setStringName(int id, String name) {
+ public void setStringName(int id, @NonNull String name) {
NamedVariable.apply(mBuffer, id, NamedVariable.STRING_TYPE, name);
}
@@ -1576,6 +1635,72 @@ public class RemoteComposeBuffer {
}
/**
+ * Add an offset modifier
+ *
+ * @param x x offset
+ * @param y y offset
+ */
+ public void addModifierOffset(float x, float y) {
+ OffsetModifierOperation.apply(mBuffer, x, y);
+ }
+
+ /**
+ * Add a zIndex modifier
+ *
+ * @param value z-Index value
+ */
+ public void addModifierZIndex(float value) {
+ ZIndexModifierOperation.apply(mBuffer, value);
+ }
+
+ /**
+ * Add a graphics layer
+ *
+ * @param scaleX
+ * @param scaleY
+ * @param rotationX
+ * @param rotationY
+ * @param rotationZ
+ * @param shadowElevation
+ * @param transformOriginX
+ * @param transformOriginY
+ */
+ public void addModifierGraphicsLayer(
+ float scaleX,
+ float scaleY,
+ float rotationX,
+ float rotationY,
+ float rotationZ,
+ float shadowElevation,
+ float transformOriginX,
+ float transformOriginY,
+ float alpha,
+ float cameraDistance,
+ int blendMode,
+ int spotShadowColorId,
+ int ambientShadowColorId,
+ int colorFilterId,
+ int renderEffectId) {
+ GraphicsLayerModifierOperation.apply(
+ mBuffer,
+ scaleX,
+ scaleY,
+ rotationX,
+ rotationY,
+ rotationZ,
+ shadowElevation,
+ transformOriginX,
+ transformOriginY,
+ alpha,
+ cameraDistance,
+ blendMode,
+ spotShadowColorId,
+ ambientShadowColorId,
+ colorFilterId,
+ renderEffectId);
+ }
+
+ /**
* Sets the clip based on rounded clip rect
*
* @param topStart
@@ -1721,7 +1846,8 @@ public class RemoteComposeBuffer {
float fontSize,
int fontStyle,
float fontWeight,
- String fontFamily) {
+ @Nullable String fontFamily,
+ int textAlign) {
mLastComponentId = getComponentId(componentId);
int fontFamilyId = -1;
if (fontFamily != null) {
@@ -1736,6 +1862,11 @@ public class RemoteComposeBuffer {
fontSize,
fontStyle,
fontWeight,
- fontFamilyId);
+ fontFamilyId,
+ textAlign);
+ }
+
+ public int createID(int type) {
+ return mRemoteComposeState.nextId(type);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 51445f2ff31d..303932814909 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
@@ -50,10 +53,11 @@ public class RemoteComposeState implements CollectionsAccess {
private final boolean[] mDataOverride = new boolean[MAX_DATA];
private final boolean[] mIntegerOverride = new boolean[MAX_DATA];
+ private final boolean[] mFloatOverride = new boolean[MAX_DATA];
private int mNextId = START_ID;
- private int[] mIdMaps = new int[] {START_ID, NanMap.START_VAR, NanMap.START_ARRAY};
- private RemoteContext mRemoteContext = null;
+ @NonNull private int[] mIdMaps = new int[] {START_ID, NanMap.START_VAR, NanMap.START_ARRAY};
+ @Nullable private RemoteContext mRemoteContext = null;
/**
* Get Object based on id. The system will cache things like bitmaps Paths etc. They can be
@@ -62,6 +66,7 @@ public class RemoteComposeState implements CollectionsAccess {
* @param id
* @return
*/
+ @Nullable
public Object getFromId(int id) {
return mIntDataMap.get(id);
}
@@ -158,10 +163,28 @@ public class RemoteComposeState implements CollectionsAccess {
/** Insert an float item in the cache */
public void updateFloat(int id, float value) {
+ if (!mFloatOverride[id]) {
+ float previous = mFloatMap.get(id);
+ if (previous != value) {
+ mFloatMap.put(id, value);
+ mIntegerMap.put(id, (int) value);
+ updateListeners(id);
+ }
+ }
+ }
+
+ /**
+ * Adds a float Override.
+ *
+ * @param id
+ * @param value the new value
+ */
+ public void overrideFloat(int id, float value) {
float previous = mFloatMap.get(id);
if (previous != value) {
mFloatMap.put(id, value);
mIntegerMap.put(id, (int) value);
+ mFloatOverride[id] = true;
updateListeners(id);
}
}
@@ -294,6 +317,16 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
+ * Clear the float override
+ *
+ * @param id the float id to clear
+ */
+ public void clearFloatOverride(int id) {
+ mFloatOverride[id] = false;
+ updateListeners(id);
+ }
+
+ /**
* Method to determine if a cached value has been written to the documents WireBuffer based on
* its id.
*/
@@ -322,7 +355,8 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
- * Get the next available id
+ * Get the next available id 0 is normal (float,int,String,color) 1 is VARIABLES 2 is
+ * collections
*
* @return
*/
@@ -342,8 +376,8 @@ public class RemoteComposeState implements CollectionsAccess {
mNextId = id;
}
- IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>();
- ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>();
+ @NonNull IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>();
+ @NonNull ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>();
private void add(int id, VariableSupport variableSupport) {
ArrayList<VariableSupport> v = mVarListeners.get(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 1066e7d9f617..23cc5b89d916 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.Theme;
@@ -40,10 +42,12 @@ public abstract class RemoteContext {
protected CoreDocument mDocument;
public RemoteComposeState mRemoteComposeState;
long mStart = System.nanoTime(); // todo This should be set at a hi level
- protected PaintContext mPaintContext = null;
+ @Nullable protected PaintContext mPaintContext = null;
+ protected float mDensity = 2.75f;
+
ContextMode mMode = ContextMode.UNSET;
- boolean mDebug = false;
+ int mDebug = 0;
private int mTheme = Theme.UNSPECIFIED;
@@ -56,6 +60,14 @@ public abstract class RemoteContext {
public Component lastComponent;
public long currentTime = 0L;
+ public float getDensity() {
+ return mDensity;
+ }
+
+ public void setDensity(float density) {
+ mDensity = density;
+ }
+
public boolean isAnimationEnabled() {
return mAnimate;
}
@@ -173,12 +185,22 @@ public abstract class RemoteContext {
public abstract void runAction(int id, String metadata);
- public abstract void runNamedAction(int textId);
+ // TODO: we might add an interface to group all valid parameter types
+ public abstract void runNamedAction(int textId, Object value);
public abstract void putObject(int mId, Object command);
public abstract Object getObject(int mId);
+ public void addTouchListener(TouchListener touchExpression) {}
+
+ /**
+ * Vibrate the device
+ *
+ * @param type 0 = none, 1-21 ,see HapticFeedbackConstants
+ */
+ public abstract void hapticEffect(int type);
+
/**
* The context can be used in a few different mode, allowing operations to skip being executed:
* - UNSET : all operations will get executed - DATA : only operations dealing with DATA (eg
@@ -206,6 +228,7 @@ public abstract class RemoteContext {
this.mMode = mode;
}
+ @Nullable
public PaintContext getPaintContext() {
return mPaintContext;
}
@@ -219,10 +242,14 @@ public abstract class RemoteContext {
}
public boolean isDebug() {
- return mDebug;
+ return mDebug == 1;
}
- public void setDebug(boolean debug) {
+ public boolean isVisualDebug() {
+ return mDebug == 2;
+ }
+
+ public void setDebug(int debug) {
this.mDebug = debug;
}
@@ -314,6 +341,14 @@ public abstract class RemoteContext {
public abstract void loadFloat(int id, float value);
/**
+ * Override an existing float value
+ *
+ * @param id
+ * @param value
+ */
+ public abstract void overrideFloat(int id, float value);
+
+ /**
* Load a integer
*
* @param id id of the integer
@@ -321,8 +356,20 @@ public abstract class RemoteContext {
*/
public abstract void loadInteger(int id, int value);
+ /**
+ * Override an existing int value
+ *
+ * @param id
+ * @param value
+ */
public abstract void overrideInteger(int id, int value);
+ /**
+ * Override an existing text value
+ *
+ * @param id
+ * @param valueId
+ */
public abstract void overrideText(int id, int valueId);
/**
@@ -400,6 +447,25 @@ public abstract class RemoteContext {
public static final int ID_OFFSET_TO_UTC = 10;
public static final int ID_WEEK_DAY = 11;
public static final int ID_DAY_OF_MONTH = 12;
+ public static final int ID_TOUCH_POS_X = 13;
+ public static final int ID_TOUCH_POS_Y = 14;
+
+ public static final int ID_TOUCH_VEL_X = 15;
+ public static final int ID_TOUCH_VEL_Y = 16;
+
+ public static final int ID_ACCELERATION_X = 17;
+ public static final int ID_ACCELERATION_Y = 18;
+ public static final int ID_ACCELERATION_Z = 19;
+
+ public static final int ID_GYRO_ROT_X = 20;
+ public static final int ID_GYRO_ROT_Y = 21;
+ public static final int ID_GYRO_ROT_Z = 22;
+
+ public static final int ID_MAGNETIC_X = 23;
+ public static final int ID_MAGNETIC_Y = 24;
+ public static final int ID_MAGNETIC_Z = 25;
+
+ public static final int ID_LIGHT = 26;
/** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
@@ -426,9 +492,52 @@ public abstract class RemoteContext {
public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT);
public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH);
public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT);
- // ID_OFFSET_TO_UTC is the offset from UTC in sec (typically / 3600f)
+
+ /** ID_OFFSET_TO_UTC is the offset from UTC in sec (typically / 3600f) */
public static final float FLOAT_OFFSET_TO_UTC = Utils.asNan(ID_OFFSET_TO_UTC);
+ /** TOUCH_POS_X is the x position of the touch */
+ public static final float FLOAT_TOUCH_POS_X = Utils.asNan(ID_TOUCH_POS_X);
+
+ /** TOUCH_POS_Y is the y position of the touch */
+ public static final float FLOAT_TOUCH_POS_Y = Utils.asNan(ID_TOUCH_POS_Y);
+
+ /** TOUCH_VEL_X is the x velocity of the touch */
+ public static final float FLOAT_TOUCH_VEL_X = Utils.asNan(ID_TOUCH_VEL_X);
+
+ /** TOUCH_VEL_Y is the x velocity of the touch */
+ public static final float FLOAT_TOUCH_VEL_Y = Utils.asNan(ID_TOUCH_VEL_Y);
+
+ /** X acceleration sensor value in M/s^2 */
+ public static final float FLOAT_ACCELERATION_X = Utils.asNan(ID_ACCELERATION_X);
+
+ /** Y acceleration sensor value in M/s^2 */
+ public static final float FLOAT_ACCELERATION_Y = Utils.asNan(ID_ACCELERATION_Y);
+
+ /** Z acceleration sensor value in M/s^2 */
+ public static final float FLOAT_ACCELERATION_Z = Utils.asNan(ID_ACCELERATION_Z);
+
+ /** X Gyroscope rotation rate sensor value in radians/second */
+ public static final float FLOAT_GYRO_ROT_X = Utils.asNan(ID_GYRO_ROT_X);
+
+ /** Y Gyroscope rotation rate sensor value in radians/second */
+ public static final float FLOAT_GYRO_ROT_Y = Utils.asNan(ID_GYRO_ROT_Y);
+
+ /** Z Gyroscope rotation rate sensor value in radians/second */
+ public static final float FLOAT_GYRO_ROT_Z = Utils.asNan(ID_GYRO_ROT_Z);
+
+ /** Ambient magnetic field in X. sensor value in micro-Tesla (uT) */
+ public static final float FLOAT_MAGNETIC_X = Utils.asNan(ID_MAGNETIC_X);
+
+ /** Ambient magnetic field in Y. sensor value in micro-Tesla (uT) */
+ public static final float FLOAT_MAGNETIC_Y = Utils.asNan(ID_MAGNETIC_Y);
+
+ /** Ambient magnetic field in Z. sensor value in micro-Tesla (uT) */
+ public static final float FLOAT_MAGNETIC_Z = Utils.asNan(ID_MAGNETIC_Z);
+
+ /** Ambient light level in SI lux */
+ public static final float FLOAT_LIGHT = Utils.asNan(ID_LIGHT);
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index fa0cf3f455c4..14aed2f0c173 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
@@ -27,7 +29,7 @@ public class TimeVariables {
*
* @param context
*/
- public void updateTime(RemoteContext context) {
+ public void updateTime(@NonNull RemoteContext context) {
LocalDateTime dateTime =
LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
// This define the time in the format
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java b/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java
new file mode 100644
index 000000000000..3dda678c2a3a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/TouchListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core;
+
+public interface TouchListener {
+ void touchDown(RemoteContext context, float x, float y);
+
+ void touchUp(RemoteContext context, float x, float y, float dx, float dy);
+
+ void touchDrag(RemoteContext context, float x, float y);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index c71b4901ca78..738e42baae0b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core;
+import android.annotation.NonNull;
+
import java.util.Arrays;
/** The base communication buffer capable of encoding and decoding various types */
@@ -184,11 +186,13 @@ public class WireBuffer {
return b;
}
+ @NonNull
public String readUTF8() {
byte[] stringBuffer = readBuffer();
return new String(stringBuffer);
}
+ @NonNull
public String readUTF8(int maxSize) {
byte[] stringBuffer = readBuffer(maxSize);
return new String(stringBuffer);
@@ -250,7 +254,7 @@ public class WireBuffer {
writeLong(Double.doubleToRawLongBits(value));
}
- public void writeBuffer(byte[] b) {
+ public void writeBuffer(@NonNull byte[] b) {
resize(b.length + 4);
writeInt(b.length);
for (int i = 0; i < b.length; i++) {
@@ -259,7 +263,7 @@ public class WireBuffer {
mSize += b.length;
}
- public void writeUTF8(String content) {
+ public void writeUTF8(@NonNull String content) {
byte[] buffer = content.getBytes();
writeBuffer(buffer);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
index c33ae244b923..5edecaa01f82 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.documentation;
+import android.annotation.NonNull;
+
import java.util.ArrayList;
public class DocumentedOperation {
@@ -40,12 +42,13 @@ public class DocumentedOperation {
boolean mWIP;
String mTextExamples;
- ArrayList<StringPair> mExamples = new ArrayList<>();
- ArrayList<OperationField> mFields = new ArrayList<>();
- String mVarSize = "";
+ @NonNull ArrayList<StringPair> mExamples = new ArrayList<>();
+ @NonNull ArrayList<OperationField> mFields = new ArrayList<>();
+ @NonNull String mVarSize = "";
int mExamplesWidth = 100;
int mExamplesHeight = 100;
+ @NonNull
public static String getType(int type) {
switch (type) {
case INT:
@@ -85,6 +88,7 @@ public class DocumentedOperation {
this(category, id, name, false);
}
+ @NonNull
public ArrayList<OperationField> getFields() {
return mFields;
}
@@ -105,6 +109,7 @@ public class DocumentedOperation {
return mWIP;
}
+ @NonNull
public String getVarSize() {
return mVarSize;
}
@@ -129,6 +134,7 @@ public class DocumentedOperation {
return mTextExamples;
}
+ @NonNull
public ArrayList<StringPair> getExamples() {
return mExamples;
}
@@ -141,16 +147,19 @@ public class DocumentedOperation {
return mExamplesHeight;
}
+ @NonNull
public DocumentedOperation field(int type, String name, String description) {
mFields.add(new OperationField(type, name, description));
return this;
}
+ @NonNull
public DocumentedOperation field(int type, String name, String varSize, String description) {
mFields.add(new OperationField(type, name, varSize, description));
return this;
}
+ @NonNull
public DocumentedOperation possibleValues(String name, int value) {
if (!mFields.isEmpty()) {
mFields.get(mFields.size() - 1).possibleValue(name, "" + value);
@@ -158,21 +167,25 @@ public class DocumentedOperation {
return this;
}
+ @NonNull
public DocumentedOperation description(String description) {
mDescription = description;
return this;
}
+ @NonNull
public DocumentedOperation examples(String examples) {
mTextExamples = examples;
return this;
}
+ @NonNull
public DocumentedOperation exampleImage(String name, String imagePath) {
mExamples.add(new StringPair(name, imagePath));
return this;
}
+ @NonNull
public DocumentedOperation examplesDimension(int width, int height) {
mExamplesWidth = width;
mExamplesHeight = height;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
index c77048391405..cbb5ca9821c3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
@@ -15,15 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.documentation;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import java.util.ArrayList;
public class OperationField {
int mType;
String mName;
String mDescription;
- String mVarSize = null;
+ @Nullable String mVarSize = null;
- ArrayList<StringPair> mPossibleValues = new ArrayList<>();
+ @NonNull ArrayList<StringPair> mPossibleValues = new ArrayList<>();
public OperationField(int type, String name, String description) {
mType = type;
@@ -50,6 +53,7 @@ public class OperationField {
return mDescription;
}
+ @NonNull
public ArrayList<StringPair> getPossibleValues() {
return mPossibleValues;
}
@@ -62,6 +66,7 @@ public class OperationField {
return !mPossibleValues.isEmpty();
}
+ @Nullable
public String getVarSize() {
return mVarSize;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 20ba8c313b47..8da0e184cbe7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -58,15 +60,17 @@ public class BitmapData implements Operation, SerializableToString {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mImageId, mImageWidth, mImageHeight, mBitmap);
}
+ @NonNull
@Override
public String toString() {
return "BITMAP DATA " + mImageId;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -75,7 +79,12 @@ public class BitmapData implements Operation, SerializableToString {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int imageId, int width, int height, byte[] bitmap) {
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int imageId,
+ int width,
+ int height,
+ @NonNull byte[] bitmap) {
buffer.start(OP_CODE);
buffer.writeInt(imageId);
buffer.writeInt(width);
@@ -83,7 +92,7 @@ public class BitmapData implements Operation, SerializableToString {
buffer.writeBuffer(bitmap);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
int width = buffer.readInt();
int height = buffer.readInt();
@@ -97,7 +106,7 @@ public class BitmapData implements Operation, SerializableToString {
operations.add(new BitmapData(imageId, width, height, bitmap));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Bitmap data")
.field(DocumentedOperation.INT, "id", "id of bitmap data")
@@ -107,17 +116,18 @@ public class BitmapData implements Operation, SerializableToString {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadBitmap(mImageId, mImageWidth, mImageHeight, mBitmap);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent,
CLASS_NAME + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index 8b9e5a8d7625..83d0ac7a1eb2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
@@ -67,10 +69,11 @@ public class ClickArea implements RemoteComposeOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mContentDescription, mLeft, mTop, mRight, mBottom, mMetadata);
}
+ @NonNull
@Override
public String toString() {
return "CLICK_AREA <"
@@ -97,18 +100,20 @@ public class ClickArea implements RemoteComposeOperation {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
if (context.getMode() != RemoteContext.ContextMode.DATA) {
return;
}
context.addClickArea(mId, mContentDescription, mLeft, mTop, mRight, mBottom, mMetadata);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -118,7 +123,7 @@ public class ClickArea implements RemoteComposeOperation {
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int id,
int contentDescription,
float left,
@@ -136,7 +141,7 @@ public class ClickArea implements RemoteComposeOperation {
buffer.writeInt(metadata);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int contentDescription = buffer.readInt();
float left = buffer.readFloat();
@@ -149,7 +154,7 @@ public class ClickArea implements RemoteComposeOperation {
operations.add(clickArea);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Define a region you can click on")
.field(DocumentedOperation.FLOAT, "left", "The left side of the region")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index 96b600acc971..db93829586bb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -57,16 +59,17 @@ public class ClipPath extends PaintOperation {
public static final int UNDEFINED = PATH_CLIP_UNDEFINED;
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId);
}
+ @NonNull
@Override
public String toString() {
return "ClipPath " + mId + ";";
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int pack = buffer.readInt();
int id = pack & 0xFFFFF;
int regionOp = pack >> 24;
@@ -74,6 +77,7 @@ public class ClipPath extends PaintOperation {
operations.add(op);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -82,19 +86,19 @@ public class ClipPath extends PaintOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int id) {
+ public static void apply(@NonNull WireBuffer buffer, int id) {
buffer.start(OP_CODE);
buffer.writeInt(id);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with the path")
.field(DocumentedOperation.INT, "id", "id of the path");
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.clipPath(mId, mRegionOp);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index b101bfb2d151..df54fb1ed834 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -29,7 +31,7 @@ public class ClipRect extends DrawBase4 {
public static final int OP_CODE = Operations.CLIP_RECT;
public static final String CLASS_NAME = "ClipRect";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = ClipRect::new;
read(m, buffer, operations);
}
@@ -38,16 +40,17 @@ public class ClipRect extends DrawBase4 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with rectangle")
.field(
@@ -74,7 +77,7 @@ public class ClipRect extends DrawBase4 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.clipRect(mX1, mY1, mX2, mY2);
}
@@ -87,7 +90,7 @@ public class ClipRect extends DrawBase4 {
* @param x2 end x of the DrawOval
* @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
index 19d80daf0c8f..929c9a603079 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -39,15 +41,17 @@ public class ColorConstant implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mColorId, mColor);
}
+ @NonNull
@Override
public String toString() {
return "ColorConstant[" + mColorId + "] = " + Utils.colorInt(mColor) + "";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -63,19 +67,19 @@ public class ColorConstant implements Operation {
* @param colorId
* @param color
*/
- public static void apply(WireBuffer buffer, int colorId, int color) {
+ public static void apply(@NonNull WireBuffer buffer, int colorId, int color) {
buffer.start(OP_CODE);
buffer.writeInt(colorId);
buffer.writeInt(color);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int colorId = buffer.readInt();
int color = buffer.readInt();
operations.add(new ColorConstant(colorId, color));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Define a Color")
.field(DocumentedOperation.INT, "id", "Id of the color")
@@ -83,10 +87,11 @@ public class ColorConstant implements Operation {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadColor(mColorId, mColor);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index b6041eaeacdc..3d840c5b8203 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -94,7 +96,7 @@ public class ColorExpression implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
if (mMode == 4) {
if (Float.isNaN(mHue)) {
mOutHue = context.getFloat(Utils.idFromNan(mHue));
@@ -118,7 +120,7 @@ public class ColorExpression implements Operation, VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (mMode == 4) {
if (Float.isNaN(mHue)) {
context.listensTo(Utils.idFromNan(mHue), this);
@@ -143,7 +145,7 @@ public class ColorExpression implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
if (mMode == 4) {
context.loadColor(
mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
@@ -164,11 +166,12 @@ public class ColorExpression implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
int mode = mMode | (mAlpha << 16);
apply(buffer, mId, mode, mColor1, mColor2, mTween);
}
+ @NonNull
@Override
public String toString() {
if (mMode == 4) {
@@ -196,6 +199,7 @@ public class ColorExpression implements Operation, VariableSupport {
+ ")";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -215,7 +219,7 @@ public class ColorExpression implements Operation, VariableSupport {
* @param tween
*/
public static void apply(
- WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
+ @NonNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(mode);
@@ -224,7 +228,7 @@ public class ColorExpression implements Operation, VariableSupport {
buffer.writeFloat(tween);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int mode = buffer.readInt();
int color1 = buffer.readInt();
@@ -234,7 +238,7 @@ public class ColorExpression implements Operation, VariableSupport {
operations.add(new ColorExpression(id, mode, color1, color2, tween));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Color defined by an expression")
.field(DocumentedOperation.INT, "id", "Id of the color")
@@ -249,6 +253,7 @@ public class ColorExpression implements Operation, VariableSupport {
.field(FLOAT, "tween", "32 bit ARGB color");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
index 992972076839..142c97b24d2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -43,10 +46,12 @@ public class ComponentValue implements Operation, SerializableToString {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
+ @NonNull
@Override
public String toString() {
return CLASS_NAME + "(" + mType + ", " + mComponentID + ", " + mValueId + ")";
@@ -65,7 +70,7 @@ public class ComponentValue implements Operation, SerializableToString {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mType, mComponentID, mValueId);
}
@@ -74,7 +79,7 @@ public class ComponentValue implements Operation, SerializableToString {
// Nothing
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int type = buffer.readInt();
int componentId = buffer.readInt();
int valueId = buffer.readInt();
@@ -82,7 +87,7 @@ public class ComponentValue implements Operation, SerializableToString {
operations.add(op);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Encode a component-related value (eg its width, height etc.)")
.field(
@@ -111,20 +116,21 @@ public class ComponentValue implements Operation, SerializableToString {
* @param componentId component id to reference
* @param valueId remote float used to represent the component value
*/
- public static void apply(WireBuffer buffer, int type, int componentId, int valueId) {
+ public static void apply(@NonNull WireBuffer buffer, int type, int componentId, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(type);
buffer.writeInt(componentId);
buffer.writeInt(valueId);
}
+ @Nullable
@Override
public String deepToString(String indent) {
return null;
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
String type = "WIDTH";
if (mType == HEIGHT) {
type = "HEIGHT";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index 00758694c254..ba02b91b36ed 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -48,7 +50,7 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
context.addCollection(mId, this);
for (float value : mValues) {
if (Utils.isVariable(value)) {
@@ -58,16 +60,17 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mValues);
}
+ @NonNull
@Override
public String toString() {
return "DataListFloat[" + Utils.idString(mId) + "] " + Arrays.toString(mValues);
}
- public static void apply(WireBuffer buffer, int id, float[] values) {
+ public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] values) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(values.length);
@@ -76,7 +79,7 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
if (len > MAX_FLOAT_ARRAY) {
@@ -90,7 +93,7 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
operations.add(data);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of Floats")
.field(DocumentedOperation.INT, "id", "id the array (2xxxxx)")
@@ -98,13 +101,14 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
.field(FLOAT_ARRAY, "values", "length", "array of floats");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.addCollection(mId, this);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index c43dab4bbee0..b9820f856a92 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -49,16 +51,17 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
public void registerListening(RemoteContext context) {}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mIds);
}
+ @NonNull
@Override
public String toString() {
return "map[" + Utils.idString(mId) + "] \"" + Arrays.toString(mIds) + "\"";
}
- public static void apply(WireBuffer buffer, int id, int[] ids) {
+ public static void apply(@NonNull WireBuffer buffer, int id, @NonNull int[] ids) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(ids.length);
@@ -67,7 +70,7 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
if (len > MAX_LIST) {
@@ -81,7 +84,7 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
operations.add(data);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of id's")
.field(DocumentedOperation.INT, "id", "id the array")
@@ -89,13 +92,14 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
.field(INT_ARRAY, "ids[n]", "length", "ids of other variables");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.addCollection(mId, this);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index 75db29d2781e..fb559bbf1863 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -64,10 +66,11 @@ public class DataMapIds implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mDataMap.mNames, mDataMap.mTypes, mDataMap.mIds);
}
+ @NonNull
@Override
public String toString() {
StringBuilder builder = new StringBuilder("DataMapIds[" + Utils.idString(mId) + "] ");
@@ -84,7 +87,8 @@ public class DataMapIds implements Operation {
return builder.toString();
}
- public static void apply(WireBuffer buffer, int id, String[] names, byte[] type, int[] ids) {
+ public static void apply(
+ @NonNull WireBuffer buffer, int id, @NonNull String[] names, byte[] type, int[] ids) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(names.length);
@@ -95,7 +99,7 @@ public class DataMapIds implements Operation {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
if (len > MAX_MAP) {
@@ -113,7 +117,7 @@ public class DataMapIds implements Operation {
operations.add(data);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a collection of name id pairs")
.field(INT, "id", "id the array")
@@ -122,13 +126,14 @@ public class DataMapIds implements Operation {
.field(UTF8, "id[0]", "length", "path encoded as floats");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.putDataMap(mId, mDataMap);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index e078307f3bd9..629f78647079 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,7 +30,7 @@ public class DrawArc extends DrawBase6 {
public static final int OP_CODE = Operations.DRAW_ARC;
private static final String CLASS_NAME = "DrawArc";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawArc::new;
read(m, buffer, operations);
}
@@ -49,7 +51,13 @@ public class DrawArc extends DrawBase6 {
* @param v6 Sweep angle (in degrees) measured clockwise
*/
public static void apply(
- WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ @NonNull WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -61,11 +69,17 @@ public class DrawArc extends DrawBase6 {
@Override
protected void write(
- WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ @NonNull WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description(
"Draw the specified arc"
@@ -90,7 +104,7 @@ public class DrawArc extends DrawBase6 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index c678cc4a36be..984599e09c19 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -27,7 +30,7 @@ import java.util.List;
/** Base class for commands that take 3 float */
public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
- protected String mName = "DrawRectBase";
+ @NonNull protected String mName = "DrawRectBase";
float mV1;
float mV2;
float mValue1;
@@ -41,13 +44,13 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mValue1)) {
context.listensTo(Utils.idFromNan(mValue1), this);
}
@@ -67,12 +70,14 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor
DrawBase2 create(float v1, float v2);
}
+ @NonNull
@Override
public String toString() {
return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
}
- public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
+ public static void read(
+ @NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
float v2 = buffer.readFloat();
@@ -87,6 +92,7 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor
* @param y1
* @return
*/
+ @Nullable
public Operation construct(float x1, float y1) {
return null;
}
@@ -99,7 +105,7 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor
* @param x1
* @param y1
*/
- protected static void write(WireBuffer buffer, int opCode, float x1, float y1) {
+ protected static void write(@NonNull WireBuffer buffer, int opCode, float x1, float y1) {
buffer.start(opCode);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index e1108e906d8d..825da5240032 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -28,7 +31,7 @@ import java.util.List;
/** Base class for commands that take 3 float */
public abstract class DrawBase3 extends PaintOperation implements VariableSupport {
- protected String mName = "DrawRectBase";
+ @NonNull protected String mName = "DrawRectBase";
float mV1;
float mV2;
float mV3;
@@ -47,14 +50,14 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mV1 = Utils.isVariable(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
mV2 = Utils.isVariable(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
mV3 = Utils.isVariable(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Utils.isVariable(mValue1)) {
context.listensTo(Utils.idFromNan(mValue1), this);
}
@@ -77,6 +80,7 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor
DrawBase3 create(float v1, float v2, float v3);
}
+ @NonNull
@Override
public String toString() {
return mName
@@ -88,7 +92,8 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor
+ floatToString(mV3);
}
- public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
+ public static void read(
+ @NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
float v2 = buffer.readFloat();
float v3 = buffer.readFloat();
@@ -104,6 +109,7 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor
* @param x2
* @return
*/
+ @Nullable
public Operation construct(float x1, float y1, float x2) {
return null;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
index 09f0df985b5c..a23bcb9dca78 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -27,7 +30,7 @@ import java.util.List;
/** Base class for draw commands that take 4 floats */
public abstract class DrawBase4 extends PaintOperation implements VariableSupport {
- protected String mName = "DrawRectBase";
+ @NonNull protected String mName = "DrawRectBase";
protected float mX1;
protected float mY1;
protected float mX2;
@@ -50,7 +53,7 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mX1 = Float.isNaN(mX1Value) ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
mY1 = Float.isNaN(mY1Value) ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
mX2 = Float.isNaN(mX2Value) ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
@@ -58,7 +61,7 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mX1Value)) {
context.listensTo(Utils.idFromNan(mX1Value), this);
}
@@ -84,6 +87,7 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
DrawBase4 create(float v1, float v2, float v3, float v4);
}
+ @NonNull
@Override
public String toString() {
return mName
@@ -97,7 +101,8 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
+ floatToString(mY2Value, mY2);
}
- public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
+ public static void read(
+ @NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
float v2 = buffer.readFloat();
float v3 = buffer.readFloat();
@@ -116,6 +121,7 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
* @param y2
* @return
*/
+ @Nullable
public Operation construct(float x1, float y1, float x2, float y2) {
return null;
}
@@ -131,7 +137,7 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
* @param y2
*/
protected static void write(
- WireBuffer buffer, int opCode, float x1, float y1, float x2, float y2) {
+ @NonNull WireBuffer buffer, int opCode, float x1, float y1, float x2, float y2) {
buffer.start(opCode);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
index e071d5f2096f..68c9f8cdc462 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -25,7 +28,7 @@ import java.util.List;
/** Base class for draw commands the take 6 floats */
public abstract class DrawBase6 extends PaintOperation implements VariableSupport {
- protected String mName = "DrawRectBase";
+ @NonNull protected String mName = "DrawRectBase";
float mV1;
float mV2;
float mV3;
@@ -56,7 +59,7 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
mV3 = Float.isNaN(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
@@ -66,7 +69,7 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mValue1)) {
context.listensTo(Utils.idFromNan(mValue1), this);
}
@@ -95,6 +98,7 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
protected abstract void write(
WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6);
+ @NonNull
@Override
public String toString() {
return mName
@@ -112,7 +116,8 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
DrawBase6 create(float v1, float v2, float v3, float v4, float v5, float v6);
}
- public static void read(Maker build, WireBuffer buffer, List<Operation> operations) {
+ public static void read(
+ @NonNull Maker build, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float sv1 = buffer.readFloat();
float sv2 = buffer.readFloat();
float sv3 = buffer.readFloat();
@@ -135,10 +140,12 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
* @param v6
* @return
*/
+ @Nullable
public Operation construct(float v1, float v2, float v3, float v4, float v5, float v6) {
return null;
}
+ @NonNull
public static String name() {
return "DrawBase6";
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 0b43fd24556a..9c23c9559953 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -54,7 +56,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutputLeft = Float.isNaN(mLeft) ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
mOutputTop = Float.isNaN(mTop) ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
mOutputRight = Float.isNaN(mRight) ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
@@ -62,7 +64,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mLeft)) {
context.listensTo(Utils.idFromNan(mLeft), this);
}
@@ -78,10 +80,11 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId);
}
+ @NonNull
@Override
public String toString() {
return "DrawBitmap (desc="
@@ -97,7 +100,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
+ ";";
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
float sLeft = buffer.readFloat();
float srcTop = buffer.readFloat();
@@ -109,6 +112,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
operations.add(op);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -118,7 +122,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int id,
float left,
float top,
@@ -134,7 +138,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
buffer.writeInt(descriptionId);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap")
.field(INT, "id", "id of float")
@@ -146,7 +150,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawBitmap(mId, mOutputLeft, mOutputTop, mOutputRight, mOutputBottom);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index fc7482759369..da9fe247bced 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -64,7 +66,7 @@ public class DrawBitmapInt extends PaintOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mImageId,
@@ -79,6 +81,7 @@ public class DrawBitmapInt extends PaintOperation {
mContentDescId);
}
+ @NonNull
@Override
public String toString() {
return "DRAW_BITMAP_INT "
@@ -103,6 +106,7 @@ public class DrawBitmapInt extends PaintOperation {
+ ";";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -112,7 +116,7 @@ public class DrawBitmapInt extends PaintOperation {
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int imageId,
int srcLeft,
int srcTop,
@@ -136,7 +140,7 @@ public class DrawBitmapInt extends PaintOperation {
buffer.writeInt(cdId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
int sLeft = buffer.readInt();
int srcTop = buffer.readInt();
@@ -155,7 +159,7 @@ public class DrawBitmapInt extends PaintOperation {
operations.add(op);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
.field(DocumentedOperation.INT, "id", "id of bitmap")
@@ -171,7 +175,7 @@ public class DrawBitmapInt extends PaintOperation {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawBitmap(
mImageId,
mSrcLeft,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
index 22742c63dd2d..e20bcd2d79b5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -45,7 +47,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
float mScaleFactor, mOutScaleFactor;
int mScaleType;
- ImageScaling mScaling = new ImageScaling();
+ @NonNull ImageScaling mScaling = new ImageScaling();
public static final int SCALE_NONE = ImageScaling.SCALE_NONE;
public static final int SCALE_INSIDE = ImageScaling.SCALE_INSIDE;
public static final int SCALE_FILL_WIDTH = ImageScaling.SCALE_FILL_WIDTH;
@@ -83,7 +85,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutSrcLeft =
Float.isNaN(mSrcLeft) ? context.getFloat(Utils.idFromNan(mSrcLeft)) : mSrcLeft;
mOutSrcTop = Float.isNaN(mSrcTop) ? context.getFloat(Utils.idFromNan(mSrcTop)) : mSrcTop;
@@ -109,7 +111,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
register(context, mSrcLeft);
register(context, mSrcTop);
register(context, mSrcRight);
@@ -121,12 +123,13 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
register(context, mScaleFactor);
}
- private void register(RemoteContext context, float value) {
+ private void register(@NonNull RemoteContext context, float value) {
if (Float.isNaN(value)) {
context.listensTo(Utils.idFromNan(value), this);
}
}
+ @NonNull
static String str(float v) {
String s = " " + (int) v;
return s.substring(s.length() - 3);
@@ -140,7 +143,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mImageId,
@@ -157,6 +160,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
mContentDescId);
}
+ @NonNull
@Override
public String toString() {
return "DrawBitmapScaled "
@@ -185,6 +189,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
+ Utils.floatToString(mScaleFactor, mOutScaleFactor);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -194,7 +199,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int imageId,
float srcLeft,
float srcTop,
@@ -225,7 +230,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
buffer.writeInt(cdId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
float sLeft = buffer.readFloat();
@@ -258,7 +263,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
operations.add(op);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
.field(DocumentedOperation.INT, "id", "id of bitmap")
@@ -289,7 +294,7 @@ public class DrawBitmapScaled extends PaintOperation implements VariableSupport
// }
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
mScaling.setup(
mOutSrcLeft,
mOutSrcTop,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index 04f095af29dc..11bd49a4a651 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,7 +30,7 @@ public class DrawCircle extends DrawBase3 {
private static final int OP_CODE = Operations.DRAW_CIRCLE;
private static final String CLASS_NAME = "DrawCircle";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawCircle::new;
read(m, buffer, operations);
}
@@ -37,11 +39,12 @@ public class DrawCircle extends DrawBase3 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a Circle")
.field(
@@ -56,7 +59,7 @@ public class DrawCircle extends DrawBase3 {
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3) {
apply(buffer, v1, v2, v3);
}
@@ -66,7 +69,7 @@ public class DrawCircle extends DrawBase3 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawCircle(mV1, mV2, mV3);
}
@@ -78,7 +81,7 @@ public class DrawCircle extends DrawBase3 {
* @param y1
* @param x2
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2) {
buffer.start(OP_CODE);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index dacbb0361761..7310a9defba6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -30,7 +32,7 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
private static final int OP_CODE = Operations.DRAW_LINE;
private static final String CLASS_NAME = "DrawLine";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawLine::new;
read(m, buffer, operations);
}
@@ -39,11 +41,12 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a line segment")
.field(
@@ -65,7 +68,7 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
@@ -75,7 +78,7 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawLine(mX1, mY1, mX2, mY2);
}
@@ -88,12 +91,12 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
* @param x2 end x of the line
* @param y2 end y of the line
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
String x1 = "" + mX1;
if (Float.isNaN(mX1Value)) {
x1 = "[" + Utils.idFromNan(mX1Value) + " = " + mX1 + "]";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 5d498e81c9d2..aa5116e950c5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,7 +30,7 @@ public class DrawOval extends DrawBase4 {
private static final int OP_CODE = Operations.DRAW_OVAL;
private static final String CLASS_NAME = "DrawOval";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawOval::new;
read(m, buffer, operations);
}
@@ -37,11 +39,12 @@ public class DrawOval extends DrawBase4 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified oval")
.field(DocumentedOperation.FLOAT, "left", "The left side of the oval")
@@ -51,12 +54,12 @@ public class DrawOval extends DrawBase4 {
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mX1, mY1, mX2, mY2);
}
@@ -66,7 +69,7 @@ public class DrawOval extends DrawBase4 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawOval(mX1, mY1, mX2, mY2);
}
@@ -79,7 +82,7 @@ public class DrawOval extends DrawBase4 {
* @param x2 end x of the DrawOval
* @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index ccbf3d9e3091..d35094b0b351 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -38,21 +40,23 @@ public class DrawPath extends PaintOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId);
}
+ @NonNull
@Override
public String toString() {
return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd;
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
DrawPath op = new DrawPath(id);
operations.add(op);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -61,19 +65,19 @@ public class DrawPath extends PaintOperation {
return Operations.DRAW_PATH;
}
- public static void apply(WireBuffer buffer, int id) {
+ public static void apply(@NonNull WireBuffer buffer, int id) {
buffer.start(Operations.DRAW_PATH);
buffer.writeInt(id);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
.field(DocumentedOperation.INT, "id", "id of path");
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawPath(mId, mStart, mEnd);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index 644011b8a214..db7633cbe335 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -29,7 +31,7 @@ public class DrawRect extends DrawBase4 {
private static final int OP_CODE = Operations.DRAW_RECT;
private static final String CLASS_NAME = "DrawRect";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawRect::new;
read(m, buffer, operations);
}
@@ -38,11 +40,12 @@ public class DrawRect extends DrawBase4 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified rectangle")
.field(DocumentedOperation.FLOAT, "left", "The left side of the rectangle")
@@ -52,7 +55,7 @@ public class DrawRect extends DrawBase4 {
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
@@ -62,7 +65,7 @@ public class DrawRect extends DrawBase4 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawRect(mX1, mY1, mX2, mY2);
}
@@ -75,7 +78,7 @@ public class DrawRect extends DrawBase4 {
* @param x2 right x of the rect
* @param y2 bottom y of the rect
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 64a3b283505b..c306e2b5f041 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -29,7 +31,7 @@ public class DrawRoundRect extends DrawBase6 {
private static final int OP_CODE = Operations.DRAW_ROUND_RECT;
private static final String CLASS_NAME = "DrawRoundRect";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawRoundRect::new;
read(m, buffer, operations);
}
@@ -50,7 +52,13 @@ public class DrawRoundRect extends DrawBase6 {
* @param v6 The y-radius of the oval used to round the corners
*/
public static void apply(
- WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ @NonNull WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -62,11 +70,17 @@ public class DrawRoundRect extends DrawBase6 {
@Override
protected void write(
- WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ @NonNull WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect")
.field(DocumentedOperation.FLOAT, "left", "The left side of the rect")
@@ -89,7 +103,7 @@ public class DrawRoundRect extends DrawBase6 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
index 3cb191647c33..3b60df7d529e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,7 +30,7 @@ public class DrawSector extends DrawBase6 {
public static final int OP_CODE = Operations.DRAW_SECTOR;
private static final String CLASS_NAME = "DrawSector";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = DrawSector::new;
read(m, buffer, operations);
}
@@ -49,7 +51,13 @@ public class DrawSector extends DrawBase6 {
* @param v6 Sweep angle (in degrees) measured clockwise
*/
public static void apply(
- WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ @NonNull WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -61,11 +69,17 @@ public class DrawSector extends DrawBase6 {
@Override
protected void write(
- WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ @NonNull WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description(
"Draw the specified sector (pie shape)"
@@ -90,7 +104,7 @@ public class DrawSector extends DrawBase6 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawSector(mV1, mV2, mV3, mV4, mV5, mV6);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
index bcb7852e6615..9c587aba3f7b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -64,13 +66,13 @@ public class DrawText extends PaintOperation implements VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mX)) {
context.listensTo(Utils.idFromNan(mX), this);
}
@@ -80,10 +82,11 @@ public class DrawText extends PaintOperation implements VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
}
+ @NonNull
@Override
public String toString() {
return "DrawTextRun ["
@@ -98,7 +101,7 @@ public class DrawText extends PaintOperation implements VariableSupport {
+ floatToString(mY, mOutY);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int text = buffer.readInt();
int start = buffer.readInt();
int end = buffer.readInt();
@@ -112,6 +115,7 @@ public class DrawText extends PaintOperation implements VariableSupport {
operations.add(op);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -134,7 +138,7 @@ public class DrawText extends PaintOperation implements VariableSupport {
* @param rtl is it Right to Left text
*/
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int textID,
int start,
int end,
@@ -154,7 +158,7 @@ public class DrawText extends PaintOperation implements VariableSupport {
buffer.writeBoolean(rtl);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", id(), CLASS_NAME)
.description("Draw a run of text, all in a single direction")
.field(DocumentedOperation.INT, "textId", "id of bitmap")
@@ -177,7 +181,7 @@ public class DrawText extends PaintOperation implements VariableSupport {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mOutX, mOutY, mRtl);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
index 95a87667dfab..8b7018191f4d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -57,7 +59,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
mOutPanX = Float.isNaN(mPanX) ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
@@ -65,7 +67,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mX)) {
context.listensTo(Utils.idFromNan(mX), this);
}
@@ -81,10 +83,11 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextID, mX, mY, mPanX, mPanY, mFlags);
}
+ @NonNull
@Override
public String toString() {
return "DrawTextAnchored ["
@@ -108,7 +111,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
return Float.toString(v);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textID = buffer.readInt();
float x = buffer.readFloat();
float y = buffer.readFloat();
@@ -121,6 +124,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
operations.add(op);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -141,7 +145,13 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
* @param flags Change the behaviour
*/
public static void apply(
- WireBuffer buffer, int textID, float x, float y, float panX, float panY, int flags) {
+ @NonNull WireBuffer buffer,
+ int textID,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
buffer.start(OP_CODE);
buffer.writeInt(textID);
buffer.writeFloat(x);
@@ -151,7 +161,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
buffer.writeInt(flags);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text centered about an anchor point")
.field(DocumentedOperation.INT, "textId", "id of bitmap")
@@ -168,7 +178,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
.field(DocumentedOperation.INT, "flags", "Change the behaviour");
}
- float[] mBounds = new float[4];
+ @NonNull float[] mBounds = new float[4];
private float getHorizontalOffset() {
// TODO scale TextSize / BaseTextSize;
@@ -188,7 +198,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
int flags =
((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index aefd6f397ebf..e90122bb95ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -46,7 +48,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutHOffset =
Float.isNaN(mHOffset) ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
mOutVOffset =
@@ -54,7 +56,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mHOffset)) {
context.listensTo(Utils.idFromNan(mHOffset), this);
}
@@ -64,10 +66,11 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mPathId, mHOffset, mVOffset);
}
+ @NonNull
@Override
public String toString() {
return "DrawTextOnPath ["
@@ -80,7 +83,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
+ Utils.floatToString(mVOffset, mOutVOffset);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int pathId = buffer.readInt();
float vOffset = buffer.readFloat();
@@ -89,6 +92,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
operations.add(op);
}
+ @NonNull
public static String name() {
return "DrawTextOnPath";
}
@@ -98,7 +102,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
}
public static void apply(
- WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
+ @NonNull WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeInt(pathId);
@@ -106,7 +110,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
buffer.writeFloat(hOffset);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
.field(DocumentedOperation.INT, "textId", "id of the text")
@@ -116,7 +120,7 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawTextOnPath(mTextId, mPathId, mOutHOffset, mOutVOffset);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index b6d45d95f2c0..0aaaf42ba838 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -50,14 +52,14 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutTween = Float.isNaN(mTween) ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
mOutStart = Float.isNaN(mStart) ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
mOutStop = Float.isNaN(mStop) ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mTween)) {
context.listensTo(Utils.idFromNan(mTween), this);
}
@@ -70,10 +72,11 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mPath1Id, mPath2Id, mTween, mStart, mStop);
}
+ @NonNull
@Override
public String toString() {
return "DrawTweenPath "
@@ -89,7 +92,7 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
+ floatToString(mStop, mOutStop);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int path1Id = buffer.readInt();
int path2Id = buffer.readInt();
float tween = buffer.readFloat();
@@ -99,6 +102,7 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
operations.add(op);
}
+ @NonNull
public static String name() {
return "DrawTweenPath";
}
@@ -108,7 +112,12 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
}
public static void apply(
- WireBuffer buffer, int path1Id, int path2Id, float tween, float start, float stop) {
+ @NonNull WireBuffer buffer,
+ int path1Id,
+ int path2Id,
+ float tween,
+ float start,
+ float stop) {
buffer.start(OP_CODE);
buffer.writeInt(path1Id);
buffer.writeInt(path2Id);
@@ -117,7 +126,7 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
buffer.writeFloat(stop);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
.field(DocumentedOperation.INT, "pathId1", "id of path 1")
@@ -128,7 +137,7 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.drawTweenPath(mPath1Id, mPath2Id, mOutTween, mOutStart, mOutStop);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 765e150e81af..89390acb8c50 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -39,15 +41,17 @@ public class FloatConstant implements com.android.internal.widget.remotecompose.
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mValue);
}
+ @NonNull
@Override
public String toString() {
return "FloatConstant[" + mTextId + "] = " + mValue;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -63,20 +67,20 @@ public class FloatConstant implements com.android.internal.widget.remotecompose.
* @param id the id
* @param value the value of the float
*/
- public static void apply(WireBuffer buffer, int id, float value) {
+ public static void apply(@NonNull WireBuffer buffer, int id, float value) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeFloat(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
float value = buffer.readFloat();
operations.add(new FloatConstant(textId, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A float and its associated id")
.field(DocumentedOperation.INT, "id", "id of float")
@@ -84,10 +88,11 @@ public class FloatConstant implements com.android.internal.widget.remotecompose.
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadFloat(mTextId, mValue);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
index d71793364a33..e1c6c2577860 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -20,6 +20,9 @@ import static com.android.internal.widget.remotecompose.core.documentation.Docum
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -48,7 +51,7 @@ public class FloatExpression implements Operation, VariableSupport {
public float[] mPreCalcValue;
private float mLastChange = Float.NaN;
private float mLastCalculatedValue = Float.NaN;
- AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+ @NonNull AnimatedFloatExpression mExp = new AnimatedFloatExpression();
public static final int MAX_EXPRESSION_SIZE = 32;
public FloatExpression(int id, float[] value, float[] animation) {
@@ -61,7 +64,7 @@ public class FloatExpression implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
mPreCalcValue = new float[mSrcValue.length];
}
@@ -107,7 +110,7 @@ public class FloatExpression implements Operation, VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
for (float v : mSrcValue) {
if (Float.isNaN(v)
&& !AnimatedFloatExpression.isMathOperator(v)
@@ -118,7 +121,7 @@ public class FloatExpression implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
updateVariables(context);
float t = context.getAnimationTime();
if (Float.isNaN(mLastChange)) {
@@ -135,10 +138,11 @@ public class FloatExpression implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mSrcValue, mSrcAnimation);
}
+ @NonNull
@Override
public String toString() {
String[] labels = new String[mSrcValue.length];
@@ -161,6 +165,7 @@ public class FloatExpression implements Operation, VariableSupport {
+ ")";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -177,7 +182,11 @@ public class FloatExpression implements Operation, VariableSupport {
* @param value the float expression array
* @param animation the animation expression array
*/
- public static void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int id,
+ @NonNull float[] value,
+ @Nullable float[] animation) {
buffer.start(OP_CODE);
buffer.writeInt(id);
@@ -197,7 +206,7 @@ public class FloatExpression implements Operation, VariableSupport {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int len = buffer.readInt();
int valueLen = len & 0xFFFF;
@@ -222,7 +231,7 @@ public class FloatExpression implements Operation, VariableSupport {
operations.add(new FloatExpression(id, values, animation));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Float expression")
.field(DocumentedOperation.INT, "id", "The id of the Color")
@@ -245,6 +254,7 @@ public class FloatExpression implements Operation, VariableSupport {
.field(FLOAT, "wrapValue", "> [Wrap value] ");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
index 4f8516f5235d..1979bc5f4331 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
@@ -80,10 +82,11 @@ public class Header implements RemoteComposeOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mWidth, mHeight, mDensity, mCapabilities);
}
+ @NonNull
@Override
public String toString() {
return "HEADER v"
@@ -102,15 +105,17 @@ public class Header implements RemoteComposeOperation {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.header(mMajorVersion, mMinorVersion, mPatchVersion, mWidth, mHeight, mCapabilities);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return toString();
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -120,7 +125,7 @@ public class Header implements RemoteComposeOperation {
}
public static void apply(
- WireBuffer buffer, int width, int height, float density, long capabilities) {
+ @NonNull WireBuffer buffer, int width, int height, float density, long capabilities) {
buffer.start(OP_CODE);
buffer.writeInt(MAJOR_VERSION); // major version number of the protocol
buffer.writeInt(MINOR_VERSION); // minor version number of the protocol
@@ -131,7 +136,7 @@ public class Header implements RemoteComposeOperation {
buffer.writeLong(capabilities);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int majorVersion = buffer.readInt();
int minorVersion = buffer.readInt();
int patchVersion = buffer.readInt();
@@ -152,7 +157,7 @@ public class Header implements RemoteComposeOperation {
operations.add(header);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description(
"Document metadata, containing the version,"
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
index c9a850875011..6375f001c818 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -45,7 +47,7 @@ public class IntegerExpression implements Operation, VariableSupport {
public int[] mPreCalcValue;
private float mLastChange = Float.NaN;
public static final int MAX_SIZE = 320;
- IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
+ @NonNull IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
public IntegerExpression(int id, int mask, int[] value) {
this.mId = id;
@@ -54,7 +56,7 @@ public class IntegerExpression implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
mPreCalcValue = new int[mSrcValue.length];
}
@@ -70,7 +72,7 @@ public class IntegerExpression implements Operation, VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
for (int i = 0; i < mSrcValue.length; i++) {
if (isId(mMask, i, mSrcValue[i])) {
context.listensTo(mSrcValue[i], this);
@@ -79,7 +81,7 @@ public class IntegerExpression implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
updateVariables(context);
float t = context.getAnimationTime();
if (Float.isNaN(mLastChange)) {
@@ -95,7 +97,7 @@ public class IntegerExpression implements Operation, VariableSupport {
* @param context current context
* @return the resulting value
*/
- public int evaluate(RemoteContext context) {
+ public int evaluate(@NonNull RemoteContext context) {
updateVariables(context);
float t = context.getAnimationTime();
if (Float.isNaN(mLastChange)) {
@@ -105,10 +107,11 @@ public class IntegerExpression implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mMask, mSrcValue);
}
+ @NonNull
@Override
public String toString() {
StringBuilder s = new StringBuilder();
@@ -132,6 +135,7 @@ public class IntegerExpression implements Operation, VariableSupport {
return "IntegerExpression[" + mId + "] = (" + s + ")";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -148,7 +152,7 @@ public class IntegerExpression implements Operation, VariableSupport {
* @param mask the mask bits of ints & operators or variables
* @param value array of integers to be evaluated
*/
- public static void apply(WireBuffer buffer, int id, int mask, int[] value) {
+ public static void apply(@NonNull WireBuffer buffer, int id, int mask, @NonNull int[] value) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(mask);
@@ -158,7 +162,7 @@ public class IntegerExpression implements Operation, VariableSupport {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int mask = buffer.readInt();
int len = buffer.readInt();
@@ -173,7 +177,7 @@ public class IntegerExpression implements Operation, VariableSupport {
operations.add(new IntegerExpression(id, mask, values));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Expression that computes an integer")
.field(DocumentedOperation.INT, "id", "id of integer")
@@ -182,6 +186,7 @@ public class IntegerExpression implements Operation, VariableSupport {
.field(INT_ARRAY, "values", "length", "Array of ints");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index 04f8a503adff..6a620e58570a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -31,20 +33,22 @@ public class MatrixRestore extends PaintOperation {
public MatrixRestore() {}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
MatrixRestore op = new MatrixRestore();
operations.add(op);
}
+ @NonNull
@Override
public String toString() {
return "MatrixRestore";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -53,17 +57,17 @@ public class MatrixRestore extends PaintOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Restore the matrix and clip");
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.matrixRestore();
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index df10f329630a..438a2aad648a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,9 +30,10 @@ public class MatrixRotate extends DrawBase3 {
public static final int OP_CODE = Operations.MATRIX_ROTATE;
private static final String CLASS_NAME = "MatrixRotate";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m =
new Maker() {
+ @NonNull
@Override
public DrawBase3 create(float v1, float v2, float v3) {
return new MatrixRotate(v1, v2, v3);
@@ -43,11 +46,12 @@ public class MatrixRotate extends DrawBase3 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("apply rotation to matrix")
.field(DocumentedOperation.FLOAT, "rotate", "Angle to rotate")
@@ -56,7 +60,7 @@ public class MatrixRotate extends DrawBase3 {
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3) {
apply(buffer, v1, v2, v3);
}
@@ -66,7 +70,7 @@ public class MatrixRotate extends DrawBase3 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.matrixRotate(mV1, mV2, mV3);
}
@@ -78,7 +82,7 @@ public class MatrixRotate extends DrawBase3 {
* @param y1 X Pivot point
* @param x2 Y Pivot point
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2) {
buffer.start(OP_CODE);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index 67612c7fd2f4..1880b19be1a7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -29,20 +31,22 @@ public class MatrixSave extends PaintOperation {
private static final String CLASS_NAME = "MatrixSave";
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
+ @NonNull
@Override
public String toString() {
return "MatrixSave;";
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
MatrixSave op = new MatrixSave();
operations.add(op);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -51,17 +55,17 @@ public class MatrixSave extends PaintOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(Operations.MATRIX_SAVE);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Save the matrix and clip to a stack");
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.matrixSave();
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 26c898acb67b..630458499977 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,7 +30,7 @@ public class MatrixScale extends DrawBase4 {
public static final int OP_CODE = Operations.MATRIX_SCALE;
public static final String CLASS_NAME = "MatrixScale";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = MatrixScale::new;
read(m, buffer, operations);
}
@@ -37,16 +39,17 @@ public class MatrixScale extends DrawBase4 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified Oval")
.field(DocumentedOperation.FLOAT, "scaleX", "The amount to scale in X")
@@ -61,7 +64,7 @@ public class MatrixScale extends DrawBase4 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.matrixScale(mX1, mY1, mX2, mY2);
}
@@ -74,7 +77,7 @@ public class MatrixScale extends DrawBase4 {
* @param x2 end x of the DrawOval
* @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
index d64117871eaa..675cf0de4743 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -29,7 +31,7 @@ public class MatrixSkew extends DrawBase2 {
public static final int OP_CODE = Operations.MATRIX_SKEW;
public static final String CLASS_NAME = "MatrixSkew";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = MatrixSkew::new;
read(m, buffer, operations);
}
@@ -38,16 +40,17 @@ public class MatrixSkew extends DrawBase2 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
apply(buffer, v1, v2);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Current matrix with the specified skew.")
.field(FLOAT, "skewX", "The amount to skew in X")
@@ -60,7 +63,7 @@ public class MatrixSkew extends DrawBase2 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.matrixSkew(mV1, mV2);
}
@@ -71,7 +74,7 @@ public class MatrixSkew extends DrawBase2 {
* @param x1 start x of DrawOval
* @param y1 start y of the DrawOval
*/
- public static void apply(WireBuffer buffer, float x1, float y1) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
write(buffer, OP_CODE, x1, y1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index e008292f1107..b0a7d352dfe3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -28,7 +30,7 @@ public class MatrixTranslate extends DrawBase2 {
public static final int OP_CODE = Operations.MATRIX_TRANSLATE;
public static final String CLASS_NAME = "MatrixTranslate";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = MatrixTranslate::new;
read(m, buffer, operations);
}
@@ -37,16 +39,17 @@ public class MatrixTranslate extends DrawBase2 {
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
apply(buffer, v1, v2);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, "MatrixTranslate")
.description("Preconcat the current matrix with the specified translation")
.field(DocumentedOperation.FLOAT, "dx", "The distance to translate in X")
@@ -59,7 +62,7 @@ public class MatrixTranslate extends DrawBase2 {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.matrixTranslate(mV1, mV2);
}
@@ -70,7 +73,7 @@ public class MatrixTranslate extends DrawBase2 {
* @param x1 start x of DrawOval
* @param y1 start y of the DrawOval
*/
- public static void apply(WireBuffer buffer, float x1, float y1) {
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
write(buffer, OP_CODE, x1, y1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
index fa6e2712a0bb..6310521e4010 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -47,10 +49,11 @@ public class NamedVariable implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mVarId, mVarType, mVarName);
}
+ @NonNull
@Override
public String toString() {
return "VariableName["
@@ -61,6 +64,7 @@ public class NamedVariable implements Operation {
+ mVarType;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -77,21 +81,22 @@ public class NamedVariable implements Operation {
* @param varType The type of variable
* @param text String
*/
- public static void apply(WireBuffer buffer, int varId, int varType, String text) {
+ public static void apply(
+ @NonNull WireBuffer buffer, int varId, int varType, @NonNull String text) {
buffer.start(Operations.NAMED_VARIABLE);
buffer.writeInt(varId);
buffer.writeInt(varType);
buffer.writeUTF8(text);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int varId = buffer.readInt();
int varType = buffer.readInt();
String text = buffer.readUTF8(MAX_STRING_SIZE);
operations.add(new NamedVariable(varId, varType, text));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Add a string name for an ID")
.field(DocumentedOperation.INT, "varId", "id to label")
@@ -100,10 +105,11 @@ public class NamedVariable implements Operation {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadVariableName(mVarName, mVarId, mVarType);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index 095a0106b3d7..527d5610788c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -33,31 +35,33 @@ import java.util.List;
public class PaintData extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.PAINT_VALUES;
private static final String CLASS_NAME = "PaintData";
- public PaintBundle mPaintData = new PaintBundle();
+ @NonNull public PaintBundle mPaintData = new PaintBundle();
public static final int MAX_STRING_SIZE = 4000;
public PaintData() {}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mPaintData.updateVariables(context);
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
mPaintData.registerVars(context, this);
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mPaintData);
}
+ @NonNull
@Override
public String toString() {
return "PaintData " + "\"" + mPaintData + "\"";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -66,31 +70,32 @@ public class PaintData extends PaintOperation implements VariableSupport {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, PaintBundle paintBundle) {
+ public static void apply(@NonNull WireBuffer buffer, @NonNull PaintBundle paintBundle) {
buffer.start(Operations.PAINT_VALUES);
paintBundle.writeBundle(buffer);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
PaintData data = new PaintData();
data.mPaintData.readBundle(buffer);
operations.add(data);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Paint ")
.field(INT, "length", "id string")
.field(INT_ARRAY, "paint", "length", "path encoded as floats");
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.applyPaint(mPaintData);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 13d5a49a584b..06a1fec36196 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -18,6 +18,9 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -43,7 +46,7 @@ public class PathData implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
for (int i = 0; i < mFloatPath.length; i++) {
float v = mFloatPath[i];
if (Utils.isVariable(v)) {
@@ -55,7 +58,7 @@ public class PathData implements Operation, VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
for (float v : mFloatPath) {
if (Float.isNaN(v)) {
context.listensTo(Utils.idFromNan(v), this);
@@ -64,15 +67,17 @@ public class PathData implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mInstanceId, mOutputPath);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return pathString(mFloatPath);
}
+ @NonNull
@Override
public String toString() {
return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\"";
@@ -102,6 +107,7 @@ public class PathData implements Operation, VariableSupport {
public static final float CLOSE_NAN = Utils.asNan(CLOSE);
public static final float DONE_NAN = Utils.asNan(DONE);
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -110,7 +116,7 @@ public class PathData implements Operation, VariableSupport {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int id, float[] data) {
+ public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) {
buffer.start(Operations.DATA_PATH);
buffer.writeInt(id);
buffer.writeInt(data.length);
@@ -119,7 +125,7 @@ public class PathData implements Operation, VariableSupport {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int imageId = buffer.readInt();
int len = buffer.readInt();
float[] data = new float[len];
@@ -129,7 +135,7 @@ public class PathData implements Operation, VariableSupport {
operations.add(new PathData(imageId, data));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Path ")
.field(DocumentedOperation.INT, "id", "id string")
@@ -137,7 +143,8 @@ public class PathData implements Operation, VariableSupport {
.field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats");
}
- public static String pathString(float[] path) {
+ @NonNull
+ public static String pathString(@Nullable float[] path) {
if (path == null) {
return "null";
}
@@ -186,7 +193,7 @@ public class PathData implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadPathData(mInstanceId, mOutputPath);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 4a8f5324b74e..6ff9ad73e35f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
@@ -172,10 +174,11 @@ public class RootContentBehavior implements RemoteComposeOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mScroll, mAlignment, mSizing, mMode);
}
+ @NonNull
@Override
public String toString() {
return "ROOT_CONTENT_BEHAVIOR scroll: "
@@ -187,15 +190,17 @@ public class RootContentBehavior implements RemoteComposeOperation {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.setRootContentBehavior(mScroll, mAlignment, mSizing, mMode);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return toString();
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -204,7 +209,8 @@ public class RootContentBehavior implements RemoteComposeOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int scroll, int alignment, int sizing, int mode) {
+ public static void apply(
+ @NonNull WireBuffer buffer, int scroll, int alignment, int sizing, int mode) {
buffer.start(OP_CODE);
buffer.writeInt(scroll);
buffer.writeInt(alignment);
@@ -212,7 +218,7 @@ public class RootContentBehavior implements RemoteComposeOperation {
buffer.writeInt(mode);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int scroll = buffer.readInt();
int alignment = buffer.readInt();
int sizing = buffer.readInt();
@@ -222,7 +228,7 @@ public class RootContentBehavior implements RemoteComposeOperation {
operations.add(rootContentBehavior);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Describes the behaviour of the root")
.field(DocumentedOperation.INT, "scroll", "scroll")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index bff902926fd3..c2d62a7b3ac4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
@@ -41,25 +43,28 @@ public class RootContentDescription implements RemoteComposeOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mContentDescription);
}
+ @NonNull
@Override
public String toString() {
return "RootContentDescription " + mContentDescription;
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.setDocumentContentDescription(mContentDescription);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return toString();
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -68,18 +73,18 @@ public class RootContentDescription implements RemoteComposeOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int contentDescription) {
+ public static void apply(@NonNull WireBuffer buffer, int contentDescription) {
buffer.start(Operations.ROOT_CONTENT_DESCRIPTION);
buffer.writeInt(contentDescription);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int contentDescription = buffer.readInt();
RootContentDescription header = new RootContentDescription(contentDescription);
operations.add(header);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Content description of root")
.field(DocumentedOperation.INT, "id", "id of Int");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
index 7ec7879bf8b2..ae61c3aa01c9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -22,6 +22,9 @@ import static com.android.internal.widget.remotecompose.core.documentation.Docum
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -43,17 +46,17 @@ public class ShaderData implements Operation, VariableSupport {
private static final String CLASS_NAME = "ShaderData";
int mShaderTextId; // the actual text of a shader
int mShaderID; // allows shaders to be referenced by number
- HashMap<String, float[]> mUniformRawFloatMap = null;
- HashMap<String, float[]> mUniformFloatMap = null;
- HashMap<String, int[]> mUniformIntMap = null;
- HashMap<String, Integer> mUniformBitmapMap = null;
+ @Nullable HashMap<String, float[]> mUniformRawFloatMap = null;
+ @Nullable HashMap<String, float[]> mUniformFloatMap = null;
+ @Nullable HashMap<String, int[]> mUniformIntMap = null;
+ @Nullable HashMap<String, Integer> mUniformBitmapMap = null;
public ShaderData(
int shaderID,
int shaderTextId,
- HashMap<String, float[]> floatMap,
- HashMap<String, int[]> intMap,
- HashMap<String, Integer> bitmapMap) {
+ @Nullable HashMap<String, float[]> floatMap,
+ @Nullable HashMap<String, int[]> intMap,
+ @Nullable HashMap<String, Integer> bitmapMap) {
mShaderID = shaderID;
mShaderTextId = shaderTextId;
if (floatMap != null) {
@@ -89,6 +92,7 @@ public class ShaderData implements Operation, VariableSupport {
*
* @return Names of all uniform floats or empty array
*/
+ @NonNull
public String[] getUniformFloatNames() {
if (mUniformFloatMap == null) return new String[0];
return mUniformFloatMap.keySet().toArray(new String[0]);
@@ -109,6 +113,7 @@ public class ShaderData implements Operation, VariableSupport {
*
* @return Name of all integer uniforms
*/
+ @NonNull
public String[] getUniformIntegerNames() {
if (mUniformIntMap == null) return new String[0];
return mUniformIntMap.keySet().toArray(new String[0]);
@@ -129,6 +134,7 @@ public class ShaderData implements Operation, VariableSupport {
*
* @return Name of all bitmap uniforms
*/
+ @NonNull
public String[] getUniformBitmapNames() {
if (mUniformBitmapMap == null) return new String[0];
return mUniformBitmapMap.keySet().toArray(new String[0]);
@@ -145,7 +151,7 @@ public class ShaderData implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mShaderID,
@@ -155,13 +161,14 @@ public class ShaderData implements Operation, VariableSupport {
mUniformBitmapMap);
}
+ @NonNull
@Override
public String toString() {
return "SHADER DATA " + mShaderID;
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
for (String name : mUniformRawFloatMap.keySet()) {
float[] value = mUniformRawFloatMap.get(name);
float[] out = null;
@@ -178,7 +185,7 @@ public class ShaderData implements Operation, VariableSupport {
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
for (String name : mUniformRawFloatMap.keySet()) {
float[] value = mUniformRawFloatMap.get(name);
for (float v : value) {
@@ -189,6 +196,7 @@ public class ShaderData implements Operation, VariableSupport {
}
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -208,12 +216,12 @@ public class ShaderData implements Operation, VariableSupport {
* @param bitmapMap the map of bitmap uniforms
*/
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int shaderID,
int shaderTextId,
- HashMap<String, float[]> floatMap,
- HashMap<String, int[]> intMap,
- HashMap<String, Integer> bitmapMap) {
+ @Nullable HashMap<String, float[]> floatMap,
+ @Nullable HashMap<String, int[]> intMap,
+ @Nullable HashMap<String, Integer> bitmapMap) {
buffer.start(OP_CODE);
buffer.writeInt(shaderID);
@@ -256,7 +264,7 @@ public class ShaderData implements Operation, VariableSupport {
}
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int shaderID = buffer.readInt();
int shaderTextId = buffer.readInt();
HashMap<String, float[]> floatMap = null;
@@ -308,7 +316,7 @@ public class ShaderData implements Operation, VariableSupport {
operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Shader")
.field(DocumentedOperation.INT, "shaderID", "id of shader")
@@ -326,10 +334,11 @@ public class ShaderData implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadShader(mShaderID, this);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index 638324950f88..dbaef7ef7ae1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -42,15 +44,17 @@ public class TextData implements Operation, SerializableToString {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mText);
}
+ @NonNull
@Override
public String toString() {
return "TextData[" + mTextId + "] = \"" + Utils.trimString(mText, 10) + "\"";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -59,20 +63,20 @@ public class TextData implements Operation, SerializableToString {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int textId, String text) {
+ public static void apply(@NonNull WireBuffer buffer, int textId, @NonNull String text) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeUTF8(text);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
String text = buffer.readUTF8(MAX_STRING_SIZE);
operations.add(new TextData(textId, text));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a string ")
.field(DocumentedOperation.INT, "id", "id string")
@@ -80,20 +84,22 @@ public class TextData implements Operation, SerializableToString {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadText(mTextId, mText);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, getSerializedName() + "<" + mTextId + "> = \"" + mText + "\"");
}
+ @NonNull
private String getSerializedName() {
return "DATA_TEXT";
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
index 0d966d10384a..fb5087f3fee3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -87,10 +89,11 @@ public class TextFromFloat implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mValue, mDigitsBefore, mDigitsAfter, mFlags);
}
+ @NonNull
@Override
public String toString() {
return "TextFromFloat["
@@ -106,19 +109,20 @@ public class TextFromFloat implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
if (Float.isNaN(mValue)) {
mOutValue = context.getFloat(Utils.idFromNan(mValue));
}
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mValue)) {
context.listensTo(Utils.idFromNan(mValue), this);
}
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -138,7 +142,7 @@ public class TextFromFloat implements Operation, VariableSupport {
* @param flags flags that control if and how to fill the empty spots
*/
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int textId,
float value,
short digitsBefore,
@@ -151,7 +155,7 @@ public class TextFromFloat implements Operation, VariableSupport {
buffer.writeInt(flags);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
float value = buffer.readFloat();
int tmp = buffer.readInt();
@@ -162,7 +166,7 @@ public class TextFromFloat implements Operation, VariableSupport {
operations.add(new TextFromFloat(textId, value, pre, post, flags));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
.field(DocumentedOperation.INT, "textId", "id of the text generated")
@@ -173,12 +177,13 @@ public class TextFromFloat implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
float v = mOutValue;
String s = StringUtils.floatToString(v, mDigitsBefore, mDigitsAfter, mPre, mAfter);
context.loadText(mTextId, s);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
index b04d698fa36c..2129edde2f59 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -48,10 +50,11 @@ public class TextLookup implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mDataSetId, mIndex);
}
+ @NonNull
@Override
public String toString() {
return "TextLookup["
@@ -63,19 +66,20 @@ public class TextLookup implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
if (Float.isNaN(mIndex)) {
mOutIndex = context.getFloat(Utils.idFromNan(mIndex));
}
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (Float.isNaN(mIndex)) {
context.listensTo(Utils.idFromNan(mIndex), this);
}
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -92,21 +96,21 @@ public class TextLookup implements Operation, VariableSupport {
* @param dataSet float pointer to the array/list to turn int a string
* @param index index of element to return
*/
- public static void apply(WireBuffer buffer, int textId, int dataSet, float index) {
+ public static void apply(@NonNull WireBuffer buffer, int textId, int dataSet, float index) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeInt(dataSet);
buffer.writeFloat(index);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int dataSetId = buffer.readInt();
float index = buffer.readFloat();
operations.add(new TextLookup(textId, dataSetId, index));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Look an array and turn into a text object")
.field(INT, "textId", "id of the text generated")
@@ -115,11 +119,12 @@ public class TextLookup implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
context.loadText(mTextId, context.getText(id));
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
index 171bea249273..ea550cbe010c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -45,10 +47,11 @@ public class TextLookupInt implements Operation, VariableSupport {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mDataSetId, mIndex);
}
+ @NonNull
@Override
public String toString() {
return "TextLookupInt["
@@ -60,15 +63,16 @@ public class TextLookupInt implements Operation, VariableSupport {
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mOutIndex = context.getInteger(mIndex);
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
context.listensTo(mIndex, this);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -85,21 +89,21 @@ public class TextLookupInt implements Operation, VariableSupport {
* @param dataSet float pointer to the array/list to turn int a string
* @param indexId index of element to return
*/
- public static void apply(WireBuffer buffer, int textId, int dataSet, int indexId) {
+ public static void apply(@NonNull WireBuffer buffer, int textId, int dataSet, int indexId) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeInt(dataSet);
buffer.writeInt(indexId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int dataSetId = buffer.readInt();
int indexId = buffer.readInt();
operations.add(new TextLookupInt(textId, dataSetId, indexId));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Look up an array and turn into a text object")
.field(DocumentedOperation.INT, "textId", "id of the text generated")
@@ -108,11 +112,12 @@ public class TextLookupInt implements Operation, VariableSupport {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
context.loadText(mTextId, context.getText(id));
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
index 78cc674a22e9..fa18b4ddb2a2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -41,15 +43,17 @@ public class TextMerge implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTextId, mSrcId1, mSrcId2);
}
+ @NonNull
@Override
public String toString() {
return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -66,14 +70,14 @@ public class TextMerge implements Operation {
* @param srcId1 source text 1
* @param srcId2 source text 2
*/
- public static void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) {
+ public static void apply(@NonNull WireBuffer buffer, int textId, int srcId1, int srcId2) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeInt(srcId1);
buffer.writeInt(srcId2);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
int srcId1 = buffer.readInt();
int srcId2 = buffer.readInt();
@@ -81,7 +85,7 @@ public class TextMerge implements Operation {
operations.add(new TextMerge(textId, srcId1, srcId2));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Merge two string into one")
.field(DocumentedOperation.INT, "textId", "id of the text")
@@ -90,12 +94,13 @@ public class TextMerge implements Operation {
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
String str1 = context.getText(mSrcId1);
String str2 = context.getText(mSrcId2);
context.loadText(mTextId, str1 + str2);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index 845f25d0cd67..1e90ab128e93 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
@@ -49,25 +51,28 @@ public class Theme implements RemoteComposeOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mTheme);
}
+ @NonNull
@Override
public String toString() {
return "SET_THEME " + mTheme;
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.setTheme(mTheme);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return indent + toString();
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -76,17 +81,17 @@ public class Theme implements RemoteComposeOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int theme) {
+ public static void apply(@NonNull WireBuffer buffer, int theme) {
buffer.start(OP_CODE);
buffer.writeInt(theme);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int theme = buffer.readInt();
operations.add(new Theme(theme));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Set a theme")
.field(INT, "THEME", "theme id")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
new file mode 100644
index 000000000000..b25a7f6ea09d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
@@ -0,0 +1,599 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.TouchListener;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
+import com.android.internal.widget.remotecompose.core.operations.utilities.touch.VelocityEasing;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Operation to deal with Touch handling (typically on canvas) This support handling of many typical
+ * touch behaviours. Including animating to Notched, positions. and tweaking the dynamics of the
+ * animation.
+ */
+public class TouchExpression implements Operation, VariableSupport, TouchListener {
+ private static final int OP_CODE = Operations.TOUCH_EXPRESSION;
+ private static final String CLASS_NAME = "TouchExpression";
+ private float mDefValue;
+ private float mOutDefValue;
+ public int mId;
+ public float[] mSrcExp;
+ int mMode = 1; // 0 = delta, 1 = absolute
+ float mMax = 1;
+ float mMin = 1;
+ float mOutMax = 1;
+ float mOutMin = 1;
+ float mValue = 0;
+ boolean mUnmodified = true;
+ public float[] mPreCalcValue;
+ private float mLastChange = Float.NaN;
+ private float mLastCalculatedValue = Float.NaN;
+ AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+ public static final int MAX_EXPRESSION_SIZE = 32;
+ private VelocityEasing mEasyTouch = new VelocityEasing();
+ private boolean mEasingToStop = false;
+ private float mTouchUpTime = 0;
+ private float mCurrentValue = Float.NaN;
+ private boolean mTouchDown = false;
+ float mMaxTime = 1;
+ float mMaxAcceleration = 5;
+ float mMaxVelocity = 7;
+ int mStopMode = 0;
+ boolean mWrapMode = false;
+ float[] mNotches;
+ float[] mStopSpec;
+ int mTouchEffects;
+ float mVelocityId;
+
+ public static final int STOP_GENTLY = 0;
+ public static final int STOP_ENDS = 2;
+ public static final int STOP_INSTANTLY = 1;
+ public static final int STOP_NOTCHES_EVEN = 3;
+ public static final int STOP_NOTCHES_PERCENTS = 4;
+ public static final int STOP_NOTCHES_ABSOLUTE = 5;
+ public static final int STOP_ABSOLUTE_POS = 6;
+
+ public TouchExpression(
+ int id,
+ float[] exp,
+ float defValue,
+ float min,
+ float max,
+ int touchEffects,
+ float velocityId,
+ int stopMode,
+ float[] stopSpec,
+ float[] easingSpec) {
+ this.mId = id;
+ this.mSrcExp = exp;
+ mOutDefValue = mDefValue = defValue;
+ mMode = STOP_ABSOLUTE_POS == stopMode ? 1 : 0;
+ mOutMax = mMax = max;
+ mTouchEffects = touchEffects;
+ mVelocityId = velocityId;
+ if (Float.isNaN(min) && Utils.idFromNan(min) == 0) {
+ mWrapMode = true;
+ } else {
+ mOutMin = mMin = min;
+ }
+ mStopMode = stopMode;
+ mStopSpec = stopSpec;
+ if (easingSpec != null) {
+ Utils.log("easingSpec " + Arrays.toString(easingSpec));
+ if (easingSpec.length >= 4) {
+ if (Float.floatToRawIntBits(easingSpec[0]) == 0) {
+ Utils.log("easingSpec[2] " + easingSpec[2]);
+ mMaxTime = easingSpec[1];
+ mMaxAcceleration = easingSpec[2];
+ mMaxVelocity = easingSpec[3];
+ }
+ }
+ }
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+
+ if (mPreCalcValue == null || mPreCalcValue.length != mSrcExp.length) {
+ mPreCalcValue = new float[mSrcExp.length];
+ }
+ if (Float.isNaN(mMax)) {
+ mOutMax = context.getFloat(Utils.idFromNan(mMax));
+ }
+ if (Float.isNaN(mMin)) {
+ mOutMin = context.getFloat(Utils.idFromNan(mMin));
+ }
+ if (Float.isNaN(mDefValue)) {
+ mOutDefValue = context.getFloat(Utils.idFromNan(mDefValue));
+ }
+
+ boolean value_changed = false;
+ for (int i = 0; i < mSrcExp.length; i++) {
+ float v = mSrcExp[i];
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v)) {
+ float newValue = context.getFloat(Utils.idFromNan(v));
+
+ mPreCalcValue[i] = newValue;
+
+ } else {
+ mPreCalcValue[i] = mSrcExp[i];
+ }
+ }
+ float v = mLastCalculatedValue;
+ if (value_changed) { // inputs changed check if output changed
+ v = mExp.eval(mPreCalcValue, mPreCalcValue.length);
+ if (v != mLastCalculatedValue) {
+ mLastChange = context.getAnimationTime();
+ mLastCalculatedValue = v;
+ } else {
+ value_changed = false;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mMax)) {
+ context.listensTo(Utils.idFromNan(mMax), this);
+ }
+ if (Float.isNaN(mMin)) {
+ context.listensTo(Utils.idFromNan(mMin), this);
+ }
+ if (Float.isNaN(mDefValue)) {
+ context.listensTo(Utils.idFromNan(mDefValue), this);
+ }
+ context.addTouchListener(this);
+ for (float v : mSrcExp) {
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
+ && !NanMap.isDataVariable(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+
+ private float wrap(float pos) {
+ if (!mWrapMode) {
+ return pos;
+ }
+ pos = pos % mOutMax;
+ if (pos < 0) {
+ pos += mOutMax;
+ }
+ return pos;
+ }
+
+ private float getStopPosition(float pos, float slope) {
+ float target = pos + slope / mMaxAcceleration;
+ if (mWrapMode) {
+ pos = wrap(pos);
+ target = pos += +slope / mMaxAcceleration;
+ } else {
+ target = Math.max(Math.min(target, mOutMax), mOutMin);
+ }
+ float[] positions = new float[mStopSpec.length];
+ float min = (mWrapMode) ? 0 : mOutMin;
+
+ switch (mStopMode) {
+ case STOP_ENDS:
+ return ((pos + slope) > (mOutMax + min) / 2) ? mOutMax : min;
+ case STOP_INSTANTLY:
+ return pos;
+ case STOP_NOTCHES_EVEN:
+ int evenSpacing = (int) mStopSpec[0];
+ float step = (mOutMax - min) / evenSpacing;
+
+ float notch = min + step * (int) (0.5f + (target - mOutMin) / step);
+
+ notch = Math.max(Math.min(notch, mOutMax), min);
+ return notch;
+ case STOP_NOTCHES_PERCENTS:
+ positions = new float[mStopSpec.length];
+ float minPos = min;
+ float minPosDist = Math.abs(mOutMin - target);
+ for (int i = 0; i < positions.length; i++) {
+ float p = mOutMin + mStopSpec[i] * (mOutMax - mOutMin);
+ float dist = Math.abs(p - target);
+ if (minPosDist > dist) {
+ minPosDist = dist;
+ minPos = p;
+ }
+ }
+ return minPos;
+ case STOP_NOTCHES_ABSOLUTE:
+ positions = mStopSpec;
+ minPos = mOutMin;
+ minPosDist = Math.abs(mOutMin - target);
+ for (int i = 0; i < positions.length; i++) {
+ float dist = Math.abs(positions[i] - target);
+ if (minPosDist > dist) {
+ minPosDist = dist;
+ minPos = positions[i];
+ }
+ }
+ return minPos;
+ case STOP_GENTLY:
+ default:
+ return target;
+ }
+ }
+
+ void haptic(RemoteContext context) {
+ int touch = ((mTouchEffects) & 0xFF);
+ if ((mTouchEffects & (1 << 15)) != 0) {
+ touch = context.getInteger(mTouchEffects & 0x7FFF);
+ }
+
+ context.hapticEffect(touch);
+ }
+
+ float mLastValue = 0;
+
+ void crossNotchCheck(RemoteContext context) {
+ float prev = mLastValue;
+ float next = mCurrentValue;
+ mLastValue = next;
+
+ // System.out.println(mStopMode + " " + prev + " -> " + next);
+ float min = (mWrapMode) ? 0 : mOutMin;
+ float max = mOutMax;
+
+ switch (mStopMode) {
+ case STOP_ENDS:
+ if (((min - prev) * (max - prev) < 0) ^ ((min - next) * (max - next)) < 0) {
+ haptic(context);
+ }
+ break;
+ case STOP_INSTANTLY:
+ haptic(context);
+ break;
+ case STOP_NOTCHES_EVEN:
+ int evenSpacing = (int) mStopSpec[0];
+ float step = (max - min) / evenSpacing;
+ if ((int) ((prev - min) / step) != (int) ((next - min) / step)) {
+ haptic(context);
+ }
+ break;
+ case STOP_NOTCHES_PERCENTS:
+ for (int i = 0; i < mStopSpec.length; i++) {
+ float p = mOutMin + mStopSpec[i] * (mOutMax - mOutMin);
+ if ((prev - p) * (next - p) < 0) {
+ haptic(context);
+ }
+ }
+ break;
+ case STOP_NOTCHES_ABSOLUTE:
+ for (int i = 0; i < mStopSpec.length; i++) {
+ float p = mStopSpec[i];
+ if ((prev - p) * (next - p) < 0) {
+ haptic(context);
+ }
+ }
+ break;
+ case STOP_GENTLY:
+ }
+ }
+
+ float mScrLeft, mScrRight, mScrTop, mScrBottom;
+
+ @Override
+ public void apply(RemoteContext context) {
+ Component comp = context.lastComponent;
+ if (comp != null) {
+ float x = comp.getX();
+ float y = comp.getY();
+ float w = comp.getWidth();
+ float h = comp.getHeight();
+ comp = comp.getParent();
+ while (comp != null) {
+ x += comp.getX();
+ y += comp.getY();
+ comp = comp.getParent();
+ }
+ mScrLeft = x;
+ mScrTop = y;
+ mScrRight = w + x;
+ mScrBottom = h + y;
+ }
+ updateVariables(context);
+ if (mUnmodified) {
+ mCurrentValue = mOutDefValue;
+
+ context.loadFloat(mId, wrap(mCurrentValue));
+ return;
+ }
+ if (mEasingToStop) {
+ float time = context.getAnimationTime() - mTouchUpTime;
+ float value = mEasyTouch.getPos(time);
+ mCurrentValue = value;
+ value = wrap(value);
+ context.loadFloat(mId, value);
+ if (mEasyTouch.getDuration() < time) {
+ mEasingToStop = false;
+ }
+ crossNotchCheck(context);
+ return;
+ }
+ if (mTouchDown) {
+ float value =
+ mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length);
+ if (mMode == 0) {
+ value = mValueAtDown + (value - mDownTouchValue);
+ }
+ if (mWrapMode) {
+ value = wrap(value);
+ } else {
+ value = Math.min(Math.max(value, mOutMin), mOutMax);
+ }
+ mCurrentValue = value;
+ }
+ crossNotchCheck(context);
+ context.loadFloat(mId, wrap(mCurrentValue));
+ }
+
+ float mValueAtDown; // The currently "displayed" value at down
+ float mDownTouchValue; // The calculated value at down
+
+ @Override
+ public void touchDown(RemoteContext context, float x, float y) {
+
+ if (!(x >= mScrLeft && x <= mScrRight && y >= mScrTop && y <= mScrBottom)) {
+ Utils.log("NOT IN WINDOW " + x + ", " + y + " " + mScrLeft + ", " + mScrTop);
+ return;
+ }
+ mTouchDown = true;
+ mUnmodified = false;
+ if (mMode == 0) {
+ mValueAtDown = context.getFloat(mId);
+ mDownTouchValue =
+ mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length);
+ }
+ }
+
+ @Override
+ public void touchUp(RemoteContext context, float x, float y, float dx, float dy) {
+ // calculate the slope (using small changes)
+ if (!mTouchDown) {
+ return;
+ }
+ mTouchDown = false;
+ float dt = 0.0001f;
+ if (mStopMode == STOP_INSTANTLY) {
+ return;
+ }
+ float v = mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length);
+ for (int i = 0; i < mSrcExp.length; i++) {
+ if (Float.isNaN(mSrcExp[i])) {
+ int id = Utils.idFromNan(mSrcExp[i]);
+ if (id == RemoteContext.ID_TOUCH_POS_X) {
+ mPreCalcValue[i] = x + dx * dt;
+ } else if (id == RemoteContext.ID_TOUCH_POS_Y) {
+ mPreCalcValue[i] = y + dy * dt;
+ }
+ }
+ }
+ float vdt = mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length);
+ float slope = (vdt - v) / dt; // the rate of change with respect to the dx,dy movement
+ float value = context.getFloat(mId);
+
+ mTouchUpTime = context.getAnimationTime();
+
+ float dest = getStopPosition(value, slope);
+ mEasyTouch.config(value, dest, slope, mMaxTime, mMaxAcceleration, mMaxVelocity, null);
+ mEasingToStop = true;
+ }
+
+ @Override
+ public void touchDrag(RemoteContext context, float x, float y) {
+ if (!mTouchDown) {
+ return;
+ }
+ apply(context);
+ context.getDocument().getRootLayoutComponent().needsRepaint();
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(
+ buffer,
+ mId,
+ mValue,
+ mMin,
+ mMax,
+ mVelocityId,
+ mTouchEffects,
+ mSrcExp,
+ mStopMode,
+ mNotches,
+ null);
+ }
+
+ @Override
+ public String toString() {
+ String[] labels = new String[mSrcExp.length];
+ for (int i = 0; i < mSrcExp.length; i++) {
+ if (Float.isNaN(mSrcExp[i])) {
+ labels[i] = "[" + Utils.idStringFromNan(mSrcExp[i]) + "]";
+ }
+ }
+ if (mPreCalcValue == null) {
+ return CLASS_NAME
+ + "["
+ + mId
+ + "] = ("
+ + AnimatedFloatExpression.toString(mSrcExp, labels)
+ + ")";
+ }
+ return CLASS_NAME
+ + "["
+ + mId
+ + "] = ("
+ + AnimatedFloatExpression.toString(mPreCalcValue, labels)
+ + ")";
+ }
+
+ // ===================== static ======================
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer The buffer to write to
+ * @param id the id of the resulting float
+ * @param value the float expression array
+ */
+ public static void apply(
+ WireBuffer buffer,
+ int id,
+ float value,
+ float min,
+ float max,
+ float velocityId,
+ int touchEffects,
+ float[] exp,
+ int touchMode,
+ float[] touchSpec,
+ float[] easingSpec) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeFloat(value);
+ buffer.writeFloat(min);
+ buffer.writeFloat(max);
+ buffer.writeFloat(velocityId);
+ buffer.writeInt(touchEffects);
+ buffer.writeInt(exp.length);
+ for (float v : exp) {
+ buffer.writeFloat(v);
+ }
+ int len = 0;
+ if (touchSpec != null) {
+ len = touchSpec.length;
+ }
+ buffer.writeInt((touchMode << 16) | len);
+ for (int i = 0; i < len; i++) {
+ buffer.writeFloat(touchSpec[i]);
+ }
+
+ if (easingSpec != null) {
+ len = easingSpec.length;
+ } else {
+ len = 0;
+ }
+ buffer.writeInt(len);
+ for (int i = 0; i < len; i++) {
+ buffer.writeFloat(easingSpec[i]);
+ }
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ float startValue = buffer.readFloat();
+ float min = buffer.readFloat();
+ float max = buffer.readFloat();
+ float velocityId = buffer.readFloat(); // TODO future support
+ int touchEffects = buffer.readInt();
+ int len = buffer.readInt();
+ int valueLen = len & 0xFFFF;
+ if (valueLen > MAX_EXPRESSION_SIZE) {
+ throw new RuntimeException("Float expression to long");
+ }
+ float[] exp = new float[valueLen];
+ for (int i = 0; i < exp.length; i++) {
+ exp[i] = buffer.readFloat();
+ }
+ int stopLogic = buffer.readInt();
+ int stopLen = stopLogic & 0xFFFF;
+ int stopMode = stopLogic >> 16;
+
+ Utils.log("stopMode " + stopMode + " stopLen " + stopLen);
+ float[] stopsData = new float[stopLen];
+ for (int i = 0; i < stopsData.length; i++) {
+ stopsData[i] = buffer.readFloat();
+ }
+ int easingLen = buffer.readInt();
+
+ float[] easingData = new float[easingLen];
+ for (int i = 0; i < easingData.length; i++) {
+ easingData[i] = buffer.readFloat();
+ }
+
+ operations.add(
+ new TouchExpression(
+ id,
+ exp,
+ startValue,
+ min,
+ max,
+ touchEffects,
+ velocityId,
+ stopMode,
+ stopsData,
+ easingData));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("A Float expression")
+ .field(INT, "id", "The id of the Color")
+ .field(SHORT, "expression_length", "expression length")
+ .field(SHORT, "animation_length", "animation description length")
+ .field(
+ FLOAT_ARRAY,
+ "expression",
+ "expression_length",
+ "Sequence of Floats representing and expression")
+ .field(
+ FLOAT_ARRAY,
+ "AnimationSpec",
+ "animation_length",
+ "Sequence of Floats representing animation curve")
+ .field(FLOAT, "duration", "> time in sec")
+ .field(INT, "bits", "> WRAP|INITALVALUE | TYPE ")
+ .field(FLOAT_ARRAY, "spec", "> [SPEC PARAMETERS] ")
+ .field(FLOAT, "initialValue", "> [Initial value] ")
+ .field(FLOAT, "wrapValue", "> [Wrap value] ");
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 8ebb40cab806..03f7e0563eeb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+import android.annotation.NonNull;
+
/** Utilities to be used across all core operations */
public class Utils {
public static float asNan(int v) {
@@ -30,11 +32,13 @@ public class Utils {
return v - 0x100000000L;
}
+ @NonNull
public static String idStringFromNan(float value) {
int b = Float.floatToRawIntBits(value) & 0x3FFFFF;
return idString(b);
}
+ @NonNull
public static String idString(int b) {
return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b;
}
@@ -50,7 +54,8 @@ public class Utils {
* @param n
* @return
*/
- public static String trimString(String str, int n) {
+ @NonNull
+ public static String trimString(@NonNull String str, int n) {
if (str.length() > n) {
str = str.substring(0, n - 3) + "...";
}
@@ -145,6 +150,7 @@ public class Utils {
* @param color
* @return
*/
+ @NonNull
public static String colorInt(int color) {
String str = "000000000000" + Integer.toHexString(color);
return "0x" + str.substring(str.length() - 8);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java
new file mode 100644
index 000000000000..e789710bb113
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/AnimatableValue.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.GeneralEasing;
+
+public class AnimatableValue {
+ boolean mIsVariable = false;
+ int mId = 0;
+ float mValue = 0f;
+
+ boolean mAnimate = false;
+ long mAnimateTargetTime = 0;
+ float mAnimateDuration = 300f;
+ float mTargetRotationX;
+ float mStartRotationX;
+
+ int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
+ FloatAnimation mMotionEasing;
+
+ public AnimatableValue(float value) {
+ if (Utils.isVariable(value)) {
+ mId = Utils.idFromNan(value);
+ mIsVariable = true;
+ } else {
+ mValue = value;
+ }
+ }
+
+ public float getValue() {
+ return mValue;
+ }
+
+ public float evaluate(PaintContext context) {
+ if (!mIsVariable) {
+ return mValue;
+ }
+ float value = context.getContext().mRemoteComposeState.getFloat(mId);
+
+ if (value != mValue && !mAnimate) {
+ // animate
+ mStartRotationX = mValue;
+ mTargetRotationX = value;
+ mAnimate = true;
+ mAnimateTargetTime = System.currentTimeMillis();
+ mMotionEasing =
+ new FloatAnimation(
+ mMotionEasingType, mAnimateDuration / 1000f, null, 0f, Float.NaN);
+ mMotionEasing.setTargetValue(1f);
+ }
+ if (mAnimate) {
+ float elapsed = System.currentTimeMillis() - mAnimateTargetTime;
+ float p = mMotionEasing.get(elapsed / mAnimateDuration);
+ mValue = (1 - p) * mStartRotationX + p * mTargetRotationX;
+ if (p >= 1f) {
+ mAnimate = false;
+ }
+ } else {
+ mValue = mTargetRotationX;
+ }
+
+ return mValue;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
index 9d80d3cc40b0..988651895bae 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.WireBuffer;
@@ -38,6 +40,7 @@ public class CanvasContent extends Component implements ComponentStartOperation
super(parent, componentId, animationId, x, y, width, height);
}
+ @NonNull
public static String name() {
return "CanvasContent";
}
@@ -46,29 +49,30 @@ public class CanvasContent extends Component implements ComponentStartOperation
return Operations.LAYOUT_CANVAS_CONTENT;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "CANVAS_CONTENT";
}
- public static void apply(WireBuffer buffer, int componentId) {
+ public static void apply(@NonNull WireBuffer buffer, int componentId) {
buffer.start(Operations.LAYOUT_CANVAS_CONTENT);
buffer.writeInt(componentId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
.description("Container for canvas commands.");
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickHandler.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickHandler.java
new file mode 100644
index 000000000000..0ca72fae9eea
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+
+/** Interface to represent operations that can handle click events */
+public interface ClickHandler {
+
+ /**
+ * callback for a click event
+ *
+ * @param context the current context
+ * @param document the current document
+ * @param component the component on which the click has been received
+ * @param x the x position of the click in document coordinates
+ * @param y the y position of the click in document coordinates
+ */
+ void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index d5ff07df54cd..b567538b1c4e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -37,7 +40,7 @@ import java.util.List;
/** Represents a click modifier + actions */
public class ClickModifierOperation extends PaintOperation
- implements ModifierOperation, DecoratorComponent {
+ implements ModifierOperation, DecoratorComponent, ClickHandler {
private static final int OP_CODE = Operations.MODIFIER_CLICK;
long mAnimateRippleStart = 0;
@@ -48,9 +51,9 @@ public class ClickModifierOperation extends PaintOperation
float mWidth = 0;
float mHeight = 0;
- public float[] locationInWindow = new float[2];
+ @NonNull public float[] locationInWindow = new float[2];
- PaintBundle mPaint = new PaintBundle();
+ @NonNull PaintBundle mPaint = new PaintBundle();
public void animateRipple(float x, float y) {
mAnimateRippleStart = System.currentTimeMillis();
@@ -58,17 +61,19 @@ public class ClickModifierOperation extends PaintOperation
mAnimateRippleY = y;
}
- public ArrayList<Operation> mList = new ArrayList<>();
+ @NonNull public ArrayList<Operation> mList = new ArrayList<>();
+ @NonNull
public ArrayList<Operation> getList() {
return mList;
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
+ @NonNull
@Override
public String toString() {
return "ClickModifier";
@@ -83,13 +88,14 @@ public class ClickModifierOperation extends PaintOperation
}
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
if (mAnimateRippleStart == 0) {
return;
}
@@ -137,7 +143,7 @@ public class ClickModifierOperation extends PaintOperation
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, "CLICK_MODIFIER");
for (Operation o : mList) {
if (o instanceof ActionOperation) {
@@ -148,7 +154,11 @@ public class ClickModifierOperation extends PaintOperation
@Override
public void onClick(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ RemoteContext context,
+ CoreDocument document,
+ @NonNull Component component,
+ float x,
+ float y) {
if (!component.isVisible()) {
return;
}
@@ -163,19 +173,20 @@ public class ClickModifierOperation extends PaintOperation
}
}
+ @NonNull
public static String name() {
return "ClickModifier";
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new ClickModifierOperation());
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, name())
.description(
"Click modifier. This operation contains"
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index 96dffca2042f..f4f4ee2a3416 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -31,7 +34,6 @@ import com.android.internal.widget.remotecompose.core.operations.layout.animatio
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Measurable;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
-import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
@@ -52,16 +54,23 @@ public class Component extends PaintOperation implements Measurable, Serializabl
protected int mAnimationId = -1;
public Visibility mVisibility = Visibility.VISIBLE;
public Visibility mScheduledVisibility = Visibility.VISIBLE;
- public ArrayList<Operation> mList = new ArrayList<>();
+ @NonNull public ArrayList<Operation> mList = new ArrayList<>();
public PaintOperation mPreTranslate;
public boolean mNeedsMeasure = true;
public boolean mNeedsRepaint = false;
- public AnimateMeasure mAnimateMeasure;
- public AnimationSpec mAnimationSpec = new AnimationSpec();
+ @Nullable public AnimateMeasure mAnimateMeasure;
+ @NonNull public AnimationSpec mAnimationSpec = new AnimationSpec();
public boolean mFirstLayout = true;
- PaintBundle mPaint = new PaintBundle();
- protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
+ @NonNull PaintBundle mPaint = new PaintBundle();
+ @NonNull protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
+
+ protected float mZIndex = 0f;
+ public float getZIndex() {
+ return mZIndex;
+ }
+
+ @NonNull
public ArrayList<Operation> getList() {
return mList;
}
@@ -115,7 +124,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
*
* @param context the current context
*/
- private void updateComponentValues(RemoteContext context) {
+ private void updateComponentValues(@NonNull RemoteContext context) {
if (DEBUG) {
System.out.println(
"UPDATE COMPONENT VALUES ("
@@ -172,7 +181,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
this(parent, componentId, -1, x, y, width, height);
}
- public Component(Component component) {
+ public Component(@NonNull Component component) {
this(
component.mParent,
component.mComponentId,
@@ -212,7 +221,10 @@ public class Component extends PaintOperation implements Measurable, Serializabl
*
* @param context the current context
*/
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
+ Component prev = context.lastComponent;
+ context.lastComponent = this;
+
if (!mComponentValues.isEmpty()) {
updateComponentValues(context);
}
@@ -224,6 +236,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
o.apply(context);
}
}
+ context.lastComponent = prev;
}
public void addComponentValue(ComponentValue v) {
@@ -283,14 +296,14 @@ public class Component extends PaintOperation implements Measurable, Serializabl
float maxWidth,
float minHeight,
float maxHeight,
- MeasurePass measure) {
+ @NonNull MeasurePass measure) {
ComponentMeasure m = measure.get(this);
m.setW(mWidth);
m.setH(mHeight);
}
@Override
- public void layout(RemoteContext context, MeasurePass measure) {
+ public void layout(@NonNull RemoteContext context, @NonNull MeasurePass measure) {
ComponentMeasure m = measure.get(this);
if (!mFirstLayout
&& context.isAnimationEnabled()
@@ -332,7 +345,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
mFirstLayout = false;
}
- public float[] locationInWindow = new float[2];
+ @NonNull public float[] locationInWindow = new float[2];
public boolean contains(float x, float y) {
locationInWindow[0] = 0f;
@@ -353,13 +366,57 @@ public class Component extends PaintOperation implements Measurable, Serializabl
if (op instanceof Component) {
((Component) op).onClick(context, document, x, y);
}
- if (op instanceof ComponentModifiers) {
- ((ComponentModifiers) op).onClick(context, document, this, x, y);
+ if (op instanceof ClickHandler) {
+ ((ClickHandler) op).onClick(context, document, this, x, y);
+ }
+ }
+ }
+
+ public void onTouchDown(RemoteContext context, CoreDocument document, float x, float y) {
+ if (!contains(x, y)) {
+ return;
+ }
+ for (Operation op : mList) {
+ if (op instanceof Component) {
+ ((Component) op).onTouchDown(context, document, x, y);
+ }
+ if (op instanceof TouchHandler) {
+ ((TouchHandler) op).onTouchDown(context, document, this, x, y);
+ }
+ }
+ }
+
+ public void onTouchUp(
+ RemoteContext context, CoreDocument document, float x, float y, boolean force) {
+ if (!force && !contains(x, y)) {
+ return;
+ }
+ for (Operation op : mList) {
+ if (op instanceof Component) {
+ ((Component) op).onTouchUp(context, document, x, y, force);
+ }
+ if (op instanceof TouchHandler) {
+ ((TouchHandler) op).onTouchUp(context, document, this, x, y);
+ }
+ }
+ }
+
+ public void onTouchCancel(
+ RemoteContext context, CoreDocument document, float x, float y, boolean force) {
+ if (!force && !contains(x, y)) {
+ return;
+ }
+ for (Operation op : mList) {
+ if (op instanceof Component) {
+ ((Component) op).onTouchCancel(context, document, x, y, force);
+ }
+ if (op instanceof TouchHandler) {
+ ((TouchHandler) op).onTouchCancel(context, document, this, x, y);
}
}
}
- public void getLocationInWindow(float[] value) {
+ public void getLocationInWindow(@NonNull float[] value) {
value[0] += mX;
value[1] += mY;
if (mParent != null) {
@@ -372,6 +429,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
}
+ @NonNull
@Override
public String toString() {
return "COMPONENT(<"
@@ -393,14 +451,14 @@ public class Component extends PaintOperation implements Measurable, Serializabl
+ ") ";
}
+ @NonNull
protected String getSerializedName() {
return "COMPONENT";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(
- indent,
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ String content =
getSerializedName()
+ " ["
+ mComponentId
@@ -416,9 +474,9 @@ public class Component extends PaintOperation implements Measurable, Serializabl
+ ", "
+ mHeight
+ "] "
- + mVisibility
- // + " [" + mNeedsMeasure + ", " + mNeedsRepaint + "]"
- );
+ + mVisibility;
+ // + " [" + mNeedsMeasure + ", " + mNeedsRepaint + "]"
+ serializer.append(indent, content);
}
@Override
@@ -427,6 +485,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
/** Returns the top-level RootLayoutComponent */
+ @NonNull
public RootLayoutComponent getRoot() throws Exception {
if (this instanceof RootLayoutComponent) {
return (RootLayoutComponent) this;
@@ -441,6 +500,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
return (RootLayoutComponent) p;
}
+ @NonNull
@Override
public String deepToString(String indent) {
StringBuilder builder = new StringBuilder();
@@ -477,6 +537,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
}
+ @NonNull
public String content() {
StringBuilder builder = new StringBuilder();
for (Operation op : mList) {
@@ -487,6 +548,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
return builder.toString();
}
+ @NonNull
public String textContent() {
StringBuilder builder = new StringBuilder();
for (Operation ignored : mList) {
@@ -499,7 +561,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
return builder.toString();
}
- public void debugBox(Component component, PaintContext context) {
+ public void debugBox(@NonNull Component component, @NonNull PaintContext context) {
float width = component.mWidth;
float height = component.mHeight;
@@ -536,13 +598,15 @@ public class Component extends PaintOperation implements Measurable, Serializabl
return 0f;
}
- public void paintingComponent(PaintContext context) {
+ public void paintingComponent(@NonNull PaintContext context) {
if (mPreTranslate != null) {
mPreTranslate.paint(context);
}
+ Component prev = context.getContext().lastComponent;
+ context.getContext().lastComponent = this;
context.save();
context.translate(mX, mY);
- if (context.isDebug()) {
+ if (context.isVisualDebug()) {
debugBox(this, context);
}
for (Operation op : mList) {
@@ -554,9 +618,10 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
}
context.restore();
+ context.getContext().lastComponent = prev;
}
- public boolean applyAnimationAsNeeded(PaintContext context) {
+ public boolean applyAnimationAsNeeded(@NonNull PaintContext context) {
if (context.isAnimationEnabled() && mAnimateMeasure != null) {
mAnimateMeasure.apply(context);
needsRepaint();
@@ -566,8 +631,8 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
@Override
- public void paint(PaintContext context) {
- if (context.isDebug()) {
+ public void paint(@NonNull PaintContext context) {
+ if (context.isVisualDebug()) {
context.save();
context.translate(mX, mY);
context.savePaint();
@@ -594,7 +659,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
paintingComponent(context);
}
- public void getComponents(ArrayList<Component> components) {
+ public void getComponents(@NonNull ArrayList<Component> components) {
for (Operation op : mList) {
if (op instanceof Component) {
components.add((Component) op);
@@ -602,7 +667,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
}
- public void getData(ArrayList<TextData> data) {
+ public void getData(@NonNull ArrayList<TextData> data) {
for (Operation op : mList) {
if (op instanceof TextData) {
data.add((TextData) op);
@@ -631,6 +696,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
return mNeedsRepaint;
}
+ @Nullable
public Component getComponent(int cid) {
if (mComponentId == cid || mAnimationId == cid) {
return this;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
index c83ee487a8ea..f370e20677e8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -26,10 +29,11 @@ import java.util.List;
public class ComponentEnd implements Operation {
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
+ @NonNull
@Override
public String toString() {
return "COMPONENT_END";
@@ -40,11 +44,13 @@ public class ComponentEnd implements Operation {
// nothing
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
+ @NonNull
public static String name() {
return "ComponentEnd";
}
@@ -53,7 +59,7 @@ public class ComponentEnd implements Operation {
return Operations.COMPONENT_END;
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(Operations.COMPONENT_END);
}
@@ -61,11 +67,11 @@ public class ComponentEnd implements Operation {
return 1 + 4 + 4 + 4;
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new ComponentEnd());
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description(
"End tag for components / layouts. This operation marks the end"
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
index 72cc9b6d2613..f250d9ac4a01 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
@@ -18,6 +18,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -69,10 +72,11 @@ public class ComponentStart implements ComponentStartOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mType, mComponentId, mWidth, mHeight);
}
+ @NonNull
@Override
public String toString() {
return "COMPONENT_START (type "
@@ -90,8 +94,9 @@ public class ComponentStart implements ComponentStartOperation {
+ ")";
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@@ -119,6 +124,7 @@ public class ComponentStart implements ComponentStartOperation {
public static final int LAYOUT_ROW = 15;
public static final int LAYOUT_COLUMN = 16;
+ @NonNull
public static String typeDescription(int type) {
switch (type) {
case DEFAULT:
@@ -152,6 +158,7 @@ public class ComponentStart implements ComponentStartOperation {
}
}
+ @NonNull
public static String name() {
return "ComponentStart";
}
@@ -161,7 +168,7 @@ public class ComponentStart implements ComponentStartOperation {
}
public static void apply(
- WireBuffer buffer, int type, int componentId, float width, float height) {
+ @NonNull WireBuffer buffer, int type, int componentId, float width, float height) {
buffer.start(Operations.COMPONENT_START);
buffer.writeInt(type);
buffer.writeInt(componentId);
@@ -173,7 +180,7 @@ public class ComponentStart implements ComponentStartOperation {
return 1 + 4 + 4 + 4;
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int type = buffer.readInt();
int componentId = buffer.readInt();
float width = buffer.readFloat();
@@ -181,7 +188,7 @@ public class ComponentStart implements ComponentStartOperation {
operations.add(new ComponentStart(type, componentId, width, height));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description(
"Basic component encapsulating draw commands." + "This is not resizable.")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
index 314650fcd597..bb4311996df0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
@@ -15,7 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.RemoteContext;
/**
@@ -24,7 +23,4 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
*/
public interface DecoratorComponent {
void layout(RemoteContext context, float width, float height);
-
- void onClick(
- RemoteContext context, CoreDocument document, Component component, float x, float y);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index 8172502d092f..e0923dfb48fb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.operations.BitmapData;
@@ -25,18 +28,22 @@ import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DimensionModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
import java.util.ArrayList;
/** Component with modifiers and children */
public class LayoutComponent extends Component {
- protected WidthModifierOperation mWidthModifier = null;
- protected HeightModifierOperation mHeightModifier = null;
+ @Nullable protected WidthModifierOperation mWidthModifier = null;
+ @Nullable protected HeightModifierOperation mHeightModifier = null;
+ @Nullable protected ZIndexModifierOperation mZIndexModifier = null;
+ @Nullable protected GraphicsLayerModifierOperation mGraphicsLayerModifier = null;
// Margins
protected float mMarginLeft = 0f;
@@ -49,8 +56,10 @@ public class LayoutComponent extends Component {
protected float mPaddingTop = 0f;
protected float mPaddingBottom = 0f;
- protected ComponentModifiers mComponentModifiers = new ComponentModifiers();
- protected ArrayList<Component> mChildrenComponents = new ArrayList<>();
+ @NonNull protected ComponentModifiers mComponentModifiers = new ComponentModifiers();
+ @NonNull protected ArrayList<Component> mChildrenComponents = new ArrayList<>();
+
+ protected boolean mChildrenHaveZIndex = false;
public LayoutComponent(
Component parent,
@@ -95,15 +104,25 @@ public class LayoutComponent extends Component {
return mPaddingBottom;
}
+ @Nullable
public WidthModifierOperation getWidthModifier() {
return mWidthModifier;
}
+ @Nullable
public HeightModifierOperation getHeightModifier() {
return mHeightModifier;
}
- protected LayoutComponentContent mContent = null;
+ @Override
+ public float getZIndex() {
+ if (mZIndexModifier != null) {
+ return mZIndexModifier.getValue();
+ }
+ return mZIndex;
+ }
+
+ @Nullable protected LayoutComponentContent mContent = null;
// Should be removed after ImageLayout is in
private static final boolean USE_IMAGE_TEMP_FIX = true;
@@ -164,6 +183,9 @@ public class LayoutComponent extends Component {
for (Component c : mChildrenComponents) {
c.mParent = this;
mList.add(c);
+ if (c instanceof LayoutComponent && ((LayoutComponent) c).mZIndexModifier != null) {
+ mChildrenHaveZIndex = true;
+ }
}
mX = 0f;
@@ -209,6 +231,12 @@ public class LayoutComponent extends Component {
mHeightModifier = (HeightModifierOperation) op;
applyVerticalMargin = false;
}
+ if (op instanceof ZIndexModifierOperation) {
+ mZIndexModifier = (ZIndexModifierOperation) op;
+ }
+ if (op instanceof GraphicsLayerModifierOperation) {
+ mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op;
+ }
}
if (mWidthModifier == null) {
mWidthModifier = new WidthModifierOperation(DimensionModifierOperation.Type.WRAP);
@@ -220,24 +248,64 @@ public class LayoutComponent extends Component {
setHeight(computeModifierDefinedHeight());
}
+ @NonNull
@Override
public String toString() {
return "UNKNOWN LAYOUT_COMPONENT";
}
@Override
- public void paintingComponent(PaintContext context) {
+ public void paintingComponent(@NonNull PaintContext context) {
+ Component prev = context.getContext().lastComponent;
+ context.getContext().lastComponent = this;
context.save();
context.translate(mX, mY);
+ if (mGraphicsLayerModifier != null) {
+ context.startGraphicsLayer((int) getWidth(), (int) getHeight());
+ float scaleX = mGraphicsLayerModifier.getScaleX();
+ float scaleY = mGraphicsLayerModifier.getScaleY();
+ float rotationX = mGraphicsLayerModifier.getRotationX();
+ float rotationY = mGraphicsLayerModifier.getRotationY();
+ float rotationZ = mGraphicsLayerModifier.getRotationZ();
+ float shadowElevation = mGraphicsLayerModifier.getShadowElevation();
+ float transformOriginX = mGraphicsLayerModifier.getTransformOriginX();
+ float transformOriginY = mGraphicsLayerModifier.getTransformOriginY();
+ float alpha = mGraphicsLayerModifier.getAlpha();
+ int renderEffectId = mGraphicsLayerModifier.getRenderEffectId();
+ context.setGraphicsLayer(
+ scaleX,
+ scaleY,
+ rotationX,
+ rotationY,
+ rotationZ,
+ shadowElevation,
+ transformOriginX,
+ transformOriginY,
+ alpha,
+ renderEffectId);
+ }
mComponentModifiers.paint(context);
float tx = mPaddingLeft;
float ty = mPaddingTop;
context.translate(tx, ty);
- for (Component child : mChildrenComponents) {
- child.paint(context);
+ if (mChildrenHaveZIndex) {
+ // TODO -- should only sort when something has changed
+ ArrayList<Component> sorted = new ArrayList<Component>(mChildrenComponents);
+ sorted.sort((a, b) -> (int) (a.getZIndex() - b.getZIndex()));
+ for (Component child : sorted) {
+ child.paint(context);
+ }
+ } else {
+ for (Component child : mChildrenComponents) {
+ child.paint(context);
+ }
+ }
+ if (mGraphicsLayerModifier != null) {
+ context.endGraphicsLayer();
}
context.translate(-tx, -ty);
context.restore();
+ context.getContext().lastComponent = prev;
}
/** Traverse the modifiers to compute indicated dimension */
@@ -248,7 +316,8 @@ public class LayoutComponent extends Component {
for (Operation c : mComponentModifiers.getList()) {
if (c instanceof WidthModifierOperation) {
WidthModifierOperation o = (WidthModifierOperation) c;
- if (o.getType() == DimensionModifierOperation.Type.EXACT) {
+ if (o.getType() == DimensionModifierOperation.Type.EXACT
+ || o.getType() == DimensionModifierOperation.Type.EXACT_DP) {
w = o.getValue();
}
break;
@@ -291,7 +360,8 @@ public class LayoutComponent extends Component {
for (Operation c : mComponentModifiers.getList()) {
if (c instanceof HeightModifierOperation) {
HeightModifierOperation o = (HeightModifierOperation) c;
- if (o.getType() == DimensionModifierOperation.Type.EXACT) {
+ if (o.getType() == DimensionModifierOperation.Type.EXACT
+ || o.getType() == DimensionModifierOperation.Type.EXACT_DP) {
h = o.getValue();
}
break;
@@ -326,6 +396,7 @@ public class LayoutComponent extends Component {
return t + b;
}
+ @NonNull
public ArrayList<Component> getChildrenComponents() {
return mChildrenComponents;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
index 66fd053c4b5e..0a085b43401c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.WireBuffer;
@@ -38,6 +40,7 @@ public class LayoutComponentContent extends Component implements ComponentStartO
super(parent, componentId, animationId, x, y, width, height);
}
+ @NonNull
public static String name() {
return "LayoutContent";
}
@@ -46,22 +49,23 @@ public class LayoutComponentContent extends Component implements ComponentStartO
return Operations.LAYOUT_CONTENT;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "CONTENT";
}
- public static void apply(WireBuffer buffer, int componentId) {
+ public static void apply(@NonNull WireBuffer buffer, int componentId) {
buffer.start(Operations.LAYOUT_CONTENT);
buffer.writeInt(componentId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
.description(
@@ -71,7 +75,7 @@ public class LayoutComponentContent extends Component implements ComponentStartO
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
new file mode 100644
index 000000000000..c4df075bb9e8
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ListActionsOperation.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.ArrayList;
+
+public abstract class ListActionsOperation extends PaintOperation
+ implements ModifierOperation, DecoratorComponent {
+
+ String mOperationName;
+ float mWidth = 0;
+ float mHeight = 0;
+
+ private final float[] mLocationInWindow = new float[2];
+
+ public ListActionsOperation(String operationName) {
+ mOperationName = operationName;
+ }
+
+ public ArrayList<Operation> mList = new ArrayList<>();
+
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @Override
+ public String toString() {
+ return mOperationName;
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ for (Operation op : mList) {
+ if (op instanceof TextData) {
+ op.apply(context);
+ }
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {}
+
+ @Override
+ public void layout(RemoteContext context, float width, float height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(indent, mOperationName);
+ for (Operation o : mList) {
+ if (o instanceof ActionOperation) {
+ ((ActionOperation) o).serializeToString(indent + 1, serializer);
+ }
+ }
+ }
+
+ public boolean applyActions(
+ RemoteContext context,
+ CoreDocument document,
+ Component component,
+ float x,
+ float y,
+ boolean force) {
+ if (!force && !component.isVisible()) {
+ return false;
+ }
+ if (!force && !component.contains(x, y)) {
+ return false;
+ }
+ mLocationInWindow[0] = 0f;
+ mLocationInWindow[1] = 0f;
+ component.getLocationInWindow(mLocationInWindow);
+ for (Operation o : mList) {
+ if (o instanceof ActionOperation) {
+ ((ActionOperation) o).runAction(context, document, component, x, y);
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
index 3086d6aaa777..c90077b88782 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -26,10 +29,11 @@ import java.util.List;
public class LoopEnd implements Operation {
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
+ @NonNull
@Override
public String toString() {
return "LOOP_END";
@@ -40,11 +44,13 @@ public class LoopEnd implements Operation {
// nothing
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
+ @NonNull
public static String name() {
return "LoopEnd";
}
@@ -53,15 +59,15 @@ public class LoopEnd implements Operation {
return Operations.LOOP_END;
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(id());
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new LoopEnd());
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Operations", id(), name()).description("End tag for loops");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
index 691000810c20..eeaeafd284c0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -30,7 +33,7 @@ import java.util.List;
public class LoopOperation extends PaintOperation {
private static final int OP_CODE = Operations.LOOP_START;
- public ArrayList<Operation> mList = new ArrayList<>();
+ @NonNull public ArrayList<Operation> mList = new ArrayList<>();
int mIndexVariableId;
float mUntil = 12;
@@ -49,27 +52,30 @@ public class LoopOperation extends PaintOperation {
mIndexVariableId = indexId;
}
+ @NonNull
public ArrayList<Operation> getList() {
return mList;
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mUntil, mFrom, mStep, mIndexVariableId);
}
+ @NonNull
@Override
public String toString() {
return "LoopOperation";
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
if (mIndexVariableId == 0) {
for (float i = mFrom; i < mUntil; i += mStep) {
for (Operation op : mList) {
@@ -89,11 +95,13 @@ public class LoopOperation extends PaintOperation {
}
}
+ @NonNull
public static String name() {
return "Loop";
}
- public static void apply(WireBuffer buffer, float count, float from, float step, int indexId) {
+ public static void apply(
+ @NonNull WireBuffer buffer, float count, float from, float step, int indexId) {
buffer.start(OP_CODE);
buffer.writeFloat(count);
buffer.writeFloat(from);
@@ -101,7 +109,7 @@ public class LoopOperation extends PaintOperation {
buffer.writeInt(indexId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float count = buffer.readFloat();
float from = buffer.readFloat();
float step = buffer.readFloat();
@@ -109,7 +117,7 @@ public class LoopOperation extends PaintOperation {
operations.add(new LoopOperation(count, from, step, indexId));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Operations", OP_CODE, name())
.description("Loop. This operation execute" + " a list of action in a loop");
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
index fe726ac78791..bd8d1f0ba9dd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/OperationsListEnd.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -23,16 +26,17 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
-public class ClickModifierEnd implements Operation {
+public class OperationsListEnd implements Operation {
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
+ @NonNull
@Override
public String toString() {
- return "CLICK_END";
+ return "LIST_END";
}
@Override
@@ -40,31 +44,31 @@ public class ClickModifierEnd implements Operation {
// nothing
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
+ @NonNull
public static String name() {
- return "ClickModifierEnd";
+ return "ListEnd";
}
public static int id() {
- return Operations.MODIFIER_CLICK_END;
+ return Operations.OPERATIONS_LIST_END;
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(id());
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
- operations.add(new ClickModifierEnd());
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
+ operations.add(new OperationsListEnd());
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description(
- "End tag for click modifiers. This operation marks the end"
- + "of a click modifier");
+ .description("End tag for list of operations.");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index 680bb0b064d1..524ae59e70ec 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -34,7 +36,8 @@ import java.util.List;
/** Represents the root layout component. Entry point to the component tree layout/paint. */
public class RootLayoutComponent extends Component implements ComponentStartOperation {
- int mCurrentId = -1;
+ private int mCurrentId = -1;
+ private boolean mHasTouchListeners = false;
public RootLayoutComponent(
int componentId,
@@ -52,6 +55,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
super(parent, componentId, -1, x, y, width, height);
}
+ @NonNull
@Override
public String toString() {
return "ROOT "
@@ -69,7 +73,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent,
"ROOT ["
@@ -89,6 +93,15 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
/**
+ * Set the flag to traverse the tree when touch events happen
+ *
+ * @param value true to indicate that the tree has touch listeners
+ */
+ public void setHasTouchListeners(boolean value) {
+ mHasTouchListeners = value;
+ }
+
+ /**
* Traverse the hierarchy and assign generated ids to component without ids. Most components
* would already have ids assigned during the document creation, but this allow us to take care
* of any components added during the inflation.
@@ -100,7 +113,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
assignId(this);
}
- private void assignId(Component component) {
+ private void assignId(@NonNull Component component) {
if (component.mComponentId == -1) {
mCurrentId--;
component.mComponentId = mCurrentId;
@@ -113,7 +126,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
/** This will measure then layout the tree of components */
- public void layout(RemoteContext context) {
+ public void layout(@NonNull RemoteContext context) {
if (!mNeedsMeasure) {
return;
}
@@ -134,7 +147,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
mNeedsRepaint = false;
context.getContext().lastComponent = this;
context.save();
@@ -152,13 +165,15 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
context.restore();
}
+ @NonNull
public String displayHierarchy() {
StringSerializer serializer = new StringSerializer();
displayHierarchy(this, 0, serializer);
return serializer.toString();
}
- public void displayHierarchy(Component component, int indent, StringSerializer serializer) {
+ public void displayHierarchy(
+ @NonNull Component component, int indent, @NonNull StringSerializer serializer) {
component.serializeToString(indent, serializer);
for (Operation c : component.mList) {
if (c instanceof ComponentModifiers) {
@@ -171,6 +186,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
}
+ @NonNull
public static String name() {
return "RootLayout";
}
@@ -179,17 +195,17 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
return Operations.LAYOUT_ROOT;
}
- public static void apply(WireBuffer buffer, int componentId) {
+ public static void apply(@NonNull WireBuffer buffer, int componentId) {
buffer.start(Operations.LAYOUT_ROOT);
buffer.writeInt(componentId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
.description(
@@ -199,7 +215,11 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId);
}
+
+ public boolean hasTouchListeners() {
+ return mHasTouchListeners;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
new file mode 100644
index 000000000000..486efbd6e00f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchCancelModifierOperation.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Represents a touch cancel modifier + actions */
+public class TouchCancelModifierOperation extends ListActionsOperation implements TouchHandler {
+
+ private static final int OP_CODE = Operations.MODIFIER_TOUCH_CANCEL;
+
+ public TouchCancelModifierOperation() {
+ super("TOUCH_CANCEL_MODIFIER");
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer);
+ }
+
+ @Override
+ public String toString() {
+ return "TouchCancelModifier";
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ RootLayoutComponent root = context.getDocument().getRootLayoutComponent();
+ if (root != null) {
+ root.setHasTouchListeners(true);
+ }
+ super.apply(context);
+ }
+
+ @Override
+ public void onTouchDown(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ // nothing
+ }
+
+ @Override
+ public void onTouchUp(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ // nothing
+ }
+
+ @Override
+ public void onTouchCancel(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ applyActions(context, document, component, x, y, true);
+ }
+
+ public static String name() {
+ return "TouchCancelModifier";
+ }
+
+ public static void apply(WireBuffer buffer) {
+ buffer.start(OP_CODE);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ operations.add(new TouchCancelModifierOperation());
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Modifier Operations", OP_CODE, name())
+ .description(
+ "Touch cancel modifier. This operation contains"
+ + " a list of action executed on Touch cancel");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
new file mode 100644
index 000000000000..5d379fe01d61
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchDownModifierOperation.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Represents a touch down modifier + actions */
+public class TouchDownModifierOperation extends ListActionsOperation implements TouchHandler {
+
+ private static final int OP_CODE = Operations.MODIFIER_TOUCH_DOWN;
+
+ public TouchDownModifierOperation() {
+ super("TOUCH_DOWN_MODIFIER");
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer);
+ }
+
+ @Override
+ public String toString() {
+ return "TouchDownModifier";
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ RootLayoutComponent root = context.getDocument().getRootLayoutComponent();
+ if (root != null) {
+ root.setHasTouchListeners(true);
+ }
+ super.apply(context);
+ }
+
+ @Override
+ public void onTouchDown(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ if (applyActions(context, document, component, x, y, false)) {
+ document.appliedTouchOperation(component);
+ }
+ }
+
+ @Override
+ public void onTouchUp(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ // nothing
+ }
+
+ @Override
+ public void onTouchCancel(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ // nothing
+ }
+
+ public static String name() {
+ return "TouchModifier";
+ }
+
+ public static void apply(WireBuffer buffer) {
+ buffer.start(OP_CODE);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ operations.add(new TouchDownModifierOperation());
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Modifier Operations", OP_CODE, name())
+ .description(
+ "Touch down modifier. This operation contains"
+ + " a list of action executed on Touch down");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java
new file mode 100644
index 000000000000..5adfc33b5ef5
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+
+/** Interface to represent operations that can handle touch events */
+public interface TouchHandler {
+
+ /**
+ * callback for a touch down event
+ *
+ * @param context the current context
+ * @param document the current document
+ * @param component the component on which the touch has been received
+ * @param x the x position of the click in document coordinates
+ * @param y the y position of the click in document coordinates
+ */
+ void onTouchDown(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
+
+ /**
+ * callback for a touch up event
+ *
+ * @param context the current context
+ * @param document the current document
+ * @param component the component on which the touch has been received
+ * @param x the x position of the click in document coordinates
+ * @param y the y position of the click in document coordinates
+ */
+ void onTouchUp(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
+
+ /**
+ * callback for a touch cancel event
+ *
+ * @param context the current context
+ * @param document the current document
+ * @param component the component on which the touch has been received
+ * @param x the x position of the click in document coordinates
+ * @param y the y position of the click in document coordinates
+ */
+ void onTouchCancel(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
new file mode 100644
index 000000000000..263cc43d5e74
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/TouchUpModifierOperation.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Represents a touch up modifier + actions */
+public class TouchUpModifierOperation extends ListActionsOperation implements TouchHandler {
+
+ private static final int OP_CODE = Operations.MODIFIER_TOUCH_UP;
+
+ public TouchUpModifierOperation() {
+ super("TOUCH_UP_MODIFIER");
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer);
+ }
+
+ @Override
+ public String toString() {
+ return "TouchUpModifier";
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ RootLayoutComponent root = context.getDocument().getRootLayoutComponent();
+ if (root != null) {
+ root.setHasTouchListeners(true);
+ }
+ super.apply(context);
+ }
+
+ @Override
+ public void onTouchDown(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ // nothing
+ }
+
+ @Override
+ public void onTouchUp(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ applyActions(context, document, component, x, y, true);
+ }
+
+ @Override
+ public void onTouchCancel(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ // nothing
+ }
+
+ public static String name() {
+ return "TouchUpModifier";
+ }
+
+ public static void apply(WireBuffer buffer) {
+ buffer.start(OP_CODE);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ operations.add(new TouchUpModifierOperation());
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Modifier Operations", OP_CODE, name())
+ .description(
+ "Touch up modifier. This operation contains"
+ + " a list of action executed on Touch up");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index e45058528859..6036b74efad3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.animation;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
@@ -44,18 +46,23 @@ public class AnimateMeasure {
float mP = 0f;
float mVp = 0f;
+
+ @NonNull
FloatAnimation mMotionEasing =
new FloatAnimation(mMotionEasingType, mDuration / 1000f, null, 0f, Float.NaN);
+
+ @NonNull
FloatAnimation mVisibilityEasing =
new FloatAnimation(
mVisibilityEasingType, mDurationVisibilityChange / 1000f, null, 0f, Float.NaN);
+
ParticleAnimation mParticleAnimation;
public AnimateMeasure(
long startTime,
- Component component,
+ @NonNull Component component,
ComponentMeasure original,
- ComponentMeasure target,
+ @NonNull ComponentMeasure target,
int duration,
int durationVisibilityChange,
AnimationSpec.ANIMATION enterAnimation,
@@ -94,9 +101,9 @@ public class AnimateMeasure {
mVp = mVisibilityEasing.get(visibilityProgress);
}
- public PaintBundle paint = new PaintBundle();
+ @NonNull public PaintBundle paint = new PaintBundle();
- public void apply(PaintContext context) {
+ public void apply(@NonNull PaintContext context) {
update(context.getContext().currentTime);
mComponent.setX(getX());
@@ -338,7 +345,7 @@ public class AnimateMeasure {
}
}
- public void updateTarget(ComponentMeasure measure, long currentTime) {
+ public void updateTarget(@NonNull ComponentMeasure measure, long currentTime) {
mOriginal.setX(getX());
mOriginal.setY(getY());
mOriginal.setW(getWidth());
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 35533cb95190..47abadeb70fa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.animati
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -92,6 +95,7 @@ public class AnimationSpec implements Operation {
return mExitAnimation;
}
+ @NonNull
@Override
public String toString() {
return "ANIMATION_SPEC (" + mMotionDuration + " ms)";
@@ -109,7 +113,7 @@ public class AnimationSpec implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mAnimationId,
@@ -126,11 +130,13 @@ public class AnimationSpec implements Operation {
// nothing here
}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
+ @NonNull
public static String name() {
return "AnimationSpec";
}
@@ -139,10 +145,11 @@ public class AnimationSpec implements Operation {
return Operations.ANIMATION_SPEC;
}
- public static int animationToInt(ANIMATION animation) {
+ public static int animationToInt(@NonNull ANIMATION animation) {
return animation.ordinal();
}
+ @NonNull
public static ANIMATION intToAnimation(int value) {
switch (value) {
case 0:
@@ -167,14 +174,14 @@ public class AnimationSpec implements Operation {
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int animationId,
int motionDuration,
int motionEasingType,
int visibilityDuration,
int visibilityEasingType,
- ANIMATION enterAnimation,
- ANIMATION exitAnimation) {
+ @NonNull ANIMATION enterAnimation,
+ @NonNull ANIMATION exitAnimation) {
buffer.start(Operations.ANIMATION_SPEC);
buffer.writeInt(animationId);
buffer.writeInt(motionDuration);
@@ -185,7 +192,7 @@ public class AnimationSpec implements Operation {
buffer.writeInt(animationToInt(exitAnimation));
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int animationId = buffer.readInt();
int motionDuration = buffer.readInt();
int motionEasingType = buffer.readInt();
@@ -205,7 +212,7 @@ public class AnimationSpec implements Operation {
operations.add(op);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description("define the animation")
.field(INT, "animationId", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
index 686643fbe1bc..37d20781f4fa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.animation;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
@@ -24,14 +26,14 @@ import java.util.ArrayList;
import java.util.HashMap;
public class ParticleAnimation {
- HashMap<Integer, ArrayList<Particle>> mAllParticles = new HashMap<>();
+ @NonNull HashMap<Integer, ArrayList<Particle>> mAllParticles = new HashMap<>();
- PaintBundle mPaint = new PaintBundle();
+ @NonNull PaintBundle mPaint = new PaintBundle();
public void animate(
- PaintContext context,
- Component component,
- ComponentMeasure start,
+ @NonNull PaintContext context,
+ @NonNull Component component,
+ @NonNull ComponentMeasure start,
ComponentMeasure end,
float progress) {
ArrayList<Particle> particles = mAllParticles.get(component.getComponentId());
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index 047a968785c4..f3e550903a65 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.manager
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -75,6 +77,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
verticalPositioning);
}
+ @NonNull
@Override
public String toString() {
return "BOX ["
@@ -93,6 +96,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
+ mVisibility;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "BOX";
@@ -100,7 +104,11 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
@Override
public void computeWrapSize(
- PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+ PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
for (Component c : mChildrenComponents) {
c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
@@ -119,14 +127,14 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
float maxWidth,
float minHeight,
float maxHeight,
- MeasurePass measure) {
+ @NonNull MeasurePass measure) {
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
}
}
@Override
- public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, @NonNull MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
@@ -161,6 +169,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
}
}
+ @NonNull
public static String name() {
return "BoxLayout";
}
@@ -170,7 +179,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int componentId,
int animationId,
int horizontalPositioning,
@@ -182,7 +191,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
buffer.writeInt(verticalPositioning);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
int horizontalPositioning = buffer.readInt();
@@ -196,7 +205,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
verticalPositioning));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description(
"Box layout implementation.\n\n"
@@ -224,7 +233,7 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId, mAnimationId, mHorizontalPositioning, mVerticalPositioning);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index f79976715ab3..12ff969530b3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.manager
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -44,6 +46,7 @@ public class CanvasLayout extends BoxLayout {
this(parent, componentId, animationId, 0, 0, 0, 0);
}
+ @NonNull
@Override
public String toString() {
return "CANVAS ["
@@ -62,11 +65,13 @@ public class CanvasLayout extends BoxLayout {
+ mVisibility;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "CANVAS";
}
+ @NonNull
public static String name() {
return "CanvasLayout";
}
@@ -75,19 +80,19 @@ public class CanvasLayout extends BoxLayout {
return Operations.LAYOUT_CANVAS;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId) {
+ public static void apply(@NonNull WireBuffer buffer, int componentId, int animationId) {
buffer.start(Operations.LAYOUT_CANVAS);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
operations.add(new CanvasLayout(null, componentId, animationId));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description("Canvas implementation. Encapsulate draw operations.\n\n")
.field(INT, "COMPONENT_ID", "unique id for this component")
@@ -98,7 +103,7 @@ public class CanvasLayout extends BoxLayout {
}
@Override
- public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, @NonNull MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
@@ -112,7 +117,7 @@ public class CanvasLayout extends BoxLayout {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mComponentId, mAnimationId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 402b784343ad..52bf4c54faf6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.manager
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -87,6 +89,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
spacedBy);
}
+ @NonNull
@Override
public String toString() {
return "COLUMN ["
@@ -105,14 +108,24 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
+ mVisibility;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "COLUMN";
}
@Override
+ public boolean isInVerticalFill() {
+ return super.isInVerticalFill() || childrenHaveVerticalWeights();
+ }
+
+ @Override
public void computeWrapSize(
- PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+ PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
int visibleChildrens = 0;
for (Component c : mChildrenComponents) {
@@ -137,7 +150,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
float maxWidth,
float minHeight,
float maxHeight,
- MeasurePass measure) {
+ @NonNull MeasurePass measure) {
DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
float mh = maxHeight;
for (Component child : mChildrenComponents) {
@@ -151,7 +164,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
}
@Override
- public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, @NonNull MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
DebugLog.s(
() ->
@@ -302,6 +315,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
DebugLog.e();
}
+ @NonNull
public static String name() {
return "ColumnLayout";
}
@@ -311,7 +325,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int componentId,
int animationId,
int horizontalPositioning,
@@ -325,7 +339,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
buffer.writeFloat(spacedBy);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
int horizontalPositioning = buffer.readInt();
@@ -341,7 +355,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
spacedBy));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description(
"Column layout implementation, positioning components one"
@@ -374,7 +388,7 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mComponentId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 308ed64ee8ea..0c4d24a4561a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
@@ -27,7 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
/** Base class for layout managers -- resizable components. */
public abstract class LayoutManager extends LayoutComponent implements Measurable {
- Size mCachedWrapSize = new Size(0f, 0f);
+ @NonNull Size mCachedWrapSize = new Size(0f, 0f);
public LayoutManager(
Component parent,
@@ -62,6 +64,38 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
// nothing here
}
+ protected boolean childrenHaveHorizontalWeights() {
+ for (Component c : mChildrenComponents) {
+ if (c instanceof LayoutManager) {
+ LayoutManager m = (LayoutManager) c;
+ if (m.getWidthModifier() != null && m.getWidthModifier().hasWeight()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected boolean childrenHaveVerticalWeights() {
+ for (Component c : mChildrenComponents) {
+ if (c instanceof LayoutManager) {
+ LayoutManager m = (LayoutManager) c;
+ if (m.getHeightModifier() != null && m.getHeightModifier().hasWeight()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isInHorizontalFill() {
+ return mWidthModifier.isFill();
+ }
+
+ public boolean isInVerticalFill() {
+ return mHeightModifier.isFill();
+ }
+
/** Base implementation of the measure resolution */
@Override
public void measure(
@@ -70,7 +104,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
float maxWidth,
float minHeight,
float maxHeight,
- MeasurePass measure) {
+ @NonNull MeasurePass measure) {
boolean hasWrap = true;
float measuredWidth =
Math.min(maxWidth, computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
@@ -87,7 +121,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
} else {
hasWrap = false;
}
- if (mWidthModifier.isFill()) {
+ if (isInHorizontalFill()) {
measuredWidth = insetMaxWidth;
} else if (mWidthModifier.hasWeight()) {
measuredWidth = Math.max(measuredWidth, computeModifierDefinedWidth());
@@ -95,7 +129,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
measuredWidth = Math.max(measuredWidth, minWidth);
measuredWidth = Math.min(measuredWidth, insetMaxWidth);
}
- if (mHeightModifier.isFill()) {
+ if (isInVerticalFill()) {
measuredHeight = insetMaxHeight;
} else if (mHeightModifier.hasWeight()) {
measuredHeight = Math.max(measuredHeight, computeModifierDefinedHeight());
@@ -136,7 +170,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
/** basic layout of internal components */
@Override
- public void layout(RemoteContext context, MeasurePass measure) {
+ public void layout(@NonNull RemoteContext context, @NonNull MeasurePass measure) {
super.layout(context, measure);
ComponentMeasure self = measure.get(this);
@@ -153,7 +187,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
* @param context
* @param measure
*/
- public void selfLayout(RemoteContext context, MeasurePass measure) {
+ public void selfLayout(@NonNull RemoteContext context, @NonNull MeasurePass measure) {
super.layout(context, measure);
ComponentMeasure self = measure.get(this);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index b29a05c27e5f..a366dc804758 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.manager
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -85,6 +87,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
spacedBy);
}
+ @NonNull
@Override
public String toString() {
return "ROW ["
@@ -103,14 +106,24 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
+ mVisibility;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "ROW";
}
@Override
+ public boolean isInHorizontalFill() {
+ return super.isInHorizontalFill() || childrenHaveHorizontalWeights();
+ }
+
+ @Override
public void computeWrapSize(
- PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+ PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
// int visibleChildrens = 0;
for (Component c : mChildrenComponents) {
@@ -135,7 +148,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
float maxWidth,
float minHeight,
float maxHeight,
- MeasurePass measure) {
+ @NonNull MeasurePass measure) {
DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
float mw = maxWidth;
for (Component child : mChildrenComponents) {
@@ -149,7 +162,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
}
@Override
- public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, @NonNull MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
DebugLog.s(
() ->
@@ -305,6 +318,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
DebugLog.e();
}
+ @NonNull
public static String name() {
return "RowLayout";
}
@@ -314,7 +328,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int componentId,
int animationId,
int horizontalPositioning,
@@ -328,7 +342,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
buffer.writeFloat(spacedBy);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
int horizontalPositioning = buffer.readInt();
@@ -344,7 +358,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
spacedBy));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description(
"Row layout implementation, positioning components one"
@@ -377,7 +391,7 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mComponentId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
index b5c728135f76..e47ffdebb253 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -50,10 +52,10 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
// This keep track of all the components associated with a given Id,
// (the key being the id), and the set of components corresponds to the set of states
// TODO: we should be able to optimize this
- public Map<Integer, Component[]> statePaintedComponents = new HashMap<>();
+ @NonNull public Map<Integer, Component[]> statePaintedComponents = new HashMap<>();
public int MAX_CACHE_ELEMENTS = 16;
- public int[] cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+ @NonNull public int[] cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
public boolean inTransition = false;
@@ -168,7 +170,7 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
}
@Override
- public void layout(RemoteContext context, MeasurePass measure) {
+ public void layout(@NonNull RemoteContext context, @NonNull MeasurePass measure) {
ComponentMeasure self = measure.get(this);
super.selfLayout(context, measure);
@@ -207,12 +209,12 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
@Override
public void measure(
- PaintContext context,
+ @NonNull PaintContext context,
float minWidth,
float maxWidth,
float minHeight,
float maxHeight,
- MeasurePass measure) {
+ @NonNull MeasurePass measure) {
// The general approach for this widget is to do most of the work/setup in measure.
// layout and paint then simply use what's been setup in the measure phase.
@@ -364,19 +366,20 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
if (mIndexId != 0) {
int newValue = context.getContext().mRemoteComposeState.getInteger(mIndexId);
if (newValue != currentLayoutIndex) {
previousLayoutIndex = currentLayoutIndex;
currentLayoutIndex = newValue;
inTransition = true;
- System.out.println("currentLayout index is $currentLayoutIndex");
+ // System.out.println("currentLayout index is $currentLayoutIndex");
// executeValueSetActions(getLayout(currentLayoutIndex));
invalidateMeasure();
}
}
- System.out.println("PAINTING LAYOUT STATELAYOUT, CURRENT INDEX " + currentLayoutIndex);
+ // System.out.println("PAINTING LAYOUT STATELAYOUT, CURRENT INDEX " +
+ // currentLayoutIndex);
// Make sure to mark any components that are not in either the current or previous layout
// as being GONE.
int index = 0;
@@ -529,6 +532,7 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
// }
// }
+ @NonNull
@Override
public String toString() {
return "STATE_LAYOUT";
@@ -539,7 +543,7 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
// }
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int componentId,
int animationId,
int horizontalPositioning,
@@ -553,7 +557,7 @@ public class StateLayout extends LayoutManager implements ComponentStartOperatio
buffer.writeInt(indexId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
buffer.readInt(); // horizontalPositioning
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index c1cabcd09c39..8aa7712635fc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.manager
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -44,22 +46,24 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
private int mFontStyle = 0;
private float mFontWeight = 400f;
private int mFontFamilyId = -1;
+ private int mTextAlign = -1;
private int mType = -1;
private float mTextX;
private float mTextY;
+ private float mTextW;
private String mCachedString = "";
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (mTextId != -1) {
context.listensTo(mTextId, this);
}
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
mCachedString = context.getText(mTextId);
if (mType == -1) {
if (mFontFamilyId != -1) {
@@ -97,7 +101,8 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
float fontSize,
int fontStyle,
float fontWeight,
- int fontFamilyId) {
+ int fontFamilyId,
+ int textAlign) {
super(parent, componentId, animationId, x, y, width, height);
mTextId = textId;
mColor = color;
@@ -105,6 +110,7 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
mFontStyle = fontStyle;
mFontWeight = fontWeight;
mFontFamilyId = fontFamilyId;
+ mTextAlign = textAlign;
}
public TextLayout(
@@ -116,7 +122,8 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
float fontSize,
int fontStyle,
float fontWeight,
- int fontFamilyId) {
+ int fontFamilyId,
+ int textAlign) {
this(
parent,
componentId,
@@ -130,13 +137,14 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
fontSize,
fontStyle,
fontWeight,
- fontFamilyId);
+ fontFamilyId,
+ textAlign);
}
- public PaintBundle mPaint = new PaintBundle();
+ @NonNull public PaintBundle mPaint = new PaintBundle();
@Override
- public void paintingComponent(PaintContext context) {
+ public void paintingComponent(@NonNull PaintContext context) {
context.save();
context.translate(mX, mY);
mComponentModifiers.paint(context);
@@ -176,6 +184,7 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
context.restore();
}
+ @NonNull
@Override
public String toString() {
return "TEXT_LAYOUT ["
@@ -194,13 +203,14 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
+ mVisibility;
}
+ @NonNull
@Override
protected String getSerializedName() {
return "TEXT_LAYOUT";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent,
getSerializedName()
@@ -228,7 +238,11 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
@Override
public void computeWrapSize(
- PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ MeasurePass measure,
+ @NonNull Size size) {
context.savePaint();
mPaint.reset();
mPaint.setTextSize(mFontSize);
@@ -244,8 +258,10 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
mTextX = -bounds[0];
size.setHeight(h);
mTextY = -bounds[1];
+ mTextW = w;
}
+ @NonNull
public static String name() {
return "TextLayout";
}
@@ -255,7 +271,7 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
int componentId,
int animationId,
int textId,
@@ -263,7 +279,8 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
float fontSize,
int fontStyle,
float fontWeight,
- int fontFamilyId) {
+ int fontFamilyId,
+ int textAlign) {
buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -273,9 +290,10 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
buffer.writeInt(fontStyle);
buffer.writeFloat(fontWeight);
buffer.writeInt(fontFamilyId);
+ buffer.writeInt(textAlign);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int componentId = buffer.readInt();
int animationId = buffer.readInt();
int textId = buffer.readInt();
@@ -284,6 +302,7 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
int fontStyle = buffer.readInt();
float fontWeight = buffer.readFloat();
int fontFamilyId = buffer.readInt();
+ int textAlign = buffer.readInt();
operations.add(
new TextLayout(
null,
@@ -294,10 +313,11 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
fontSize,
fontStyle,
fontWeight,
- fontFamilyId));
+ fontFamilyId,
+ textAlign));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description("Text layout implementation.\n\n")
.field(INT, "COMPONENT_ID", "unique id for this component")
@@ -313,7 +333,7 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mComponentId,
@@ -323,6 +343,7 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
mFontSize,
mFontStyle,
mFontWeight,
- mFontFamilyId);
+ mFontFamilyId,
+ mTextAlign);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
index 285425f99765..426e02337f5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
/** Encapsulate the result of a measure pass for a component */
@@ -80,7 +82,7 @@ public class ComponentMeasure {
this(id, x, y, w, h, Component.Visibility.VISIBLE);
}
- public ComponentMeasure(Component component) {
+ public ComponentMeasure(@NonNull Component component) {
this(
component.getComponentId(),
component.getX(),
@@ -90,7 +92,7 @@ public class ComponentMeasure {
component.mVisibility);
}
- public void copyFrom(ComponentMeasure m) {
+ public void copyFrom(@NonNull ComponentMeasure m) {
mX = m.mX;
mY = m.mY;
mW = m.mW;
@@ -98,7 +100,7 @@ public class ComponentMeasure {
mVisibility = m.mVisibility;
}
- public boolean same(ComponentMeasure m) {
+ public boolean same(@NonNull ComponentMeasure m) {
return mX == m.mX && mY == m.mY && mW == m.mW && mH == m.mH && mVisibility == m.mVisibility;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
index 8d01fea03690..112ab1b93474 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import java.util.HashMap;
@@ -24,13 +26,13 @@ import java.util.HashMap;
* array vs the current hashmap
*/
public class MeasurePass {
- HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
+ @NonNull HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
public void clear() {
mList.clear();
}
- public void add(ComponentMeasure measure) throws Exception {
+ public void add(@NonNull ComponentMeasure measure) throws Exception {
if (measure.mId == -1) {
throw new Exception("Component has no id!");
}
@@ -41,7 +43,7 @@ public class MeasurePass {
return mList.containsKey(id);
}
- public ComponentMeasure get(Component c) {
+ public ComponentMeasure get(@NonNull Component c) {
if (!mList.containsKey(c.getComponentId())) {
ComponentMeasure measure =
new ComponentMeasure(
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index 64e40f7c591c..76a97ca0eb51 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -42,7 +44,7 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
float mA;
int mShapeType = ShapeType.RECTANGLE;
- public PaintBundle mPaint = new PaintBundle();
+ @NonNull public PaintBundle mPaint = new PaintBundle();
public BackgroundModifierOperation(
float x,
@@ -66,12 +68,12 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mX, mY, mWidth, mHeight, mR, mG, mB, mA, mShapeType);
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent,
"BACKGROUND = ["
@@ -101,11 +103,13 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
this.mHeight = height;
}
+ @NonNull
@Override
public String toString() {
return "BackgroundModifierOperation(" + mWidth + " x " + mHeight + ")";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -115,7 +119,7 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
float x,
float y,
float width,
@@ -138,7 +142,7 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
buffer.writeInt(shapeType);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float x = buffer.readFloat();
float y = buffer.readFloat();
float width = buffer.readFloat();
@@ -153,7 +157,7 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.savePaint();
mPaint.reset();
mPaint.setStyle(PaintBundle.STYLE_FILL);
@@ -167,7 +171,7 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
context.restorePaint();
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Background Modifier")
.field(FLOAT, "x", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 92c0a733d8a1..d48a9c732cd5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -45,7 +47,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
float mA;
int mShapeType = ShapeType.RECTANGLE;
- public PaintBundle paint = new PaintBundle();
+ @NonNull public PaintBundle paint = new PaintBundle();
public BorderModifierOperation(
float x,
@@ -73,7 +75,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent,
"BORDER = ["
@@ -105,7 +107,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(
buffer,
mX,
@@ -127,6 +129,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
this.mHeight = height;
}
+ @NonNull
@Override
public String toString() {
return "BorderModifierOperation("
@@ -152,6 +155,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
+ ")";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -161,7 +165,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
}
public static void apply(
- WireBuffer buffer,
+ @NonNull WireBuffer buffer,
float x,
float y,
float width,
@@ -188,7 +192,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
buffer.writeInt(shapeType);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float x = buffer.readFloat();
float y = buffer.readFloat();
float width = buffer.readFloat();
@@ -206,7 +210,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.savePaint();
paint.reset();
paint.setColor(mR, mG, mB, mA);
@@ -225,7 +229,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
context.restorePaint();
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Border Modifier")
.field(FLOAT, "x", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index 0d8aeaa2f06a..78b51c3f83aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -15,14 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import com.android.internal.widget.remotecompose.core.CoreDocument;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
-import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
@@ -35,7 +35,7 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
float mHeight;
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.clipRect(0f, 0f, mWidth, mHeight);
}
@@ -46,21 +46,16 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void onClick(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
- // nothing
- }
-
- @Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer);
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -69,15 +64,15 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer) {
+ public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, @NonNull List<Operation> operations) {
operations.add(new ClipRectModifierOperation());
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect");
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index 95786a8b62b0..011d7edd18c8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
@@ -22,29 +24,34 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
+import com.android.internal.widget.remotecompose.core.operations.layout.ClickHandler;
import com.android.internal.widget.remotecompose.core.operations.layout.ClickModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.TouchHandler;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.ArrayList;
/** Maintain a list of modifiers */
-public class ComponentModifiers extends PaintOperation implements DecoratorComponent {
- ArrayList<ModifierOperation> mList = new ArrayList<>();
+public class ComponentModifiers extends PaintOperation
+ implements DecoratorComponent, ClickHandler, TouchHandler {
+ @NonNull ArrayList<ModifierOperation> mList = new ArrayList<>();
+ @NonNull
public ArrayList<ModifierOperation> getList() {
return mList;
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
super.apply(context);
for (ModifierOperation op : mList) {
op.apply(context);
}
}
+ @NonNull
@Override
public String toString() {
String str = "ComponentModifiers \n";
@@ -59,7 +66,7 @@ public class ComponentModifiers extends PaintOperation implements DecoratorCompo
// nothing
}
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, "MODIFIERS");
for (ModifierOperation m : mList) {
m.serializeToString(indent + 1, serializer);
@@ -75,7 +82,7 @@ public class ComponentModifiers extends PaintOperation implements DecoratorCompo
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
float tx = 0f;
float ty = 0f;
for (ModifierOperation op : mList) {
@@ -127,8 +134,38 @@ public class ComponentModifiers extends PaintOperation implements DecoratorCompo
public void onClick(
RemoteContext context, CoreDocument document, Component component, float x, float y) {
for (ModifierOperation op : mList) {
- if (op instanceof DecoratorComponent) {
- ((DecoratorComponent) op).onClick(context, document, component, x, y);
+ if (op instanceof ClickHandler) {
+ ((ClickHandler) op).onClick(context, document, component, x, y);
+ }
+ }
+ }
+
+ @Override
+ public void onTouchDown(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ for (ModifierOperation op : mList) {
+ if (op instanceof TouchHandler) {
+ ((TouchHandler) op).onTouchDown(context, document, component, x, y);
+ }
+ }
+ }
+
+ @Override
+ public void onTouchUp(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ for (ModifierOperation op : mList) {
+ if (op instanceof TouchHandler) {
+ ((TouchHandler) op).onTouchUp(context, document, component, x, y);
+ }
+ }
+ }
+
+ @Override
+ public void onTouchCancel(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ for (ModifierOperation op : mList) {
+ if (op instanceof TouchHandler) {
+ ((TouchHandler) op).onTouchCancel(context, document, component, x, y);
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index 312d016029fb..26e737b32027 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -17,7 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
-import com.android.internal.widget.remotecompose.core.CoreDocument;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -37,49 +39,52 @@ public class ComponentVisibilityOperation
private static final int OP_CODE = Operations.MODIFIER_VISIBILITY;
int mVisibilityId;
- Component.Visibility mVisibility = Component.Visibility.VISIBLE;
+ @NonNull Component.Visibility mVisibility = Component.Visibility.VISIBLE;
private LayoutComponent mParent;
public ComponentVisibilityOperation(int id) {
mVisibilityId = id;
}
+ @NonNull
@Override
public String toString() {
return "ComponentVisibilityOperation(" + mVisibilityId + ")";
}
+ @NonNull
public String serializedName() {
return "COMPONENT_VISIBILITY";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, serializedName() + " = " + mVisibilityId);
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@Override
public void write(WireBuffer buffer) {}
- public static void apply(WireBuffer buffer, int valueId) {
+ public static void apply(@NonNull WireBuffer buffer, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
operations.add(new ComponentVisibilityOperation(valueId));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ComponentVisibility")
.description(
"This operation allows setting a component"
@@ -88,12 +93,12 @@ public class ComponentVisibilityOperation
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
context.listensTo(mVisibilityId, this);
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
int visibility = context.getInteger(mVisibilityId);
if (visibility == Component.Visibility.VISIBLE.ordinal()) {
mVisibility = Component.Visibility.VISIBLE;
@@ -115,8 +120,4 @@ public class ComponentVisibilityOperation
@Override
public void layout(RemoteContext context, float width, float height) {}
-
- @Override
- public void onClick(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
index 41e18cbafbe6..b4c4108802ee 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
@@ -15,10 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.RemoteContext;
-import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
/**
@@ -26,11 +23,4 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Decorato
* output (background, border...)
*/
public abstract class DecoratorModifierOperation extends PaintOperation
- implements ModifierOperation, DecoratorComponent {
-
- @Override
- public void onClick(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
- // nothing
- }
-}
+ implements ModifierOperation, DecoratorComponent {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index 408bebcfb7d9..3c2d85cfee5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.operations.Utils;
@@ -29,8 +32,10 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
WRAP,
WEIGHT,
INTRINSIC_MIN,
- INTRINSIC_MAX;
+ INTRINSIC_MAX,
+ EXACT_DP;
+ @NonNull
static Type fromInt(int value) {
switch (value) {
case 0:
@@ -45,6 +50,8 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
return INTRINSIC_MIN;
case 5:
return INTRINSIC_MAX;
+ case 6:
+ return EXACT_DP;
}
return EXACT;
}
@@ -68,19 +75,32 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
}
@Override
- public void updateVariables(RemoteContext context) {
+ public void updateVariables(@NonNull RemoteContext context) {
if (mType == Type.EXACT) {
mOutValue = Float.isNaN(mValue) ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
}
+ if (mType == Type.EXACT_DP) {
+ float pre = mOutValue;
+ mOutValue = Float.isNaN(mValue) ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
+ mOutValue *= context.getDensity();
+ if (pre != mOutValue) {
+ context.getDocument().getRootLayoutComponent().invalidateMeasure();
+ }
+ }
}
@Override
- public void registerListening(RemoteContext context) {
+ public void registerListening(@NonNull RemoteContext context) {
if (mType == Type.EXACT) {
if (Float.isNaN(mValue)) {
context.listensTo(Utils.idFromNan(mValue), this);
}
}
+ if (mType == Type.EXACT_DP) {
+ if (Float.isNaN(mValue)) {
+ context.listensTo(Utils.idFromNan(mValue), this);
+ }
+ }
}
public boolean hasWeight() {
@@ -107,25 +127,31 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
mOutValue = mValue = value;
}
+ @NonNull
public String serializedName() {
return "DIMENSION";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
if (mType == Type.EXACT) {
serializer.append(indent, serializedName() + " = " + mValue);
}
+ if (mType == Type.EXACT_DP) {
+ serializer.append(indent, serializedName() + " = " + mValue + " dp");
+ }
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
+ @NonNull
@Override
public String toString() {
return "DimensionModifierOperation(" + mValue + ")";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
new file mode 100644
index 000000000000..2b3038281d57
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/GraphicsLayerModifierOperation.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.AnimatableValue;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/**
+ * Represents a padding modifier. Padding modifiers can be chained and will impact following
+ * modifiers.
+ */
+public class GraphicsLayerModifierOperation extends DecoratorModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_GRAPHICS_LAYER;
+ public static final String CLASS_NAME = "GraphicsLayerModifierOperation";
+
+ AnimatableValue mScaleX;
+ AnimatableValue mScaleY;
+ AnimatableValue mRotationX;
+ AnimatableValue mRotationY;
+ AnimatableValue mRotationZ;
+ AnimatableValue mTransformOriginX;
+ AnimatableValue mTransformOriginY;
+ AnimatableValue mShadowElevation;
+ AnimatableValue mAlpha;
+ AnimatableValue mCameraDistance;
+ int mBlendMode;
+ int mSpotShadowColorId;
+ int mAmbientShadowColorId;
+ int mColorFilterId;
+ int mRenderEffectId;
+
+ public GraphicsLayerModifierOperation(
+ float scaleX,
+ float scaleY,
+ float rotationX,
+ float rotationY,
+ float rotationZ,
+ float shadowElevation,
+ float transformOriginX,
+ float transformOriginY,
+ float alpha,
+ float cameraDistance,
+ int blendMode,
+ int spotShadowColorId,
+ int ambientShadowColorId,
+ int colorFilterId,
+ int renderEffectId) {
+ mScaleX = new AnimatableValue(scaleX);
+ mScaleY = new AnimatableValue(scaleY);
+ mRotationX = new AnimatableValue(rotationX);
+ mRotationY = new AnimatableValue(rotationY);
+ mRotationZ = new AnimatableValue(rotationZ);
+ mShadowElevation = new AnimatableValue(shadowElevation);
+ mTransformOriginX = new AnimatableValue(transformOriginX);
+ mTransformOriginY = new AnimatableValue(transformOriginY);
+ mAlpha = new AnimatableValue(alpha);
+ mCameraDistance = new AnimatableValue(cameraDistance);
+ mBlendMode = blendMode;
+ mSpotShadowColorId = spotShadowColorId;
+ mAmbientShadowColorId = ambientShadowColorId;
+ mColorFilterId = colorFilterId;
+ mRenderEffectId = renderEffectId;
+ }
+
+ public float getScaleX() {
+ return mScaleX.getValue();
+ }
+
+ public float getScaleY() {
+ return mScaleY.getValue();
+ }
+
+ public float getRotationX() {
+ return mRotationX.getValue();
+ }
+
+ public float getRotationY() {
+ return mRotationY.getValue();
+ }
+
+ public float getRotationZ() {
+ return mRotationZ.getValue();
+ }
+
+ public float getShadowElevation() {
+ return mShadowElevation.getValue();
+ }
+
+ public float getTransformOriginX() {
+ return mTransformOriginX.getValue();
+ }
+
+ public float getTransformOriginY() {
+ return mTransformOriginY.getValue();
+ }
+
+ public float getAlpha() {
+ return mAlpha.getValue();
+ }
+
+ public float getCameraDistance() {
+ return mCameraDistance.getValue();
+ }
+
+ // TODO: add implementation for blendmode
+ public int getBlendModeId() {
+ return mBlendMode;
+ }
+
+ // TODO: add implementation for shadow
+ public int getSpotShadowColorId() {
+ return mSpotShadowColorId;
+ }
+
+ public int getAmbientShadowColorId() {
+ return mAmbientShadowColorId;
+ }
+
+ // TODO: add implementation for color filters
+ public int getColorFilterId() {
+ return mColorFilterId;
+ }
+
+ public int getRenderEffectId() {
+ return mRenderEffectId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(
+ buffer,
+ mScaleX.getValue(),
+ mScaleY.getValue(),
+ mRotationX.getValue(),
+ mRotationY.getValue(),
+ mRotationZ.getValue(),
+ mShadowElevation.getValue(),
+ mTransformOriginX.getValue(),
+ mTransformOriginY.getValue(),
+ mAlpha.getValue(),
+ mCameraDistance.getValue(),
+ mBlendMode,
+ mSpotShadowColorId,
+ mAmbientShadowColorId,
+ mColorFilterId,
+ mRenderEffectId);
+ }
+
+ @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(indent, "GRAPHICS_LAYER = [" + mScaleX + ", " + mScaleY + "]");
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ mScaleX.evaluate(context);
+ mScaleY.evaluate(context);
+ mRotationX.evaluate(context);
+ mRotationY.evaluate(context);
+ mRotationZ.evaluate(context);
+ mTransformOriginX.evaluate(context);
+ mTransformOriginY.evaluate(context);
+ mShadowElevation.evaluate(context);
+ mAlpha.evaluate(context);
+ mCameraDistance.evaluate(context);
+ }
+
+ @Override
+ public String toString() {
+ return "GraphicsLayerModifierOperation(" + mScaleX + ", " + mScaleY + ")";
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(
+ WireBuffer buffer,
+ float scaleX,
+ float scaleY,
+ float rotationX,
+ float rotationY,
+ float rotationZ,
+ float shadowElevation,
+ float transformOriginX,
+ float transformOriginY,
+ float alpha,
+ float cameraDistance,
+ int blendMode,
+ int spotShadowColorId,
+ int ambientShadowColorId,
+ int colorFilterId,
+ int renderEffectId) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(scaleX);
+ buffer.writeFloat(scaleY);
+ buffer.writeFloat(rotationX);
+ buffer.writeFloat(rotationY);
+ buffer.writeFloat(rotationZ);
+ buffer.writeFloat(shadowElevation);
+ buffer.writeFloat(transformOriginX);
+ buffer.writeFloat(transformOriginY);
+ buffer.writeFloat(alpha);
+ buffer.writeFloat(cameraDistance);
+ buffer.writeInt(blendMode);
+ buffer.writeInt(spotShadowColorId);
+ buffer.writeInt(ambientShadowColorId);
+ buffer.writeInt(colorFilterId);
+ buffer.writeInt(renderEffectId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ float scaleX = buffer.readFloat();
+ float scaleY = buffer.readFloat();
+ float rotationX = buffer.readFloat();
+ float rotationY = buffer.readFloat();
+ float rotationZ = buffer.readFloat();
+ float shadowElevation = buffer.readFloat();
+ float transformOriginX = buffer.readFloat();
+ float transformOriginY = buffer.readFloat();
+ float alpha = buffer.readFloat();
+ float cameraDistance = buffer.readFloat();
+ int blendMode = buffer.readInt();
+ int spotShadowColorId = buffer.readInt();
+ int ambientShadowColorId = buffer.readInt();
+ int colorFilterId = buffer.readInt();
+ int renderEffectId = buffer.readInt();
+ operations.add(
+ new GraphicsLayerModifierOperation(
+ scaleX,
+ scaleY,
+ rotationX,
+ rotationY,
+ rotationZ,
+ shadowElevation,
+ transformOriginX,
+ transformOriginY,
+ alpha,
+ cameraDistance,
+ blendMode,
+ spotShadowColorId,
+ ambientShadowColorId,
+ colorFilterId,
+ renderEffectId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
+ .description("define the GraphicsLayer Modifier")
+ .field(FLOAT, "scaleX", "")
+ .field(FLOAT, "scaleY", "")
+ .field(FLOAT, "rotationX", "")
+ .field(FLOAT, "rotationY", "")
+ .field(FLOAT, "rotationZ", "")
+ .field(FLOAT, "shadowElevation", "")
+ .field(FLOAT, "transformOriginX", "")
+ .field(FLOAT, "transformOriginY", "")
+ .field(FLOAT, "alpha", "")
+ .field(FLOAT, "cameraDistance", "")
+ .field(INT, "blendMode", "")
+ .field(INT, "spotShadowColorId", "")
+ .field(INT, "ambientShadowColorId", "")
+ .field(INT, "colorFilterId", "")
+ .field(INT, "renderEffectId", "");
+ }
+
+ @Override
+ public void layout(RemoteContext context, float width, float height) {}
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index d3613f844981..97c76c0aedf7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.WireBuffer;
@@ -30,6 +32,7 @@ public class HeightModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_HEIGHT;
public static final String CLASS_NAME = "HeightModifierOperation";
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -38,13 +41,13 @@ public class HeightModifierOperation extends DimensionModifierOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int type, float value) {
+ public static void apply(@NonNull WireBuffer buffer, int type, float value) {
buffer.start(OP_CODE);
buffer.writeInt(type);
buffer.writeFloat(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Type type = Type.fromInt(buffer.readInt());
float value = buffer.readFloat();
Operation op = new HeightModifierOperation(type, value);
@@ -52,7 +55,7 @@ public class HeightModifierOperation extends DimensionModifierOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mType.ordinal(), mValue);
}
@@ -68,17 +71,19 @@ public class HeightModifierOperation extends DimensionModifierOperation {
super(value);
}
+ @NonNull
@Override
public String toString() {
- return "Height(" + mValue + ")";
+ return "Height(" + mType + ", " + mValue + ")";
}
+ @NonNull
@Override
public String serializedName() {
return "HEIGHT";
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the animation")
.field(INT, "type", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index ac42470a6f8f..836321fff8e6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -39,6 +42,7 @@ public class HostActionOperation implements ActionOperation {
mActionId = id;
}
+ @NonNull
@Override
public String toString() {
return "HostActionOperation(" + mActionId + ")";
@@ -48,20 +52,22 @@ public class HostActionOperation implements ActionOperation {
return mActionId;
}
+ @NonNull
public String serializedName() {
return "HOST_ACTION";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, serializedName() + " = " + mActionId);
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@@ -70,21 +76,25 @@ public class HostActionOperation implements ActionOperation {
@Override
public void runAction(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ @NonNull RemoteContext context,
+ CoreDocument document,
+ Component component,
+ float x,
+ float y) {
context.runAction(mActionId, "");
}
- public static void apply(WireBuffer buffer, int actionId) {
+ public static void apply(@NonNull WireBuffer buffer, int actionId) {
buffer.start(OP_CODE);
buffer.writeInt(actionId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int actionId = buffer.readInt();
operations.add(new HostActionOperation(actionId));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "HostAction")
.description("Host action. This operation represents a host action")
.field(INT, "ACTION_ID", "Host Action ID");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index b674a582fc14..e97e89784a23 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -33,31 +36,47 @@ import java.util.List;
public class HostNamedActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_NAMED_ACTION;
+ public static final int FLOAT_TYPE = 0;
+ public static final int INT_TYPE = 1;
+ public static final int STRING_TYPE = 2;
+ public static final int NONE_TYPE = -1;
+
int mTextId = -1;
+ int mType = NONE_TYPE;
+ int mValueId = -1;
- public HostNamedActionOperation(int id) {
+ public HostNamedActionOperation(int id, int type, int valueId) {
mTextId = id;
+ mType = type;
+ mValueId = valueId;
}
+ @NonNull
@Override
public String toString() {
- return "HostNamedActionOperation(" + mTextId + ")";
+ return "HostNamedActionOperation(" + mTextId + " : " + mValueId + ")";
}
+ @NonNull
public String serializedName() {
return "HOST_NAMED_ACTION";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, serializedName() + " = " + mTextId);
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ if (mValueId != -1) {
+ serializer.append(indent, serializedName() + " = " + mTextId + " : " + mValueId);
+ } else {
+ serializer.append(indent, serializedName() + " = " + mTextId);
+ }
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@@ -66,23 +85,42 @@ public class HostNamedActionOperation implements ActionOperation {
@Override
public void runAction(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
- context.runNamedAction(mTextId);
+ @NonNull RemoteContext context,
+ CoreDocument document,
+ Component component,
+ float x,
+ float y) {
+ Object value = null;
+ if (mValueId != -1) {
+ if (mType == INT_TYPE) {
+ value = context.mRemoteComposeState.getInteger(mValueId);
+ } else if (mType == STRING_TYPE) {
+ value = context.mRemoteComposeState.getFromId(mValueId);
+ } else if (mType == FLOAT_TYPE) {
+ value = context.mRemoteComposeState.getFloat(mValueId);
+ }
+ }
+ context.runNamedAction(mTextId, value);
}
- public static void apply(WireBuffer buffer, int textId) {
+ public static void apply(@NonNull WireBuffer buffer, int textId, int type, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
+ buffer.writeInt(type);
+ buffer.writeInt(valueId);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int textId = buffer.readInt();
- operations.add(new HostNamedActionOperation(textId));
+ int type = buffer.readInt();
+ int valueId = buffer.readInt();
+ operations.add(new HostNamedActionOperation(textId, type, valueId));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "HostNamedAction")
.description("Host Named action. This operation represents a host action")
- .field(INT, "TEXT_ID", "Named Host Action Text ID");
+ .field(INT, "TEXT_ID", "Named Host Action Text ID")
+ .field(INT, "VALUE_ID", "Named Host Action Value ID");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
new file mode 100644
index 000000000000..65fe345ac920
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/OffsetModifierOperation.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Represents an offset modifier. */
+public class OffsetModifierOperation extends DecoratorModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_OFFSET;
+ public static final String CLASS_NAME = "OffsetModifierOperation";
+
+ float mX;
+ float mY;
+
+ public OffsetModifierOperation(float x, float y) {
+ this.mX = x;
+ this.mY = y;
+ }
+
+ public float getX() {
+ return mX;
+ }
+
+ public float getY() {
+ return mY;
+ }
+
+ public void setX(float x) {
+ this.mX = x;
+ }
+
+ public void setY(float y) {
+ this.mY = y;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mX, mY);
+ }
+
+ // @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(indent, "OFFSET = [" + mX + ", " + mY + "]");
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ float x = context.getContext().mRemoteComposeState.getFloat(Utils.idFromNan(mX));
+ float y = context.getContext().mRemoteComposeState.getFloat(Utils.idFromNan(mY));
+ float density = context.getContext().getDensity();
+ x *= density;
+ y *= density;
+ context.translate(x, y);
+ }
+
+ @Override
+ public String toString() {
+ return "OffsetModifierOperation(" + mX + ", " + mY + ")";
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(WireBuffer buffer, float x, float y) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x);
+ buffer.writeFloat(y);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ float x = buffer.readFloat();
+ float y = buffer.readFloat();
+ operations.add(new OffsetModifierOperation(x, y));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
+ .description("define the Offset Modifier")
+ .field(FLOAT, "x", "")
+ .field(FLOAT, "y", "");
+ }
+
+ @Override
+ public void layout(RemoteContext context, float width, float height) {}
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index e0ec1a60ef7e..ed5522ea865f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -78,12 +81,12 @@ public class PaddingModifierOperation implements ModifierOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mLeft, mTop, mRight, mBottom);
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent, "PADDING = [" + mLeft + ", " + mTop + ", " + mRight + ", " + mBottom + "]");
}
@@ -91,11 +94,13 @@ public class PaddingModifierOperation implements ModifierOperation {
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
+ @NonNull
@Override
public String toString() {
return "PaddingModifierOperation("
@@ -109,6 +114,7 @@ public class PaddingModifierOperation implements ModifierOperation {
+ ")";
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -117,7 +123,8 @@ public class PaddingModifierOperation implements ModifierOperation {
return Operations.MODIFIER_PADDING;
}
- public static void apply(WireBuffer buffer, float left, float top, float right, float bottom) {
+ public static void apply(
+ @NonNull WireBuffer buffer, float left, float top, float right, float bottom) {
buffer.start(Operations.MODIFIER_PADDING);
buffer.writeFloat(left);
buffer.writeFloat(top);
@@ -125,7 +132,7 @@ public class PaddingModifierOperation implements ModifierOperation {
buffer.writeFloat(bottom);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float left = buffer.readFloat();
float top = buffer.readFloat();
float right = buffer.readFloat();
@@ -133,7 +140,7 @@ public class PaddingModifierOperation implements ModifierOperation {
operations.add(new PaddingModifierOperation(left, top, right, bottom));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Padding Modifier")
.field(FLOAT, "left", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
index dc95fe7774aa..6218dd5f3311 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
@@ -17,7 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
-import com.android.internal.widget.remotecompose.core.CoreDocument;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -25,7 +26,6 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
import com.android.internal.widget.remotecompose.core.operations.DrawBase4;
-import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
@@ -37,7 +37,7 @@ public class RoundedClipRectModifierOperation extends DrawBase4
public static final int OP_CODE = Operations.MODIFIER_ROUNDED_CLIP_RECT;
public static final String CLASS_NAME = "RoundedClipRectModifierOperation";
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Maker m = RoundedClipRectModifierOperation::new;
read(m, buffer, operations);
}
@@ -46,16 +46,17 @@ public class RoundedClipRectModifierOperation extends DrawBase4
return OP_CODE;
}
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@Override
- protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Modifier Operations", id(), "RoundedClipRectModifierOperation")
.description("clip with rectangle")
.field(
@@ -90,7 +91,7 @@ public class RoundedClipRectModifierOperation extends DrawBase4
}
@Override
- public void paint(PaintContext context) {
+ public void paint(@NonNull PaintContext context) {
context.roundedClipRect(mWidth, mHeight, mX1, mY1, mX2, mY2);
}
@@ -101,13 +102,7 @@ public class RoundedClipRectModifierOperation extends DrawBase4
}
@Override
- public void onClick(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
- // nothing
- }
-
- @Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent,
"ROUNDED_CLIP_RECT = ["
@@ -135,7 +130,11 @@ public class RoundedClipRectModifierOperation extends DrawBase4
* @param bottomEnd bottomEnd radius
*/
public static void apply(
- WireBuffer buffer, float topStart, float topEnd, float bottomStart, float bottomEnd) {
+ @NonNull WireBuffer buffer,
+ float topStart,
+ float topEnd,
+ float bottomStart,
+ float bottomEnd) {
write(buffer, OP_CODE, topStart, topEnd, bottomStart, bottomEnd);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
new file mode 100644
index 000000000000..29ec82810a7c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueFloatChangeActionOperation.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Apply a value change on an float variable. */
+public class ValueFloatChangeActionOperation implements ActionOperation {
+ private static final int OP_CODE = Operations.VALUE_FLOAT_CHANGE_ACTION;
+
+ int mTargetValueId = -1;
+ float mValue = -1;
+
+ public ValueFloatChangeActionOperation(int id, float value) {
+ mTargetValueId = id;
+ mValue = value;
+ }
+
+ @Override
+ public String toString() {
+ return "ValueFloatChangeActionOperation(" + mTargetValueId + ")";
+ }
+
+ public String serializedName() {
+ return "VALUE_FLOAT_CHANGE";
+ }
+
+ @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(indent, serializedName() + " = " + mTargetValueId + " -> " + mValue);
+ }
+
+ @Override
+ public void apply(RemoteContext context) {}
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {}
+
+ @Override
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ System.out.println("OVERRIDE " + mTargetValueId + " TO " + mValue);
+ context.overrideFloat(mTargetValueId, mValue);
+ }
+
+ public static void apply(WireBuffer buffer, int valueId, float value) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(valueId);
+ buffer.writeFloat(value);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int valueId = buffer.readInt();
+ float value = buffer.readFloat();
+ operations.add(new ValueFloatChangeActionOperation(valueId, value));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "ValueFloatChangeActionOperation")
+ .description(
+ "ValueIntegerChange action. "
+ + " This operation represents a value change for the given id")
+ .field(INT, "TARGET_VALUE_ID", "Value ID")
+ .field(FLOAT, "VALUE", "float value to be assigned to the target");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index 8876720c9990..d7ce8acc72bf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -41,25 +44,28 @@ public class ValueIntegerChangeActionOperation implements ActionOperation {
mValue = value;
}
+ @NonNull
@Override
public String toString() {
return "ValueChangeActionOperation(" + mTargetValueId + ")";
}
+ @NonNull
public String serializedName() {
return "VALUE_INTEGER_CHANGE";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, serializedName() + " = " + mTargetValueId + " -> " + mValue);
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@@ -68,23 +74,27 @@ public class ValueIntegerChangeActionOperation implements ActionOperation {
@Override
public void runAction(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ @NonNull RemoteContext context,
+ CoreDocument document,
+ Component component,
+ float x,
+ float y) {
context.overrideInteger(mTargetValueId, mValue);
}
- public static void apply(WireBuffer buffer, int valueId, int value) {
+ public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
buffer.writeInt(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
int value = buffer.readInt();
operations.add(new ValueIntegerChangeActionOperation(valueId, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueIntegerChangeActionOperation")
.description(
"ValueIntegerChange action. "
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
index fb5e911e8bc6..75d13e785d4c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -41,17 +44,19 @@ public class ValueIntegerExpressionChangeActionOperation implements ActionOperat
mValueExpressionId = value;
}
+ @NonNull
@Override
public String toString() {
return "ValueIntegerExpressionChangeActionOperation(" + mTargetValueId + ")";
}
+ @NonNull
public String serializedName() {
return "VALUE_INTEGER_EXPRESSION_CHANGE";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(
indent, serializedName() + " = " + mTargetValueId + " -> " + mValueExpressionId);
}
@@ -59,8 +64,9 @@ public class ValueIntegerExpressionChangeActionOperation implements ActionOperat
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@@ -69,23 +75,27 @@ public class ValueIntegerExpressionChangeActionOperation implements ActionOperat
@Override
public void runAction(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ @NonNull RemoteContext context,
+ @NonNull CoreDocument document,
+ Component component,
+ float x,
+ float y) {
document.evaluateIntExpression(mValueExpressionId, (int) mTargetValueId, context);
}
- public static void apply(WireBuffer buffer, long valueId, long value) {
+ public static void apply(@NonNull WireBuffer buffer, long valueId, long value) {
buffer.start(OP_CODE);
buffer.writeLong(valueId);
buffer.writeLong(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
long valueId = buffer.readLong();
long value = buffer.readLong();
operations.add(new ValueIntegerExpressionChangeActionOperation(valueId, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueIntegerExpressionChangeActionOperation")
.description(
"ValueIntegerExpressionChange action. "
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index a64a492a8cc1..26d7244eee8c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -17,6 +17,9 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -41,6 +44,7 @@ public class ValueStringChangeActionOperation implements ActionOperation {
mValueId = value;
}
+ @NonNull
@Override
public String toString() {
return "ValueChangeActionOperation(" + mTargetValueId + ")";
@@ -50,20 +54,22 @@ public class ValueStringChangeActionOperation implements ActionOperation {
return mTargetValueId;
}
+ @NonNull
public String serializedName() {
return "VALUE_CHANGE";
}
@Override
- public void serializeToString(int indent, StringSerializer serializer) {
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
serializer.append(indent, serializedName() + " = " + mTargetValueId + " -> " + mValueId);
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
- public String deepToString(String indent) {
+ public String deepToString(@Nullable String indent) {
return (indent != null ? indent : "") + toString();
}
@@ -72,23 +78,27 @@ public class ValueStringChangeActionOperation implements ActionOperation {
@Override
public void runAction(
- RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ @NonNull RemoteContext context,
+ CoreDocument document,
+ Component component,
+ float x,
+ float y) {
context.overrideText(mTargetValueId, mValueId);
}
- public static void apply(WireBuffer buffer, int valueId, int value) {
+ public static void apply(@NonNull WireBuffer buffer, int valueId, int value) {
buffer.start(OP_CODE);
buffer.writeInt(valueId);
buffer.writeInt(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int valueId = buffer.readInt();
int value = buffer.readInt();
operations.add(new ValueStringChangeActionOperation(valueId, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueStringChangeActionOperation")
.description(
"ValueStrin gChange action. "
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 62403b3e5e7e..e2f899ce2b46 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -18,6 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.WireBuffer;
@@ -30,6 +32,7 @@ public class WidthModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_WIDTH;
public static final String CLASS_NAME = "WidthModifierOperation";
+ @NonNull
public static String name() {
return CLASS_NAME;
}
@@ -38,13 +41,13 @@ public class WidthModifierOperation extends DimensionModifierOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int type, float value) {
+ public static void apply(@NonNull WireBuffer buffer, int type, float value) {
buffer.start(OP_CODE);
buffer.writeInt(type);
buffer.writeFloat(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
Type type = Type.fromInt(buffer.readInt());
float value = buffer.readFloat();
Operation op = new WidthModifierOperation(type, value);
@@ -56,7 +59,7 @@ public class WidthModifierOperation extends DimensionModifierOperation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mType.ordinal(), mValue);
}
@@ -68,17 +71,19 @@ public class WidthModifierOperation extends DimensionModifierOperation {
super(value);
}
+ @NonNull
@Override
public String toString() {
- return "Width(" + mValue + ")";
+ return "Width(" + mType + ", " + mValue + ")";
}
+ @NonNull
@Override
public String serializedName() {
return "WIDTH";
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the animation")
.field(INT, "type", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
new file mode 100644
index 000000000000..aa20e0388d31
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ZIndexModifierOperation.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Represents a ZIndex modifier, allowing to change the z-index of a component. */
+public class ZIndexModifierOperation extends DecoratorModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_ZINDEX;
+ public static final String CLASS_NAME = "ZIndexModifierOperation";
+ float mValue;
+ float mCurrentValue;
+
+ public ZIndexModifierOperation(float value) {
+ this.mValue = value;
+ }
+
+ public float getValue() {
+ return mCurrentValue;
+ }
+
+ public void setmValue(float value) {
+ this.mValue = value;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mValue);
+ }
+
+ // @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(indent, "ZINDEX = [" + mValue + "]");
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ mCurrentValue = mValue;
+ if (Utils.isVariable(mValue)) {
+ mCurrentValue =
+ context.getContext().mRemoteComposeState.getFloat(Utils.idFromNan(mValue));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ZIndexModifierOperation(" + mValue + ")";
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(WireBuffer buffer, float value) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(value);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ float value = buffer.readFloat();
+ operations.add(new ZIndexModifierOperation(value));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
+ .description("define the Z-Index Modifier")
+ .field(FLOAT, "value", "");
+ }
+
+ @Override
+ public void layout(RemoteContext context, float width, float height) {}
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
index 4849b12c65b1..d8e49b0a9ccd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.utils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
import java.util.ArrayList;
/** Internal utility debug class */
@@ -23,12 +26,12 @@ public class DebugLog {
public static final boolean DEBUG_LAYOUT_ON = false;
public static class Node {
- public Node parent;
+ @Nullable public Node parent;
public String name;
public String endString;
- public ArrayList<Node> list = new ArrayList<>();
+ @NonNull public ArrayList<Node> list = new ArrayList<>();
- public Node(Node parent, String name) {
+ public Node(@Nullable Node parent, String name) {
this.parent = parent;
this.name = name;
this.endString = name + " DONE";
@@ -48,21 +51,21 @@ public class DebugLog {
}
}
- public static Node node = new Node(null, "Root");
- public static Node currentNode = node;
+ @NonNull public static Node node = new Node(null, "Root");
+ @NonNull public static Node currentNode = node;
public static void clear() {
node = new Node(null, "Root");
currentNode = node;
}
- public static void s(StringValueSupplier valueSupplier) {
+ public static void s(@NonNull StringValueSupplier valueSupplier) {
if (DEBUG_LAYOUT_ON) {
currentNode = new Node(currentNode, valueSupplier.getString());
}
}
- public static void log(StringValueSupplier valueSupplier) {
+ public static void log(@NonNull StringValueSupplier valueSupplier) {
if (DEBUG_LAYOUT_ON) {
new LogNode(currentNode, valueSupplier.getString());
}
@@ -78,7 +81,7 @@ public class DebugLog {
}
}
- public static void e(StringValueSupplier valueSupplier) {
+ public static void e(@NonNull StringValueSupplier valueSupplier) {
if (DEBUG_LAYOUT_ON) {
currentNode.endString = valueSupplier.getString();
if (currentNode.parent != null) {
@@ -89,7 +92,7 @@ public class DebugLog {
}
}
- public static void printNode(int indent, Node node, StringBuilder builder) {
+ public static void printNode(int indent, @NonNull Node node, @NonNull StringBuilder builder) {
if (DEBUG_LAYOUT_ON) {
StringBuilder indentationBuilder = new StringBuilder();
for (int i = 0; i < indent; i++) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
index 9a3cd54c3f85..a808cf0e17b3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
+import android.annotation.NonNull;
+
/** Provides a Builder pattern for a PaintBundle */
class Painter {
PaintBundle mPaint;
@@ -24,16 +26,19 @@ class Painter {
return mPaint;
}
+ @NonNull
public Painter setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
return this;
}
+ @NonNull
public Painter setColor(int color) {
mPaint.setColor(color);
return this;
}
+ @NonNull
public Painter setColorId(int colorId) {
mPaint.setColorId(colorId);
return this;
@@ -44,6 +49,7 @@ class Painter {
*
* @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
*/
+ @NonNull
public Painter setStrokeJoin(int join) {
mPaint.setStrokeJoin(join);
return this;
@@ -56,6 +62,7 @@ class Painter {
* @param width set the paint's stroke width, used whenever the paint's style is Stroke or
* StrokeAndFill.
*/
+ @NonNull
public Painter setStrokeWidth(float width) {
mPaint.setStrokeWidth(width);
return this;
@@ -67,6 +74,7 @@ class Painter {
*
* @param style The new style to set in the paint
*/
+ @NonNull
public Painter setStyle(int style) {
mPaint.setStyle(style);
return this;
@@ -78,6 +86,7 @@ class Painter {
* @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
* StrokeAndFill.
*/
+ @NonNull
public Painter setStrokeCap(int cap) {
mPaint.setStrokeCap(cap);
return this;
@@ -90,6 +99,7 @@ class Painter {
* @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
* StrokeAndFill.
*/
+ @NonNull
public Painter setStrokeMiter(float miter) {
mPaint.setStrokeMiter(miter);
return this;
@@ -101,6 +111,7 @@ class Painter {
*
* @param alpha set the alpha component [0..1.0] of the paint's color.
*/
+ @NonNull
public Painter setAlpha(float alpha) {
mPaint.setAlpha((alpha > 2) ? alpha / 255f : alpha);
return this;
@@ -112,6 +123,7 @@ class Painter {
* @param color The ARGB source color used with the specified Porter-Duff mode
* @param mode The porter-duff mode that is applied
*/
+ @NonNull
public Painter setPorterDuffColorFilter(int color, int mode) {
mPaint.setColorFilter(color, mode);
return this;
@@ -130,6 +142,7 @@ class Painter {
* line.
* @param tileMode The Shader tiling mode
*/
+ @NonNull
public Painter setLinearGradient(
float startX,
float startY,
@@ -155,6 +168,7 @@ class Painter {
* circle.
* @param tileMode The Shader tiling mode
*/
+ @NonNull
public Painter setRadialGradient(
float centerX,
float centerY,
@@ -178,6 +192,7 @@ class Painter {
* may produce unexpected results. If positions is NULL, then the colors are automatically
* spaced evenly.
*/
+ @NonNull
public Painter setSweepGradient(float centerX, float centerY, int[] colors, float[] positions) {
mPaint.setSweepGradient(colors, 0, positions, centerX, centerY);
return this;
@@ -188,6 +203,7 @@ class Painter {
*
* @param size set the paint's text size in pixel units.
*/
+ @NonNull
public Painter setTextSize(float size) {
mPaint.setTextSize(size);
return this;
@@ -215,16 +231,19 @@ class Painter {
* @param weight The desired weight to be drawn.
* @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
*/
+ @NonNull
public Painter setTypeface(int fontType, int weight, boolean italic) {
mPaint.setTextStyle(fontType, weight, italic);
return this;
}
+ @NonNull
public Painter setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
return this;
}
+ @NonNull
public Painter setShader(int id) {
mPaint.setShader(id);
return this;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index 1d673c4c5ab6..b25f4cd3c530 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -15,11 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
-/**
- * high performance floating point expression evaluator used in animation
- */
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/** high performance floating point expression evaluator used in animation */
public class AnimatedFloatExpression {
- static IntMap<String> sNames = new IntMap<>();
+ @NonNull static IntMap<String> sNames = new IntMap<>();
public static final int OFFSET = 0x310_000;
public static final float ADD = asNan(OFFSET + 1);
public static final float SUB = asNan(OFFSET + 2);
@@ -74,7 +75,7 @@ public class AnimatedFloatExpression {
private static final float FP_TO_DEG = 0.017453292f; // 180/PI
float[] mStack;
- float[] mLocalStack = new float[128];
+ @NonNull float[] mLocalStack = new float[128];
float[] mVar;
CollectionsAccess mCollectionsAccess;
@@ -201,7 +202,7 @@ public class AnimatedFloatExpression {
* @param var
* @return
*/
- public float eval(float[] exp, int len, float... var) {
+ public float eval(@NonNull float[] exp, int len, float... var) {
System.arraycopy(exp, 0, mLocalStack, 0, len);
mStack = mLocalStack;
mVar = var;
@@ -224,7 +225,7 @@ public class AnimatedFloatExpression {
* @param var
* @return
*/
- public float evalDB(float[] exp, float... var) {
+ public float evalDB(@NonNull float[] exp, float... var) {
mStack = exp;
mVar = var;
int sp = -1;
@@ -240,195 +241,281 @@ public class AnimatedFloatExpression {
return mStack[sp];
}
- Op[] mOps = {
+ @NonNull Op[] mOps;
+
+ {
+ Op mADD =
+ (sp) -> { // ADD
+ mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+ return sp - 1;
+ };
+ Op mSUB =
+ (sp) -> { // SUB
+ mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+ return sp - 1;
+ };
+ Op mMUL =
+ (sp) -> { // MUL
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+ return sp - 1;
+ };
+ Op mDIV =
+ (sp) -> { // DIV
+ mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+ return sp - 1;
+ };
+ Op mMOD =
+ (sp) -> { // MOD
+ mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+ return sp - 1;
+ };
+ Op mMIN =
+ (sp) -> { // MIN
+ mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mMAX =
+ (sp) -> { // MAX
+ mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mPOW =
+ (sp) -> { // POW
+ mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mSQRT =
+ (sp) -> { // SQRT
+ mStack[sp] = (float) Math.sqrt(mStack[sp]);
+ return sp;
+ };
+ Op mABS =
+ (sp) -> { // ABS
+ mStack[sp] = (float) Math.abs(mStack[sp]);
+ return sp;
+ };
+ Op mSIGN =
+ (sp) -> { // SIGN
+ mStack[sp] = (float) Math.signum(mStack[sp]);
+ return sp;
+ };
+ Op mCOPY_SIGN =
+ (sp) -> { // copySign
+ mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mEXP =
+ (sp) -> { // EXP
+ mStack[sp] = (float) Math.exp(mStack[sp]);
+ return sp;
+ };
+ Op mFLOOR =
+ (sp) -> { // FLOOR
+ mStack[sp] = (float) Math.floor(mStack[sp]);
+ return sp;
+ };
+ Op mLOG =
+ (sp) -> { // LOG
+ mStack[sp] = (float) Math.log10(mStack[sp]);
+ return sp;
+ };
+ Op mLN =
+ (sp) -> { // LN
+ mStack[sp] = (float) Math.log(mStack[sp]);
+ return sp;
+ };
+ Op mROUND =
+ (sp) -> { // ROUND
+ mStack[sp] = (float) Math.round(mStack[sp]);
+ return sp;
+ };
+ Op mSIN =
+ (sp) -> { // SIN
+ mStack[sp] = (float) Math.sin(mStack[sp]);
+ return sp;
+ };
+ Op mCOS =
+ (sp) -> { // COS
+ mStack[sp] = (float) Math.cos(mStack[sp]);
+ return sp;
+ };
+ Op mTAN =
+ (sp) -> { // TAN
+ mStack[sp] = (float) Math.tan(mStack[sp]);
+ return sp;
+ };
+ Op mASIN =
+ (sp) -> { // ASIN
+ mStack[sp] = (float) Math.asin(mStack[sp]);
+ return sp;
+ };
+ Op mACOS =
+ (sp) -> { // ACOS
+ mStack[sp] = (float) Math.acos(mStack[sp]);
+ return sp;
+ };
+ Op mATAN =
+ (sp) -> { // ATAN
+ mStack[sp] = (float) Math.atan(mStack[sp]);
+ return sp;
+ };
+ Op mATAN2 =
+ (sp) -> { // ATAN2
+ mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mMAD =
+ (sp) -> { // MAD
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+ };
+ Op mTERNARY_CONDITIONAL =
+ (sp) -> { // TERNARY_CONDITIONAL
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+ };
+ Op mCLAMP =
+ (sp) -> { // CLAMP
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
+ return sp - 2;
+ };
+ Op mCBRT =
+ (sp) -> { // CBRT
+ mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
+ return sp;
+ };
+ Op mDEG =
+ (sp) -> { // DEG
+ mStack[sp] = mStack[sp] * FP_TO_RAD;
+ return sp;
+ };
+ Op mRAD =
+ (sp) -> { // RAD
+ mStack[sp] = mStack[sp] * FP_TO_DEG;
+ return sp;
+ };
+ Op mCEIL =
+ (sp) -> { // CEIL
+ mStack[sp] = (float) Math.ceil(mStack[sp]);
+ return sp;
+ };
+ Op mA_DEREF =
+ (sp) -> { // A_DEREF
+ int id = fromNaN(mStack[sp]);
+ mStack[sp - 1] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp - 1]);
+ return sp - 1;
+ };
+ Op mA_MAX =
+ (sp) -> { // A_MAX
+ int id = fromNaN(mStack[sp]);
+ float[] array = mCollectionsAccess.getFloats(id);
+ float max = array[0];
+ for (int i = 1; i < array.length; i++) {
+ max = Math.max(max, array[i]);
+ }
+ mStack[sp] = max;
+ return sp;
+ };
+ Op mA_MIN =
+ (sp) -> { // A_MIN
+ int id = fromNaN(mStack[sp]);
+ float[] array = mCollectionsAccess.getFloats(id);
+ float max = array[0];
+ for (int i = 1; i < array.length; i++) {
+ max = Math.max(max, array[i]);
+ }
+ mStack[sp] = max;
+ return sp;
+ };
+ Op mA_SUM =
+ (sp) -> { // A_SUM
+ int id = fromNaN(mStack[sp]);
+ float[] array = mCollectionsAccess.getFloats(id);
+ float sum = 0;
+ for (int i = 0; i < array.length; i++) {
+ sum += array[i];
+ }
+ mStack[sp] = sum;
+ return sp;
+ };
+ Op mA_AVG =
+ (sp) -> { // A_AVG
+ int id = fromNaN(mStack[sp]);
+ float[] array = mCollectionsAccess.getFloats(id);
+ float sum = 0;
+ for (int i = 0; i < array.length; i++) {
+ sum += array[i];
+ }
+ mStack[sp] = sum / array.length;
+ return sp;
+ };
+ Op mA_LEN =
+ (sp) -> { // A_LEN
+ int id = fromNaN(mStack[sp]);
+ mStack[sp] = mCollectionsAccess.getListLength(id);
+ return sp;
+ };
+ Op mFIRST_VAR =
+ (sp) -> { // FIRST_VAR
+ mStack[sp] = mVar[0];
+ return sp;
+ };
+ Op mSECOND_VAR =
+ (sp) -> { // SECOND_VAR
+ mStack[sp] = mVar[1];
+ return sp;
+ };
+ Op mTHIRD_VAR =
+ (sp) -> { // THIRD_VAR
+ mStack[sp] = mVar[2];
+ return sp;
+ };
+
+ Op[] ops = {
null,
- (sp) -> { // ADD
- mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
- return sp - 1;
- },
- (sp) -> { // SUB
- mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
- return sp - 1;
- },
- (sp) -> { // MUL
- mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
- return sp - 1;
- },
- (sp) -> { // DIV
- mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
- return sp - 1;
- },
- (sp) -> { // MOD
- mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
- return sp - 1;
- },
- (sp) -> { // MIN
- mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // MAX
- mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // POW
- mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // SQRT
- mStack[sp] = (float) Math.sqrt(mStack[sp]);
- return sp;
- },
- (sp) -> { // ABS
- mStack[sp] = (float) Math.abs(mStack[sp]);
- return sp;
- },
- (sp) -> { // SIGN
- mStack[sp] = (float) Math.signum(mStack[sp]);
- return sp;
- },
- (sp) -> { // copySign
- mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // EXP
- mStack[sp] = (float) Math.exp(mStack[sp]);
- return sp;
- },
- (sp) -> { // FLOOR
- mStack[sp] = (float) Math.floor(mStack[sp]);
- return sp;
- },
- (sp) -> { // LOG
- mStack[sp] = (float) Math.log10(mStack[sp]);
- return sp;
- },
- (sp) -> { // LN
- mStack[sp] = (float) Math.log(mStack[sp]);
- return sp;
- },
- (sp) -> { // ROUND
- mStack[sp] = (float) Math.round(mStack[sp]);
- return sp;
- },
- (sp) -> { // SIN
- mStack[sp] = (float) Math.sin(mStack[sp]);
- return sp;
- },
- (sp) -> { // COS
- mStack[sp] = (float) Math.cos(mStack[sp]);
- return sp;
- },
- (sp) -> { // TAN
- mStack[sp] = (float) Math.tan(mStack[sp]);
- return sp;
- },
- (sp) -> { // ASIN
- mStack[sp] = (float) Math.asin(mStack[sp]);
- return sp;
- },
- (sp) -> { // ACOS
- mStack[sp] = (float) Math.acos(mStack[sp]);
- return sp;
- },
- (sp) -> { // ATAN
- mStack[sp] = (float) Math.atan(mStack[sp]);
- return sp;
- },
- (sp) -> { // ATAN2
- mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // MAD
- mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
- return sp - 2;
- },
- (sp) -> { // Ternary conditional
- mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
- return sp - 2;
- },
- (sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
- return sp - 2;
- },
- (sp) -> { // CBRT cuberoot
- mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
- return sp;
- },
- (sp) -> { // DEG
- mStack[sp] = mStack[sp] * FP_TO_RAD;
- return sp;
- },
- (sp) -> { // RAD
- mStack[sp] = mStack[sp] * FP_TO_DEG;
- return sp;
- },
- (sp) -> { // CEIL
- mStack[sp] = (float) Math.ceil(mStack[sp]);
- return sp;
- },
- (sp) -> { // A_DEREF
- int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp - 1]);
- return sp - 1;
- },
- (sp) -> { // A_MAX
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float max = array[0];
- for (int i = 1; i < array.length; i++) {
- max = Math.max(max, array[i]);
- }
- mStack[sp] = max;
- return sp;
- },
- (sp) -> { // A_MIN
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float max = array[0];
- for (int i = 1; i < array.length; i++) {
- max = Math.max(max, array[i]);
- }
- mStack[sp] = max;
- return sp;
- },
- (sp) -> { // A_SUM
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float sum = 0;
- for (int i = 0; i < array.length; i++) {
- sum += array[i];
- }
- mStack[sp] = sum;
- return sp;
- },
- (sp) -> { // A_AVG
- int id = fromNaN(mStack[sp]);
- float[] array = mCollectionsAccess.getFloats(id);
- float sum = 0;
- for (int i = 0; i < array.length; i++) {
- sum += array[i];
- }
- mStack[sp] = sum / array.length;
- return sp;
- },
- (sp) -> { // A_LEN
- int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getListLength(id);
- return sp;
- },
- (sp) -> { // first var =
- mStack[sp] = mVar[0];
- return sp;
- },
- (sp) -> { // second var y?
- mStack[sp] = mVar[1];
- return sp;
- },
- (sp) -> { // 3rd var z?
- mStack[sp] = mVar[2];
- return sp;
- },
- };
+ mADD,
+ mSUB,
+ mMUL,
+ mDIV,
+ mMOD,
+ mMIN,
+ mMAX,
+ mPOW,
+ mSQRT,
+ mABS,
+ mSIGN,
+ mCOPY_SIGN,
+ mEXP,
+ mFLOOR,
+ mLOG,
+ mLN,
+ mROUND,
+ mSIN,
+ mCOS,
+ mTAN,
+ mASIN,
+ mACOS,
+ mATAN,
+ mATAN2,
+ mMAD,
+ mTERNARY_CONDITIONAL,
+ mCLAMP,
+ mCBRT,
+ mDEG,
+ mRAD,
+ mCEIL,
+ mA_DEREF,
+ mA_MAX,
+ mA_MIN,
+ mA_SUM,
+ mA_AVG,
+ mA_LEN,
+ mFIRST_VAR,
+ mSECOND_VAR,
+ mTHIRD_VAR,
+ };
+ mOps = ops;
+ }
static {
int k = 0;
@@ -483,6 +570,7 @@ public class AnimatedFloatExpression {
* @param f
* @return
*/
+ @Nullable
public static String toMathName(float f) {
int id = fromNaN(f) - OFFSET;
return sNames.get(id);
@@ -495,7 +583,8 @@ public class AnimatedFloatExpression {
* @param labels
* @return
*/
- public static String toString(float[] exp, String[] labels) {
+ @NonNull
+ public static String toString(@NonNull float[] exp, @Nullable String[] labels) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < exp.length; i++) {
float v = exp[i];
@@ -525,7 +614,7 @@ public class AnimatedFloatExpression {
return s.toString();
}
- static String toString(float[] exp, int sp) {
+ static String toString(@NonNull float[] exp, int sp) {
// String[] str = new String[exp.length];
if (Float.isNaN(exp[sp])) {
int id = fromNaN(exp[sp]) - OFFSET;
@@ -575,42 +664,42 @@ public class AnimatedFloatExpression {
}
static final int[] NO_OF_OPS = {
- -1, // no op
- 2,
- 2,
- 2,
- 2,
- 2, // + - * / %
- 2,
- 2,
- 2, // min max, power
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1, // sqrt,abs,CopySign,exp,floor,log,ln
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 2, // round,sin,cos,tan,asin,acos,atan,atan2
- 3,
- 3,
- 3,
- 1,
- 1,
- 1,
- 1,
- 0,
- 0,
- 0 // mad, ?:,
- // a[0],a[1],a[2]
+ -1, // no op
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // + - * / %
+ 2,
+ 2,
+ 2, // min max, power
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // sqrt,abs,CopySign,exp,floor,log,ln
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2, // round,sin,cos,tan,asin,acos,atan,atan2
+ 3,
+ 3,
+ 3,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0 // mad, ?:,
+ // a[0],a[1],a[2]
};
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
index 00c87c1f9c80..e74b3350f427 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+import android.annotation.NonNull;
+
/** Implement the scaling logic for Compose Image or ImageView */
public class ImageScaling {
@@ -97,6 +99,7 @@ public class ImageScaling {
adjustDrawToType();
}
+ @NonNull
static String str(float v) {
String s = " " + (int) v;
return s.substring(s.length() - 3);
@@ -210,6 +213,7 @@ public class ImageScaling {
}
}
+ @NonNull
public static String typeToString(int type) {
String[] typeString = {
"none",
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 84e78431790a..749c0fe0dcc3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+import android.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Arrays;
@@ -42,6 +44,7 @@ public class IntMap<T> {
mSize = 0;
}
+ @Nullable
public T put(int key, T value) {
if (key == NOT_PRESENT) throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
if (mSize > mKeys.length * LOAD_FACTOR) {
@@ -50,6 +53,7 @@ public class IntMap<T> {
return insert(key, value);
}
+ @Nullable
public T get(int key) {
int index = findKey(key);
if (index == -1) {
@@ -61,6 +65,7 @@ public class IntMap<T> {
return mSize;
}
+ @Nullable
private T insert(int key, T value) {
int index = hash(key) % mKeys.length;
while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
index baa144d6b28d..8905431d14d7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/**
* High performance Integer expression evaluator
*
@@ -22,7 +25,7 @@ package com.android.internal.widget.remotecompose.core.operations.utilities;
* 0)
*/
public class IntegerExpressionEvaluator {
- static IntMap<String> sNames = new IntMap<>();
+ @NonNull static IntMap<String> sNames = new IntMap<>();
public static final int OFFSET = 0x10000;
// add, sub, mul,div,mod,min,max, shl, shr, ushr, OR, AND , XOR, COPY_SIGN
public static final int I_ADD = OFFSET + 1;
@@ -57,7 +60,7 @@ public class IntegerExpressionEvaluator {
public static final int I_VAR2 = OFFSET + 25;
int[] mStack;
- int[] mLocalStack = new int[128];
+ @NonNull int[] mLocalStack = new int[128];
int[] mVar;
interface Op {
@@ -68,8 +71,8 @@ public class IntegerExpressionEvaluator {
* Evaluate an integer expression
*
* @param mask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int eval(int mask, int[] exp, int... var) {
@@ -91,12 +94,12 @@ public class IntegerExpressionEvaluator {
* Evaluate a integer expression
*
* @param mask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param len the number of values in the expression
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param len the number of values in the expression
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
- public int eval(int mask, int[] exp, int len, int... var) {
+ public int eval(int mask, @NonNull int[] exp, int len, int... var) {
System.arraycopy(exp, 0, mLocalStack, 0, len);
mStack = mLocalStack;
mVar = var;
@@ -116,11 +119,11 @@ public class IntegerExpressionEvaluator {
* Evaluate a int expression
*
* @param opMask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
- public int evalDB(int opMask, int[] exp, int... var) {
+ public int evalDB(int opMask, @NonNull int[] exp, int... var) {
mStack = exp;
mVar = var;
int sp = -1;
@@ -137,113 +140,172 @@ public class IntegerExpressionEvaluator {
return mStack[sp];
}
- Op[] mOps = {
+ @NonNull Op[] mOps;
+
+ {
+ Op mADD =
+ (sp) -> { // ADD
+ mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+ return sp - 1;
+ };
+ Op mSUB =
+ (sp) -> { // SUB
+ mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+ return sp - 1;
+ };
+ Op mMUL =
+ (sp) -> { // MUL
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+ return sp - 1;
+ };
+ Op mDIV =
+ (sp) -> { // DIV
+ mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+ return sp - 1;
+ };
+ Op mMOD =
+ (sp) -> { // MOD
+ mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+ return sp - 1;
+ };
+ Op mSHL =
+ (sp) -> { // SHL
+ mStack[sp - 1] = mStack[sp - 1] << mStack[sp];
+ return sp - 1;
+ };
+ Op mSHR =
+ (sp) -> { // SHR
+ mStack[sp - 1] = mStack[sp - 1] >> mStack[sp];
+ return sp - 1;
+ };
+ Op mUSHR =
+ (sp) -> { // USHR
+ mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp];
+ return sp - 1;
+ };
+ Op mOR =
+ (sp) -> { // OR
+ mStack[sp - 1] = mStack[sp - 1] | mStack[sp];
+ return sp - 1;
+ };
+ Op mAND =
+ (sp) -> { // AND
+ mStack[sp - 1] = mStack[sp - 1] & mStack[sp];
+ return sp - 1;
+ };
+ Op mXOR =
+ (sp) -> { // XOR
+ mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp];
+ return sp - 1;
+ };
+ Op mCOPY_SIGN =
+ (sp) -> { // COPY_SIGN
+ mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
+ return sp - 1;
+ };
+ Op mMIN =
+ (sp) -> { // MIN
+ mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mMAX =
+ (sp) -> { // MAX
+ mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ };
+ Op mNEG =
+ (sp) -> { // NEG
+ mStack[sp] = -mStack[sp];
+ return sp;
+ };
+ Op mABS =
+ (sp) -> { // ABS
+ mStack[sp] = Math.abs(mStack[sp]);
+ return sp;
+ };
+ Op mINCR =
+ (sp) -> { // INCR
+ mStack[sp] = mStack[sp] + 1;
+ return sp;
+ };
+ Op mDECR =
+ (sp) -> { // DECR
+ mStack[sp] = mStack[sp] - 1;
+ return sp;
+ };
+ Op mNOT =
+ (sp) -> { // NOT
+ mStack[sp] = ~mStack[sp];
+ return sp;
+ };
+ Op mSIGN =
+ (sp) -> { // SIGN
+ mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
+ return sp;
+ };
+ Op mCLAMP =
+ (sp) -> { // CLAMP
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
+ return sp - 2;
+ };
+ Op mTERNARY_CONDITIONAL =
+ (sp) -> { // TERNARY_CONDITIONAL
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+ };
+ Op mMAD =
+ (sp) -> { // MAD
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+ };
+ Op mFIRST_VAR =
+ (sp) -> { // FIRST_VAR
+ mStack[sp] = mVar[0];
+ return sp;
+ };
+ Op mSECOND_VAR =
+ (sp) -> { // SECOND_VAR
+ mStack[sp] = mVar[1];
+ return sp;
+ };
+ Op mTHIRD_VAR =
+ (sp) -> { // THIRD_VAR
+ mStack[sp] = mVar[2];
+ return sp;
+ };
+
+ Op[] ops = {
null,
- (sp) -> { // ADD
- mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
- return sp - 1;
- },
- (sp) -> { // SUB
- mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
- return sp - 1;
- },
- (sp) -> { // MUL
- mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
- return sp - 1;
- },
- (sp) -> { // DIV
- mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
- return sp - 1;
- },
- (sp) -> { // MOD
- mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
- return sp - 1;
- },
- (sp) -> { // SHL shift left
- mStack[sp - 1] = mStack[sp - 1] << mStack[sp];
- return sp - 1;
- },
- (sp) -> { // SHR shift right
- mStack[sp - 1] = mStack[sp - 1] >> mStack[sp];
- return sp - 1;
- },
- (sp) -> { // USHR unsigned shift right
- mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp];
- return sp - 1;
- },
- (sp) -> { // OR operator
- mStack[sp - 1] = mStack[sp - 1] | mStack[sp];
- return sp - 1;
- },
- (sp) -> { // AND operator
- mStack[sp - 1] = mStack[sp - 1] & mStack[sp];
- return sp - 1;
- },
- (sp) -> { // XOR xor operator
- mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp];
- return sp - 1;
- },
- (sp) -> { // COPY_SIGN copy the sing of (using bit magic)
- mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
- return sp - 1;
- },
- (sp) -> { // MIN
- mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // MAX
- mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]);
- return sp - 1;
- },
- (sp) -> { // NEG
- mStack[sp] = -mStack[sp];
- return sp;
- },
- (sp) -> { // ABS
- mStack[sp] = Math.abs(mStack[sp]);
- return sp;
- },
- (sp) -> { // INCR increment
- mStack[sp] = mStack[sp] + 1;
- return sp;
- },
- (sp) -> { // DECR decrement
- mStack[sp] = mStack[sp] - 1;
- return sp;
- },
- (sp) -> { // NOT Bit invert
- mStack[sp] = ~mStack[sp];
- return sp;
- },
- (sp) -> { // SIGN x<0 = -1,x==0 = 0 , x>0 = 1
- mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
- return sp;
- },
- (sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
- return sp - 2;
- },
- (sp) -> { // Ternary conditional
- mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
- return sp - 2;
- },
- (sp) -> { // MAD
- mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
- return sp - 2;
- },
- (sp) -> { // first var =
- mStack[sp] = mVar[0];
- return sp;
- },
- (sp) -> { // second var y?
- mStack[sp] = mVar[1];
- return sp;
- },
- (sp) -> { // 3rd var z?
- mStack[sp] = mVar[2];
- return sp;
- },
- };
+ mADD,
+ mSUB,
+ mMUL,
+ mDIV,
+ mMOD,
+ mSHL,
+ mSHR,
+ mUSHR,
+ mOR,
+ mAND,
+ mXOR,
+ mCOPY_SIGN,
+ mMIN,
+ mMAX,
+ mNEG,
+ mABS,
+ mINCR,
+ mDECR,
+ mNOT,
+ mSIGN,
+ mCLAMP,
+ mTERNARY_CONDITIONAL,
+ mMAD,
+ mFIRST_VAR,
+ mSECOND_VAR,
+ mTHIRD_VAR,
+ };
+
+ mOps = ops;
+ }
static {
int k = 0;
@@ -283,6 +345,7 @@ public class IntegerExpressionEvaluator {
* @param f the numerical value of the function + offset
* @return the math name of the function
*/
+ @Nullable
public static String toMathName(int f) {
int id = f - OFFSET;
return sNames.get(id);
@@ -292,11 +355,12 @@ public class IntegerExpressionEvaluator {
* Convert an expression encoded as an array of ints int to a string
*
* @param opMask bits that are operators
- * @param exp rpn sequence of values and operators
+ * @param exp rpn sequence of values and operators
* @param labels String that represent the variable names
* @return
*/
- public static String toString(int opMask, int[] exp, String[] labels) {
+ @NonNull
+ public static String toString(int opMask, @NonNull int[] exp, String[] labels) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < exp.length; i++) {
int v = exp[i];
@@ -324,10 +388,11 @@ public class IntegerExpressionEvaluator {
* Convert an expression encoded as an array of ints int ot a string
*
* @param opMask bit mask of operators vs commands
- * @param exp rpn sequence of values and operators
+ * @param exp rpn sequence of values and operators
* @return string representation of the expression
*/
- public static String toString(int opMask, int[] exp) {
+ @NonNull
+ public static String toString(int opMask, @NonNull int[] exp) {
StringBuilder s = new StringBuilder();
s.append(Integer.toBinaryString(opMask));
s.append(" : ");
@@ -355,13 +420,15 @@ public class IntegerExpressionEvaluator {
* This creates an infix string expression
*
* @param opMask The bits that are operators
- * @param exp the array of expressions
+ * @param exp the array of expressions
* @return infix string
*/
- public static String toStringInfix(int opMask, int[] exp) {
+ @NonNull
+ public static String toStringInfix(int opMask, @NonNull int[] exp) {
return toString(opMask, exp, exp.length - 1);
}
+ @NonNull
static String toString(int mask, int[] exp, int sp) {
if (((1 << sp) & mask) != 0) {
int id = exp[sp] - OFFSET;
@@ -412,34 +479,34 @@ public class IntegerExpressionEvaluator {
}
static final int[] NO_OF_OPS = {
- -1, // no op
- 2,
- 2,
- 2,
- 2,
- 2, // + - * / %
- 2,
- 2,
- 2,
- 2,
- 2,
- 2,
- 2,
- 2,
- 2, // <<, >> , >>> , | , &, ^, min max
- 1,
- 1,
- 1,
- 1,
- 1,
- 1, // neg, abs, ++, -- , not , sign
- 3,
- 3,
- 3, // clamp, ifElse, mad,
- 0,
- 0,
- 0 // mad, ?:,
- // a[0],a[1],a[2]
+ -1, // no op
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // + - * / %
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // <<, >> , >>> , | , &, ^, min max
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // neg, abs, ++, -- , not , sign
+ 3,
+ 3,
+ 3, // clamp, ifElse, mad,
+ 0,
+ 0,
+ 0 // mad, ?:,
+ // a[0],a[1],a[2]
};
/**
@@ -456,7 +523,7 @@ public class IntegerExpressionEvaluator {
* is it an id or operation
*
* @param opMask the bits that mark elements as an operation
- * @param i the bit to check
+ * @param i the bit to check
* @return true if the bit is 1
*/
public static boolean isOperation(int opMask, int i) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
index ab7576e12aa6..92127c12f401 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
@@ -15,9 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/** Utility serializer maintaining an indent buffer */
public class StringSerializer {
- StringBuffer mBuffer = new StringBuffer();
+ @NonNull StringBuffer mBuffer = new StringBuffer();
+
+ @NonNull
String mIndentBuffer = " ";
/**
@@ -26,7 +31,7 @@ public class StringSerializer {
* @param indent the indentation level to use
* @param content content to append
*/
- public void append(int indent, String content) {
+ public void append(int indent, @Nullable String content) {
String indentation = mIndentBuffer.substring(0, indent);
mBuffer.append(indentation);
mBuffer.append(indentation);
@@ -44,6 +49,7 @@ public class StringSerializer {
*
* @return string representation
*/
+ @NonNull
@Override
public String toString() {
return mBuffer.toString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index f2ccb401ea8f..a95a175d0edd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+import android.annotation.NonNull;
+
import java.util.Arrays;
/** Utilities for string manipulation */
@@ -30,6 +32,7 @@ public class StringUtils {
* @param post character to pad width 0 = no pad typically ' ' or '0'
* @return
*/
+ @NonNull
public static String floatToString(
float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
index 60a59cf464cd..1343345df6e5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+import android.annotation.NonNull;
+
class CubicEasing extends Easing {
float mX1 = 0f;
float mY1 = 0f;
@@ -62,7 +64,7 @@ class CubicEasing extends Easing {
mType = type;
}
- void setup(float[] values) {
+ void setup(@NonNull float[] values) {
setup(values[0], values[1], values[2], values[3]);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
index a29b8af5fbd1..ebb22b6e98c5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/** Support Animation of the FloatExpression */
public class FloatAnimation extends Easing {
float[] mSpec;
@@ -31,6 +34,7 @@ public class FloatAnimation extends Easing {
// private float mScale = 1;
float mOffset = 0;
+ @NonNull
@Override
public String toString() {
@@ -74,7 +78,7 @@ public class FloatAnimation extends Easing {
* @return
*/
public static float[] packToFloatArray(
- float duration, int type, float[] spec, float initialValue, float wrap) {
+ float duration, int type, @Nullable float[] spec, float initialValue, float wrap) {
int count = 0;
if (!Float.isNaN(initialValue)) {
@@ -129,6 +133,90 @@ public class FloatAnimation extends Easing {
}
/**
+ * Useful to debug the packed form of an animation string
+ *
+ * @param description
+ * @return
+ */
+ public static String unpackAnimationToString(float[] description) {
+ float[] mSpec = description;
+ float mDuration = (mSpec.length == 0) ? 1 : mSpec[0];
+ int len = 0;
+ int type = 0;
+ float wrapValue = Float.NaN;
+ float initialValue = Float.NaN;
+ if (mSpec.length > 1) {
+ int num_type = Float.floatToRawIntBits(mSpec[1]);
+ type = num_type & 0xFF;
+ boolean wrap = ((num_type >> 8) & 0x1) > 0;
+ boolean init = ((num_type >> 8) & 0x2) > 0;
+ len = (num_type >> 16) & 0xFFFF;
+ int off = 2 + len;
+ if (init) {
+ initialValue = mSpec[off++];
+ }
+ if (wrap) {
+ wrapValue = mSpec[off];
+ }
+ }
+ float[] params = description;
+ int offset = 2;
+
+ String typeStr = "";
+ switch (type) {
+ case CUBIC_STANDARD:
+ typeStr = "CUBIC_STANDARD";
+ break;
+ case CUBIC_ACCELERATE:
+ typeStr = "CUBIC_ACCELERATE";
+ break;
+ case CUBIC_DECELERATE:
+ typeStr = "CUBIC_DECELERATE";
+ break;
+ case CUBIC_LINEAR:
+ typeStr = "CUBIC_LINEAR";
+ break;
+ case CUBIC_ANTICIPATE:
+ typeStr = "CUBIC_ANTICIPATE";
+ break;
+ case CUBIC_OVERSHOOT:
+ typeStr = "CUBIC_OVERSHOOT";
+
+ break;
+ case CUBIC_CUSTOM:
+ typeStr = "CUBIC_CUSTOM (";
+ typeStr += params[offset + 0] + " ";
+ typeStr += params[offset + 1] + " ";
+ typeStr += params[offset + 2] + " ";
+ typeStr += params[offset + 3] + " )";
+ break;
+ case EASE_OUT_BOUNCE:
+ typeStr = "EASE_OUT_BOUNCE";
+
+ break;
+ case EASE_OUT_ELASTIC:
+ typeStr = "EASE_OUT_ELASTIC";
+ break;
+ case SPLINE_CUSTOM:
+ typeStr = "SPLINE_CUSTOM (";
+ for (int i = offset; i < offset + len; i++) {
+ typeStr += params[i] + " ";
+ }
+ typeStr += ")";
+ break;
+ }
+
+ String str = mDuration + " " + typeStr;
+ if (!Float.isNaN(initialValue)) {
+ str += " init =" + initialValue;
+ }
+ if (!Float.isNaN(wrapValue)) {
+ str += " wrap =" + wrapValue;
+ }
+ return str;
+ }
+
+ /**
* Create an animation based on a float encoding of the animation
*
* @param description
@@ -208,21 +296,43 @@ public class FloatAnimation extends Easing {
setScaleOffset();
}
+ private static float wrap(float wrap, float value) {
+ value = value % wrap;
+ if (value < 0) {
+ value += wrap;
+ }
+ return value;
+ }
+
+ float wrapDistance(float wrap, float from, float to) {
+ float delta = (to - from) % 360;
+ if (delta < -wrap / 2) {
+ delta += wrap;
+ } else if (delta > wrap / 2) {
+ delta -= wrap;
+ }
+ return delta;
+ }
+
/**
* Set the target value to interpolate to
*
* @param value
*/
public void setTargetValue(float value) {
- if (Float.isNaN(mWrap)) {
- mTargetValue = value;
- } else {
- if (Math.abs((value % mWrap) + mWrap - mInitialValue)
- < Math.abs((value % mWrap) - mInitialValue)) {
- mTargetValue = (value % mWrap) + mWrap;
+ mTargetValue = value;
+ if (!Float.isNaN(mWrap)) {
+ mInitialValue = wrap(mWrap, mInitialValue);
+ mTargetValue = wrap(mWrap, mTargetValue);
+ if (Float.isNaN(mInitialValue)) {
+ mInitialValue = mTargetValue;
+ }
- } else {
- mTargetValue = value % mWrap;
+ float dist = wrapDistance(mWrap, mInitialValue, mTargetValue);
+ if ((dist > 0) && (mTargetValue < mInitialValue)) {
+ mTargetValue += mWrap;
+ } else if ((dist < 0) && (mTargetValue > mInitialValue)) {
+ mTargetValue -= mWrap;
}
}
setScaleOffset();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
index 75a60324aa3c..90b65bf2353a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -15,10 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+import android.annotation.NonNull;
+
/** Provides and interface to create easing functions */
public class GeneralEasing extends Easing {
float[] mEasingData = new float[0];
- Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
+ @NonNull Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
/**
* Set the curve based on the float encoding of it
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
index 9355cacde4ad..f540e7008471 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+import android.annotation.NonNull;
+
import java.util.Arrays;
/** This performs a spline interpolation in multiple dimensions */
@@ -32,7 +34,7 @@ public class MonotonicCurveFit {
* @param time the point along the curve
* @param y the parameter at those points
*/
- public MonotonicCurveFit(double[] time, double[][] y) {
+ public MonotonicCurveFit(@NonNull double[] time, @NonNull double[][] y) {
final int n = time.length;
final int dim = y[0].length;
mSlopeTemp = new double[dim];
@@ -331,7 +333,8 @@ public class MonotonicCurveFit {
}
/** This builds a monotonic spline to be used as a wave function */
- public static MonotonicCurveFit buildWave(String configString) {
+ @NonNull
+ public static MonotonicCurveFit buildWave(@NonNull String configString) {
// done this way for efficiency
String str = configString;
double[] values = new double[str.length() / 2];
@@ -350,7 +353,8 @@ public class MonotonicCurveFit {
return buildWave(Arrays.copyOf(values, count));
}
- private static MonotonicCurveFit buildWave(double[] values) {
+ @NonNull
+ private static MonotonicCurveFit buildWave(@NonNull double[] values) {
int length = values.length * 3 - 2;
int len = values.length - 1;
double gap = 1.0 / len;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
index b4596897a44f..c7be3cab4c0b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+import android.annotation.NonNull;
+
/**
* This class translates a series of floating point values into a continuous curve for use in an
* easing function including quantize functions it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)"
@@ -28,6 +30,7 @@ public class StepCurve extends Easing {
mCurveFit = genSpline(params, offset, len);
}
+ @NonNull
private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) {
int length = arrayLen * 3 - 2;
int len = arrayLen - 1;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
new file mode 100644
index 000000000000..3e24372f9b8c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/touch/VelocityEasing.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.touch;
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class VelocityEasing {
+ private float mStartPos = 0;
+ private float mStartV = 0;
+ private float mEndPos = 0;
+ private float mDuration = 0;
+
+ private Stage[] mStage = {new Stage(1), new Stage(2), new Stage(3)};
+ private int mNumberOfStages = 0;
+ private Easing mEasing;
+ private double mEasingAdapterDistance = 0;
+ private double mEasingAdapterA = 0;
+ private double mEasingAdapterB = 0;
+ private boolean mOneDimension = true;
+ private float mTotalEasingDuration = 0;
+
+ public float getDuration() {
+ if (mEasing != null) {
+ return mTotalEasingDuration;
+ }
+ return mDuration;
+ }
+
+ public float getV(float t) {
+ if (mEasing == null) {
+ for (int i = 0; i < mNumberOfStages; i++) {
+ if (mStage[i].mEndTime > t) {
+ return mStage[i].getVel(t);
+ }
+ }
+ return 0f;
+ }
+ int lastStages = mNumberOfStages - 1;
+ for (int i = 0; i < lastStages; i++) {
+ if (mStage[i].mEndTime > t) {
+ return mStage[i].getVel(t);
+ }
+ }
+ return (float) getEasingDiff((t - mStage[lastStages].mStartTime));
+ }
+
+ public float getPos(float t) {
+ if (mEasing == null) {
+ for (int i = 0; i < mNumberOfStages; i++) {
+ if (mStage[i].mEndTime > t) {
+ return mStage[i].getPos(t);
+ }
+ }
+ return mEndPos;
+ }
+ int lastStages = mNumberOfStages - 1;
+ for (int i = 0; i < lastStages; i++) {
+ if (mStage[i].mEndTime > t) {
+ return mStage[i].getPos(t);
+ }
+ }
+ var ret = (float) getEasing((t - mStage[lastStages].mStartTime));
+ ret += mStage[lastStages].mStartPos;
+ return ret;
+ }
+
+ public String toString() {
+ var s = " ";
+ for (int i = 0; i < mNumberOfStages; i++) {
+ Stage stage = mStage[i];
+ s += " $i $stage";
+ }
+ return s;
+ }
+
+ public void config(
+ float currentPos,
+ float destination,
+ float currentVelocity,
+ float maxTime,
+ float maxAcceleration,
+ float maxVelocity,
+ Easing easing) {
+ float pos = currentPos;
+ float velocity = currentVelocity;
+ if (pos == destination) {
+ pos += 1f;
+ }
+ mStartPos = pos;
+ mEndPos = destination;
+ if (easing != null) {
+ this.mEasing = easing.clone();
+ }
+ float dir = Math.signum(destination - pos);
+ float maxV = maxVelocity * dir;
+ float maxA = maxAcceleration * dir;
+ if (velocity == 0.0) {
+ velocity = 0.0001f * dir;
+ }
+ mStartV = velocity;
+ if (!rampDown(pos, destination, velocity, maxTime)) {
+ if (!(mOneDimension
+ && cruseThenRampDown(pos, destination, velocity, maxTime, maxA, maxV))) {
+ if (!rampUpRampDown(pos, destination, velocity, maxA, maxV, maxTime)) {
+ rampUpCruseRampDown(pos, destination, velocity, maxA, maxV, maxTime);
+ }
+ }
+ }
+ if (mOneDimension) {
+ configureEasingAdapter();
+ }
+ }
+
+ private boolean rampDown(
+ float currentPos, float destination, float currentVelocity, float maxTime) {
+ float timeToDestination = 2 * ((destination - currentPos) / currentVelocity);
+ if (timeToDestination > 0 && timeToDestination <= maxTime) { // hit the brakes
+ mNumberOfStages = 1;
+ mStage[0].setUp(currentVelocity, currentPos, 0f, 0f, destination, timeToDestination);
+ mDuration = timeToDestination;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean cruseThenRampDown(
+ float currentPos,
+ float destination,
+ float currentVelocity,
+ float maxTime,
+ float maxA,
+ float maxV) {
+ float timeToBreak = currentVelocity / maxA;
+ float brakeDist = currentVelocity * timeToBreak / 2;
+ float cruseDist = destination - currentPos - brakeDist;
+ float cruseTime = cruseDist / currentVelocity;
+ float totalTime = cruseTime + timeToBreak;
+ if (totalTime > 0 && totalTime < maxTime) {
+ mNumberOfStages = 2;
+ mStage[0].setUp(currentVelocity, currentPos, 0f, currentVelocity, cruseDist, cruseTime);
+ mStage[1].setUp(
+ currentVelocity,
+ currentPos + cruseDist,
+ cruseTime,
+ 0f,
+ destination,
+ cruseTime + timeToBreak);
+ mDuration = cruseTime + timeToBreak;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean rampUpRampDown(
+ float currentPos,
+ float destination,
+ float currentVelocity,
+ float maxA,
+ float maxVelocity,
+ float maxTime) {
+ float peak_v =
+ Math.signum(maxA)
+ * (float)
+ Math.sqrt(
+ (maxA * (destination - currentPos)
+ + currentVelocity * currentVelocity / 2));
+ if (maxVelocity / peak_v > 1) {
+ float t1 = (peak_v - currentVelocity) / maxA;
+ float d1 = (peak_v + currentVelocity) * t1 / 2 + currentPos;
+ float t2 = peak_v / maxA;
+ mNumberOfStages = 2;
+ mStage[0].setUp(currentVelocity, currentPos, 0f, peak_v, d1, t1);
+ mStage[1].setUp(peak_v, d1, t1, 0f, destination, t2 + t1);
+ mDuration = t2 + t1;
+ if (mDuration > maxTime) {
+ return false;
+ }
+ if (mDuration < maxTime / 2) {
+ t1 = mDuration / 2;
+ t2 = t1;
+ peak_v = (2 * (destination - currentPos) / t1 - currentVelocity) / 2;
+ d1 = (peak_v + currentVelocity) * t1 / 2 + currentPos;
+ mNumberOfStages = 2;
+ mStage[0].setUp(currentVelocity, currentPos, 0f, peak_v, d1, t1);
+ mStage[1].setUp(peak_v, d1, t1, 0f, destination, t2 + t1);
+ mDuration = t2 + t1;
+ if (mDuration > maxTime) {
+ System.out.println(" fail ");
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void rampUpCruseRampDown(
+ float currentPos,
+ float destination,
+ float currentVelocity,
+ float maxA,
+ float maxV,
+ float maxTime) {
+ float t1 = maxTime / 3;
+ float t2 = t1 * 2;
+ float distance = destination - currentPos;
+ float dt2 = t2 - t1;
+ float dt3 = maxTime - t2;
+ float v1 = (2 * distance - currentVelocity * t1) / (t1 + 2 * dt2 + dt3);
+ mDuration = maxTime;
+ float d1 = (currentVelocity + v1) * t1 / 2;
+ float d2 = (v1 + v1) * (t2 - t1) / 2;
+ mNumberOfStages = 3;
+ float acc = (v1 - currentVelocity) / t1;
+ float dec = v1 / dt3;
+ mStage[0].setUp(currentVelocity, currentPos, 0f, v1, currentPos + d1, t1);
+ mStage[1].setUp(v1, currentPos + d1, t1, v1, currentPos + d1 + d2, t2);
+ mStage[2].setUp(v1, currentPos + d1 + d2, t2, 0f, destination, maxTime);
+ mDuration = maxTime;
+ }
+
+ double getEasing(double t) {
+ double gx = t * t * mEasingAdapterA + t * mEasingAdapterB;
+ if (gx > 1) {
+ return mEasingAdapterDistance;
+ } else {
+ return mEasing.get(gx) * mEasingAdapterDistance;
+ }
+ }
+
+ private double getEasingDiff(double t) {
+ double gx = t * t * mEasingAdapterA + t * mEasingAdapterB;
+ if (gx > 1) {
+ return 0.0;
+ } else {
+ return mEasing.getDiff(gx)
+ * mEasingAdapterDistance
+ * (t * mEasingAdapterA + mEasingAdapterB);
+ }
+ }
+
+ protected void configureEasingAdapter() {
+ if (mEasing == null) {
+ return;
+ }
+ int last = mNumberOfStages - 1;
+ float initialVelocity = mStage[last].mStartV;
+ float distance = mStage[last].mEndPos - mStage[last].mStartPos;
+ float duration = mStage[last].mEndTime - mStage[last].mStartTime;
+ double baseVel = mEasing.getDiff(0.0);
+ mEasingAdapterB = initialVelocity / (baseVel * distance);
+ mEasingAdapterA = 1 - mEasingAdapterB;
+ mEasingAdapterDistance = distance;
+ double easingDuration =
+ (Math.sqrt(4 * mEasingAdapterA + mEasingAdapterB * mEasingAdapterB)
+ - mEasingAdapterB)
+ / (2 * mEasingAdapterA);
+ mTotalEasingDuration = (float) (easingDuration + mStage[last].mStartTime);
+ }
+
+ interface Easing {
+ double get(double t);
+
+ double getDiff(double t);
+
+ Easing clone();
+ }
+
+ class Stage {
+ private float mStartV = 0;
+ private float mStartPos = 0;
+ private float mStartTime = 0;
+ private float mEndV = 0;
+ private float mEndPos = 0;
+ private float mEndTime = 0;
+ private float mDeltaV = 0;
+ private float mDeltaT = 0;
+ final int mStage;
+
+ Stage(int n) {
+ mStage = n;
+ }
+
+ void setUp(
+ float startV,
+ float startPos,
+ float startTime,
+ float endV,
+ float endPos,
+ float endTime) {
+ this.mStartV = startV;
+ this.mStartPos = startPos;
+ this.mStartTime = startTime;
+ this.mEndV = endV;
+ this.mEndTime = endTime;
+ this.mEndPos = endPos;
+ mDeltaV = this.mEndV - this.mStartV;
+ mDeltaT = this.mEndTime - this.mStartTime;
+ }
+
+ float getPos(float t) {
+ float dt = t - mStartTime;
+ float pt = dt / mDeltaT;
+ float v = mStartV + mDeltaV * pt;
+ return dt * (mStartV + v) / 2 + mStartPos;
+ }
+
+ float getVel(float t) {
+ float dt = t - mStartTime;
+ float pt = dt / (mEndTime - mStartTime);
+ return mStartV + mDeltaV * pt;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
index 57a804284f0d..3fba8acf8bca 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.types;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -47,23 +49,26 @@ public class BooleanConstant implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mValue);
}
@Override
public void apply(RemoteContext context) {}
+ @NonNull
@Override
public String deepToString(String indent) {
return toString();
}
+ @NonNull
@Override
public String toString() {
return "BooleanConstant[" + mId + "] = " + mValue + "";
}
+ @NonNull
public static String name() {
return "OrigamiBoolean";
}
@@ -79,20 +84,20 @@ public class BooleanConstant implements Operation {
* @param id
* @param value
*/
- public static void apply(WireBuffer buffer, int id, boolean value) {
+ public static void apply(@NonNull WireBuffer buffer, int id, boolean value) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeBoolean(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
boolean value = buffer.readBoolean();
operations.add(new BooleanConstant(id, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, "BooleanConstant")
.description("A boolean and its associated id")
.field(DocumentedOperation.INT, "id", "id of Int")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index 3ef9db9de915..79f2a8d8dec5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.types;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -37,25 +39,28 @@ public class IntegerConstant implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mValue);
}
@Override
- public void apply(RemoteContext context) {
+ public void apply(@NonNull RemoteContext context) {
context.loadInteger(mId, mValue);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return toString();
}
+ @NonNull
@Override
public String toString() {
return "IntegerConstant[" + mId + "] = " + mValue + "";
}
+ @NonNull
public static String name() {
return "IntegerConstant";
}
@@ -71,20 +76,20 @@ public class IntegerConstant implements Operation {
* @param textId
* @param value
*/
- public static void apply(WireBuffer buffer, int textId, int value) {
+ public static void apply(@NonNull WireBuffer buffer, int textId, int value) {
buffer.start(Operations.DATA_INT);
buffer.writeInt(textId);
buffer.writeInt(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
int value = buffer.readInt();
operations.add(new IntegerConstant(id, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", id(), "IntegerConstant")
.description("A integer and its associated id")
.field(DocumentedOperation.INT, "id", "id of Int")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index 6d51d194708f..01672b469728 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.types;
import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
+import android.annotation.NonNull;
+
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -47,7 +49,7 @@ public class LongConstant implements Operation {
}
@Override
- public void write(WireBuffer buffer) {
+ public void write(@NonNull WireBuffer buffer) {
apply(buffer, mId, mValue);
}
@@ -56,11 +58,13 @@ public class LongConstant implements Operation {
context.putObject(mId, this);
}
+ @NonNull
@Override
public String deepToString(String indent) {
return toString();
}
+ @NonNull
@Override
public String toString() {
return "LongConstant[" + mId + "] = " + mValue + "";
@@ -73,20 +77,20 @@ public class LongConstant implements Operation {
* @param id
* @param value
*/
- public static void apply(WireBuffer buffer, int id, long value) {
+ public static void apply(@NonNull WireBuffer buffer, int id, long value) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeLong(value);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
int id = buffer.readInt();
long value = buffer.readLong();
operations.add(new LongConstant(id, value));
}
- public static void documentation(DocumentationBuilder doc) {
+ public static void documentation(@NonNull DocumentationBuilder doc) {
doc.operation("Expressions Operations", OP_CODE, "LongConstant")
.description("A boolean and its associated id")
.field(DocumentedOperation.INT, "id", "id of Int")
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index 906282c1dde3..aaee9c565fbb 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -112,6 +112,16 @@ public class RemoteComposeDocument {
}
/**
+ * Gets a array of Names of the named variables of a specific type defined in the doc.
+ *
+ * @param type the type of variable NamedVariable.COLOR_TYPE, STRING_TYPE, etc
+ * @return array of name or null
+ */
+ public String[] getNamedVariables(int type) {
+ return mDocument.getNamedVariables(type);
+ }
+
+ /**
* Return a component associated with id
*
* @param id the component id
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 06bf4cdb0a0d..cc74b119866d 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -21,11 +21,14 @@ import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
+import android.view.HapticFeedbackConstants;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
@@ -57,11 +60,7 @@ public class RemoteComposePlayer extends FrameLayout {
* @param debugFlags 1 to set debug on
*/
public void setDebug(int debugFlags) {
- if (debugFlags == 1) {
- mInner.setDebug(true);
- } else {
- mInner.setDebug(false);
- }
+ mInner.setDebug(debugFlags);
}
public RemoteComposeDocument getDocument() {
@@ -82,6 +81,14 @@ public class RemoteComposePlayer extends FrameLayout {
mInner.setDocument(null);
}
mapColors();
+ mInner.setHapticEngine(
+ new CoreDocument.HapticEngine() {
+
+ @Override
+ public void haptic(int type) {
+ provideHapticFeedback(type);
+ }
+ });
}
/**
@@ -259,13 +266,40 @@ public class RemoteComposePlayer extends FrameLayout {
/**
* This returns a list of colors that have names in the Document.
*
- * @return
+ * @return the names of named Strings or null
*/
public String[] getNamedColors() {
return mInner.getNamedColors();
}
/**
+ * This returns a list of floats that have names in the Document.
+ *
+ * @return return the names of named floats in the document
+ */
+ public String[] getNamedFloats() {
+ return mInner.getNamedVariables(NamedVariable.FLOAT_TYPE);
+ }
+
+ /**
+ * This returns a list of string name that have names in the Document.
+ *
+ * @return the name of named string (not the string itself)
+ */
+ public String[] getNamedStrings() {
+ return mInner.getNamedVariables(NamedVariable.STRING_TYPE);
+ }
+
+ /**
+ * This returns a list of images that have names in the Document.
+ *
+ * @return
+ */
+ public String[] getNamedImages() {
+ return mInner.getNamedVariables(NamedVariable.IMAGE_TYPE);
+ }
+
+ /**
* This sets a color based on its name. Overriding the color set in the document.
*
* @param colorName Name of the color
@@ -481,4 +515,32 @@ public class RemoteComposePlayer extends FrameLayout {
return color;
}
}
+
+ private static int[] sHapticTable = {
+ HapticFeedbackConstants.NO_HAPTICS,
+ HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.KEYBOARD_TAP,
+ HapticFeedbackConstants.CLOCK_TICK,
+ HapticFeedbackConstants.CONTEXT_CLICK,
+ HapticFeedbackConstants.KEYBOARD_PRESS,
+ HapticFeedbackConstants.KEYBOARD_RELEASE,
+ HapticFeedbackConstants.VIRTUAL_KEY_RELEASE,
+ HapticFeedbackConstants.TEXT_HANDLE_MOVE,
+ HapticFeedbackConstants.GESTURE_START,
+ HapticFeedbackConstants.GESTURE_END,
+ HapticFeedbackConstants.CONFIRM,
+ HapticFeedbackConstants.REJECT,
+ HapticFeedbackConstants.TOGGLE_ON,
+ HapticFeedbackConstants.TOGGLE_OFF,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE,
+ HapticFeedbackConstants.DRAG_START,
+ HapticFeedbackConstants.SEGMENT_TICK,
+ HapticFeedbackConstants.SEGMENT_FREQUENT_TICK,
+ };
+
+ private void provideHapticFeedback(int type) {
+ performHapticFeedback(sHapticTable[type % sHapticTable.length]);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index f59a0d3fa015..0b650a93c9db 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -26,6 +26,8 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
import android.graphics.RuntimeShader;
import android.graphics.Shader;
import android.graphics.SweepGradient;
@@ -51,6 +53,8 @@ public class AndroidPaintContext extends PaintContext {
List<Paint> mPaintList = new ArrayList<>();
Canvas mCanvas;
Rect mTmpRect = new Rect(); // use in calculation of bounds
+ RenderNode mNode = null;
+ Canvas mPreviousCanvas = null;
public AndroidPaintContext(RemoteContext context, Canvas canvas) {
super(context);
@@ -122,6 +126,53 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
+ public void startGraphicsLayer(int w, int h) {
+ mNode = new RenderNode("layer");
+ mNode.setPosition(0, 0, w, h);
+ mPreviousCanvas = mCanvas;
+ mCanvas = mNode.beginRecording();
+ }
+
+ @Override
+ public void setGraphicsLayer(
+ float scaleX,
+ float scaleY,
+ float rotationX,
+ float rotationY,
+ float rotationZ,
+ float shadowElevation,
+ float transformOriginX,
+ float transformOriginY,
+ float alpha,
+ int renderEffectId) {
+ if (mNode == null) {
+ return;
+ }
+ mNode.setScaleX(scaleX);
+ mNode.setScaleY(scaleY);
+ mNode.setRotationX(rotationX);
+ mNode.setRotationY(rotationY);
+ mNode.setRotationZ(rotationZ);
+ mNode.setPivotX(transformOriginX * mNode.getWidth());
+ mNode.setPivotY(transformOriginY * mNode.getHeight());
+ mNode.setAlpha(alpha);
+ if (renderEffectId == 1) {
+
+ RenderEffect effect = RenderEffect.createBlurEffect(8f, 8f, Shader.TileMode.CLAMP);
+ mNode.setRenderEffect(effect);
+ }
+ }
+
+ @Override
+ public void endGraphicsLayer() {
+ mNode.endRecording();
+ mCanvas = mPreviousCanvas;
+ mCanvas.drawRenderNode(mNode);
+ // node.discardDisplayList();
+ mNode = null;
+ }
+
+ @Override
public void translate(float translateX, float translateY) {
mCanvas.translate(translateX, translateY);
}
@@ -241,6 +292,8 @@ public class AndroidPaintContext extends PaintContext {
if (start != 0) {
textToPaint = textToPaint.substring(start);
}
+ } else if (end > textToPaint.length()) {
+ textToPaint = textToPaint.substring(start);
} else {
textToPaint = textToPaint.substring(start, end);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
index f9b22a25ceab..f28e85a44c1b 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
@@ -18,6 +18,7 @@ package com.android.internal.widget.remotecompose.player.platform;
import android.graphics.Bitmap;
import android.graphics.Path;
import android.graphics.PathIterator;
+import android.util.Log;
import com.android.internal.widget.remotecompose.core.Platform;
import com.android.internal.widget.remotecompose.core.operations.PathData;
@@ -27,6 +28,8 @@ import java.util.Arrays;
/** Services that are needed to be provided by the platform during encoding. */
public class AndroidPlatformServices implements Platform {
+ private static final String LOG_TAG = "RemoteCompose";
+
@Override
public byte[] imageToByteArray(Object image) {
if (image instanceof Bitmap) {
@@ -67,6 +70,24 @@ public class AndroidPlatformServices implements Platform {
return null;
}
+ @Override
+ public void log(LogCategory category, String message) {
+ switch (category) {
+ case DEBUG:
+ Log.d(LOG_TAG, message);
+ break;
+ case INFO:
+ Log.i(LOG_TAG, message);
+ break;
+ case WARN:
+ Log.w(LOG_TAG, message);
+ break;
+ default:
+ Log.e(LOG_TAG, message);
+ break;
+ }
+ }
+
private float[] androidPathToFloatArray(Path path) {
PathIterator i = path.getPathIterator();
int estimatedSize = 0;
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index e7c0cc8a915d..7a7edba160c8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -20,6 +20,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
@@ -143,9 +144,9 @@ class AndroidRemoteContext extends RemoteContext {
}
@Override
- public void runNamedAction(int id) {
+ public void runNamedAction(int id, Object value) {
String text = getText(id);
- mDocument.runNamedAction(text);
+ mDocument.runNamedAction(text, value);
}
/**
@@ -200,6 +201,11 @@ class AndroidRemoteContext extends RemoteContext {
}
@Override
+ public void overrideFloat(int id, float value) {
+ mRemoteComposeState.overrideFloat(id, value);
+ }
+
+ @Override
public void loadInteger(int id, int value) {
mRemoteComposeState.updateInteger(id, value);
}
@@ -268,6 +274,11 @@ class AndroidRemoteContext extends RemoteContext {
return (ShaderData) mRemoteComposeState.getFromId(id);
}
+ @Override
+ public void addTouchListener(TouchListener touchExpression) {
+ mDocument.addTouchListener(touchExpression);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -285,4 +296,8 @@ class AndroidRemoteContext extends RemoteContext {
String metadata = (String) mRemoteComposeState.getFromId(metadataId);
mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
}
+
+ public void hapticEffect(int type) {
+ mDocument.haptic(type);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 7de6988157b7..b54ed8a77ec5 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -21,6 +21,7 @@ import android.graphics.Color;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.View;
import android.widget.FrameLayout;
@@ -38,7 +39,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
RemoteComposeDocument mDocument = null;
int mTheme = Theme.LIGHT;
boolean mInActionDown = false;
- boolean mDebug = false;
+ int mDebug = 0;
boolean mHasClickAreas = false;
Point mActionDownPoint = new Point(0, 0);
AndroidRemoteContext mARContext = new AndroidRemoteContext();
@@ -65,14 +66,14 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
}
}
- public void setDebug(boolean value) {
+ public void setDebug(int value) {
if (mDebug != value) {
mDebug = value;
if (USE_VIEW_AREA_CLICK) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof ClickAreaView) {
- ((ClickAreaView) child).setDebug(mDebug);
+ ((ClickAreaView) child).setDebug(mDebug == 1);
}
}
}
@@ -107,7 +108,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
ClickAreaView viewArea =
new ClickAreaView(
getContext(),
- mDebug,
+ mDebug == 1,
area.getId(),
area.getContentDescription(),
area.getMetadata());
@@ -128,6 +129,10 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
}
}
+ public void setHapticEngine(CoreDocument.HapticEngine engine) {
+ mDocument.getDocument().setHapticEngine(engine);
+ }
+
@Override
public void onViewDetachedFromWindow(View view) {
removeAllViews();
@@ -138,6 +143,16 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
}
/**
+ * Gets a array of Names of the named variables of a specific type defined in the loaded doc.
+ *
+ * @param type the type of variable NamedVariable.COLOR_TYPE, STRING_TYPE, etc
+ * @return array of name or null
+ */
+ public String[] getNamedVariables(int type) {
+ return mDocument.getNamedVariables(type);
+ }
+
+ /**
* set the color associated with this name.
*
* @param colorName Name of color typically "android.xxx"
@@ -198,7 +213,12 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
this.mTheme = theme;
}
+ private VelocityTracker mVelocityTracker = null;
+
public boolean onTouchEvent(MotionEvent event) {
+ int index = event.getActionIndex();
+ int action = event.getActionMasked();
+ int pointerId = event.getPointerId(index);
if (USE_VIEW_AREA_CLICK && mHasClickAreas) {
return super.onTouchEvent(event);
}
@@ -207,15 +227,51 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mActionDownPoint.x = (int) event.getX();
mActionDownPoint.y = (int) event.getY();
mInActionDown = true;
+ CoreDocument doc = mDocument.getDocument();
+ if (doc.hasTouchListener()) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ mVelocityTracker.addMovement(event);
+ doc.touchDown(mARContext, event.getX(), event.getY());
+ }
return true;
+
case MotionEvent.ACTION_CANCEL:
mInActionDown = false;
+ doc = mDocument.getDocument();
+ if (doc.hasTouchListener()) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float dx = mVelocityTracker.getXVelocity(pointerId);
+ float dy = mVelocityTracker.getYVelocity(pointerId);
+ doc.touchCancel(mARContext, event.getX(), event.getY(), dx, dy);
+ }
return true;
case MotionEvent.ACTION_UP:
mInActionDown = false;
performClick();
+ doc = mDocument.getDocument();
+ if (doc.hasTouchListener()) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float dx = mVelocityTracker.getXVelocity(pointerId);
+ float dy = mVelocityTracker.getYVelocity(pointerId);
+ doc.touchUp(mARContext, event.getX(), event.getY(), dx, dy);
+ }
return true;
+
case MotionEvent.ACTION_MOVE:
+ if (mInActionDown) {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(event);
+ doc = mDocument.getDocument();
+ boolean repaint = doc.touchDrag(mARContext, event.getX(), event.getY());
+ if (repaint) {
+ invalidate();
+ }
+ }
+ }
}
return false;
}
@@ -292,7 +348,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mARContext.mWidth = getWidth();
mARContext.mHeight = getHeight();
mDocument.paint(mARContext, mTheme);
- if (mDebug) {
+ if (mDebug == 1) {
mCount++;
if (System.nanoTime() - mTime > 1000000000L) {
System.out.println(" count " + mCount + " fps");
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2bb6e71e9000..25412581303c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -76,7 +76,6 @@ cc_library_shared_for_libandroid_runtime {
"android_content_res_ApkAssets.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
- "android_os_Trace.cpp",
"android_text_AndroidCharacter.cpp",
"android_util_AssetManager.cpp",
"android_util_EventLog.cpp",
@@ -104,10 +103,6 @@ cc_library_shared_for_libandroid_runtime {
"system/media/private/camera/include",
],
- shared_libs: [
- "libtracing_perfetto",
- ],
-
static_libs: [
"libziparchive_for_incfs",
"libguiflags",
@@ -190,6 +185,7 @@ cc_library_shared_for_libandroid_runtime {
"android_os_ServiceManagerNative.cpp",
"android_os_SharedMemory.cpp",
"android_os_storage_StorageManager.cpp",
+ "android_os_Trace.cpp",
"android_os_UEventObserver.cpp",
"android_os_incremental_IncrementalManager.cpp",
"android_net_LocalSocketImpl.cpp",
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 50252c11ffb1..42406147b2f0 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -538,7 +538,7 @@ static bool attributionSourceStateForJavaParcel(JNIEnv *env, jobject jClientAttr
return false;
}
- if (!(useContextAttributionSource && flags::use_context_attribution_source())) {
+ if (!(useContextAttributionSource && flags::data_delivery_permission_checks())) {
clientAttribution.uid = Camera::USE_CALLING_UID;
clientAttribution.pid = Camera::USE_CALLING_PID;
}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 69f633420a0d..f1c4913fe006 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -83,68 +83,53 @@ static struct {
jmethodID ctor;
} gRegionClassInfo;
-static Mutex gHandleMutex;
-
-
-// --- NativeInputWindowHandle ---
-
-NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) :
- mObjWeak(objWeak) {
-}
-
-NativeInputWindowHandle::~NativeInputWindowHandle() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mObjWeak);
+// --- Global functions ---
- // Clear the weak reference to the layer handle and flush any binder ref count operations so we
- // do not hold on to any binder references.
- // TODO(b/139697085) remove this after it can be flushed automatically
- mInfo.touchableRegionCropHandle.clear();
- IPCThreadState::self()->flushCommands();
-}
+sp<gui::WindowInfoHandle> android_view_InputWindowHandle_getHandle(JNIEnv* env, jobject obj) {
+ sp<gui::WindowInfoHandle> handle = [&]() {
+ jlong cachedHandle = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr);
+ if (cachedHandle) {
+ return sp<gui::WindowInfoHandle>::fromExisting(
+ reinterpret_cast<gui::WindowInfoHandle*>(cachedHandle));
+ }
-jobject NativeInputWindowHandle::getInputWindowHandleObjLocalRef(JNIEnv* env) {
- return env->NewLocalRef(mObjWeak);
-}
+ auto newHandle = sp<gui::WindowInfoHandle>::make();
+ newHandle->incStrong((void*)android_view_InputWindowHandle_getHandle);
+ env->SetLongField(obj, gInputWindowHandleClassInfo.ptr,
+ reinterpret_cast<jlong>(newHandle.get()));
+ return newHandle;
+ }();
-bool NativeInputWindowHandle::updateInfo() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject obj = env->NewLocalRef(mObjWeak);
- if (!obj) {
- releaseChannel();
- return false;
- }
+ gui::WindowInfo* windowInfo = handle->editInfo();
- mInfo.touchableRegion.clear();
+ windowInfo->touchableRegion.clear();
jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token);
if (tokenObj) {
- mInfo.token = ibinderForJavaObject(env, tokenObj);
+ windowInfo->token = ibinderForJavaObject(env, tokenObj);
env->DeleteLocalRef(tokenObj);
} else {
- mInfo.token.clear();
+ windowInfo->token.clear();
}
- mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
+ windowInfo->name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
- mInfo.dispatchingTimeout = std::chrono::milliseconds(
+ windowInfo->dispatchingTimeout = std::chrono::milliseconds(
env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
ScopedLocalRef<jobject> frameObj(env,
env->GetObjectField(obj, gInputWindowHandleClassInfo.frame));
- mInfo.frame = JNICommon::rectFromObj(env, frameObj.get());
+ windowInfo->frame = JNICommon::rectFromObj(env, frameObj.get());
- mInfo.surfaceInset = env->GetIntField(obj,
- gInputWindowHandleClassInfo.surfaceInset);
- mInfo.globalScaleFactor = env->GetFloatField(obj,
- gInputWindowHandleClassInfo.scaleFactor);
+ windowInfo->surfaceInset = env->GetIntField(obj, gInputWindowHandleClassInfo.surfaceInset);
+ windowInfo->globalScaleFactor =
+ env->GetFloatField(obj, gInputWindowHandleClassInfo.scaleFactor);
- jobject regionObj = env->GetObjectField(obj,
- gInputWindowHandleClassInfo.touchableRegion);
+ jobject regionObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.touchableRegion);
if (regionObj) {
for (graphics::RegionIterator it(env, regionObj); !it.isDone(); it.next()) {
ARect rect = it.getRect();
- mInfo.addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom));
+ windowInfo->addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom));
}
env->DeleteLocalRef(regionObj);
}
@@ -153,49 +138,55 @@ bool NativeInputWindowHandle::updateInfo() {
env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
const auto type = static_cast<WindowInfo::Type>(
env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
- mInfo.layoutParamsFlags = flags;
- mInfo.layoutParamsType = type;
+ windowInfo->layoutParamsFlags = flags;
+ windowInfo->layoutParamsType = type;
- mInfo.inputConfig = static_cast<gui::WindowInfo::InputConfig>(
+ windowInfo->inputConfig = static_cast<gui::WindowInfo::InputConfig>(
env->GetIntField(obj, gInputWindowHandleClassInfo.inputConfig));
- mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
+ windowInfo->touchOcclusionMode = static_cast<TouchOcclusionMode>(
env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
- mInfo.ownerPid = gui::Pid{env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid)};
- mInfo.ownerUid = gui::Uid{
+ windowInfo->ownerPid = gui::Pid{env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid)};
+ windowInfo->ownerUid = gui::Uid{
static_cast<uid_t>(env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid))};
- mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
- mInfo.displayId =
+ windowInfo->packageName =
+ getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
+ windowInfo->displayId =
ui::LogicalDisplayId{env->GetIntField(obj, gInputWindowHandleClassInfo.displayId)};
- jobject inputApplicationHandleObj = env->GetObjectField(obj,
- gInputWindowHandleClassInfo.inputApplicationHandle);
+ jobject inputApplicationHandleObj =
+ env->GetObjectField(obj, gInputWindowHandleClassInfo.inputApplicationHandle);
if (inputApplicationHandleObj) {
std::shared_ptr<InputApplicationHandle> inputApplicationHandle =
android_view_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
if (inputApplicationHandle != nullptr) {
inputApplicationHandle->updateInfo();
- mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
+ windowInfo->applicationInfo = *(inputApplicationHandle->getInfo());
}
env->DeleteLocalRef(inputApplicationHandleObj);
}
- mInfo.replaceTouchableRegionWithCrop = env->GetBooleanField(obj,
- gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop);
+ windowInfo->replaceTouchableRegionWithCrop =
+ env->GetBooleanField(obj, gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop);
- jobject weakSurfaceCtrl = env->GetObjectField(obj,
- gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl);
+ jobject weakSurfaceCtrl =
+ env->GetObjectField(obj,
+ gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl);
bool touchableRegionCropHandleSet = false;
if (weakSurfaceCtrl) {
// Promote java weak reference.
- jobject strongSurfaceCtrl = env->CallObjectMethod(weakSurfaceCtrl,
- gInputWindowHandleClassInfo.touchableRegionSurfaceControl.get);
+ jobject strongSurfaceCtrl =
+ env->CallObjectMethod(weakSurfaceCtrl,
+ gInputWindowHandleClassInfo.touchableRegionSurfaceControl
+ .get);
if (strongSurfaceCtrl) {
- jlong mNativeObject = env->GetLongField(strongSurfaceCtrl,
- gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject);
+ jlong mNativeObject =
+ env->GetLongField(strongSurfaceCtrl,
+ gInputWindowHandleClassInfo.touchableRegionSurfaceControl
+ .mNativeObject);
if (mNativeObject) {
auto ctrl = reinterpret_cast<SurfaceControl *>(mNativeObject);
- mInfo.touchableRegionCropHandle = ctrl->getHandle();
+ windowInfo->touchableRegionCropHandle = ctrl->getHandle();
touchableRegionCropHandleSet = true;
}
env->DeleteLocalRef(strongSurfaceCtrl);
@@ -203,15 +194,15 @@ bool NativeInputWindowHandle::updateInfo() {
env->DeleteLocalRef(weakSurfaceCtrl);
}
if (!touchableRegionCropHandleSet) {
- mInfo.touchableRegionCropHandle.clear();
+ windowInfo->touchableRegionCropHandle.clear();
}
jobject windowTokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.windowToken);
if (windowTokenObj) {
- mInfo.windowToken = ibinderForJavaObject(env, windowTokenObj);
+ windowInfo->windowToken = ibinderForJavaObject(env, windowTokenObj);
env->DeleteLocalRef(windowTokenObj);
} else {
- mInfo.windowToken.clear();
+ windowInfo->windowToken.clear();
}
ScopedLocalRef<jobject>
@@ -220,41 +211,16 @@ bool NativeInputWindowHandle::updateInfo() {
gInputWindowHandleClassInfo
.focusTransferTarget));
if (focusTransferTargetObj.get()) {
- mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
+ windowInfo->focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
} else {
- mInfo.focusTransferTarget.clear();
+ windowInfo->focusTransferTarget.clear();
}
- env->DeleteLocalRef(obj);
- return true;
-}
-
-
-// --- Global functions ---
-
-sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
- JNIEnv* env, jobject inputWindowHandleObj) {
- if (!inputWindowHandleObj) {
- return NULL;
- }
-
- AutoMutex _l(gHandleMutex);
-
- jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr);
- NativeInputWindowHandle* handle;
- if (ptr) {
- handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
- } else {
- jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
- handle = new NativeInputWindowHandle(objWeak);
- handle->incStrong((void*)android_view_InputWindowHandle_getHandle);
- env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
- reinterpret_cast<jlong>(handle));
- }
return handle;
}
-jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowInfo windowInfo) {
+jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env,
+ const gui::WindowInfo& windowInfo) {
ScopedLocalRef<jobject>
applicationHandle(env,
android_view_InputApplicationHandle_fromInputApplicationInfo(
@@ -337,18 +303,15 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn
// --- JNI ---
static void android_view_InputWindowHandle_nativeDispose(JNIEnv* env, jobject obj) {
- AutoMutex _l(gHandleMutex);
-
jlong ptr = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr);
- if (ptr) {
- env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0);
-
- NativeInputWindowHandle* handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
- handle->decStrong((void*)android_view_InputWindowHandle_getHandle);
+ if (!ptr) {
+ return;
}
+ env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0);
+ auto handle = reinterpret_cast<gui::WindowInfoHandle*>(ptr);
+ handle->decStrong((void*)android_view_InputWindowHandle_getHandle);
}
-
static const JNINativeMethod gInputWindowHandleMethods[] = {
/* name, signature, funcPtr */
{ "nativeDispose", "()V",
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 408e0f1bfa36..aa375e9ef477 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -24,24 +24,11 @@
namespace android {
-class NativeInputWindowHandle : public gui::WindowInfoHandle {
-public:
- NativeInputWindowHandle(jweak objWeak);
- virtual ~NativeInputWindowHandle();
+sp<gui::WindowInfoHandle> android_view_InputWindowHandle_getHandle(JNIEnv* env,
+ jobject inputWindowHandleObj);
- jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
-
- virtual bool updateInfo();
-
-private:
- jweak mObjWeak;
-};
-
-extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
- JNIEnv* env, jobject inputWindowHandleObj);
-
-extern jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env,
- gui::WindowInfo windowInfo);
+jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env,
+ const gui::WindowInfo& windowInfo);
} // namespace android
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index df87a69f02ce..d3bf36e60345 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -979,14 +979,16 @@ static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong transactionObj,
static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobject inputWindow) {
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ if (!inputWindow) {
+ jniThrowNullPointerException(env, "InputWindowHandle is null");
+ return;
+ }
- sp<NativeInputWindowHandle> handle = android_view_InputWindowHandle_getHandle(
- env, inputWindow);
- handle->updateInfo();
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ sp<gui::WindowInfoHandle> info = android_view_InputWindowHandle_getHandle(env, inputWindow);
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- transaction->setInputWindowInfo(ctrl, *handle->getInfo());
+ transaction->setInputWindowInfo(ctrl, std::move(info));
}
static void nativeAddWindowInfosReportedListener(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -2403,6 +2405,11 @@ SurfaceComposerClient::Transaction* android_view_SurfaceTransaction_getNativeSur
}
}
+static void nativeEnableDebugLogCallPoints(JNIEnv* env, jclass clazz, jlong transactionObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ transaction->enableDebugLogCallPoints();
+}
+
static const JNINativeMethod sSurfaceControlMethods[] = {
// clang-format off
{"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
@@ -2649,6 +2656,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeNotifyShutdown", "()V",
(void*)nativeNotifyShutdown },
{"nativeSetLuts", "(JJ[F[I[I[I[I)V", (void*)nativeSetLuts },
+ {"nativeEnableDebugLogCallPoints", "(J)V", (void*)nativeEnableDebugLogCallPoints },
// clang-format on
};
diff --git a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
index bea5ffe31da0..a5b5057ecbac 100644
--- a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
@@ -49,27 +49,26 @@ static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) {
* to the supplied multi-state counter in accordance with the counter's state.
*/
static jboolean addCpuTimeInFreqDelta(
- jint uid, jlong counterNativePtr, jlong timestampMs,
+ JNIEnv *env, jint uid, jlong counterNativePtr, jlong timestampMs,
std::optional<std::vector<std::vector<uint64_t>>> timeInFreqDataNanos,
- jlong deltaOutContainerNativePtr) {
+ jlongArray deltaOut) {
if (!timeInFreqDataNanos) {
return false;
}
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr);
+ auto counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr);
size_t s = 0;
for (const auto &cluster : *timeInFreqDataNanos) s += cluster.size();
- std::vector<uint64_t> flattened;
- flattened.reserve(s);
- auto offset = flattened.begin();
+ battery::Uint64ArrayRW flattened(s);
+ uint64_t *out = flattened.dataRW();
+ auto offset = out;
for (const auto &cluster : *timeInFreqDataNanos) {
- flattened.insert(offset, cluster.begin(), cluster.end());
+ memcpy(offset, cluster.data(), cluster.size() * sizeof(uint64_t));
offset += cluster.size();
}
for (size_t i = 0; i < s; ++i) {
- flattened[i] /= NSEC_PER_MSEC;
+ out[i] /= NSEC_PER_MSEC;
}
if (s != counter->getCount(0).size()) { // Counter has at least one state
ALOGE("Mismatch between eBPF data size (%d) and the counter size (%d)", (int)s,
@@ -77,29 +76,32 @@ static jboolean addCpuTimeInFreqDelta(
return false;
}
- const std::vector<uint64_t> &delta = counter->updateValue(flattened, timestampMs);
- if (deltaOutContainerNativePtr) {
- std::vector<uint64_t> *vector =
- reinterpret_cast<std::vector<uint64_t> *>(deltaOutContainerNativePtr);
- *vector = delta;
+ const battery::Uint64Array &delta = counter->updateValue(flattened, timestampMs);
+ if (deltaOut) {
+ ScopedLongArrayRW scopedArray(env, deltaOut);
+ uint64_t *array = reinterpret_cast<uint64_t *>(scopedArray.get());
+ if (delta.data() != nullptr) {
+ memcpy(array, delta.data(), s * sizeof(uint64_t));
+ } else {
+ memset(array, 0, s * sizeof(uint64_t));
+ }
}
return true;
}
-static jboolean addDeltaFromBpf(jint uid, jlong counterNativePtr, jlong timestampMs,
- jlong deltaOutContainerNativePtr) {
- return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
- android::bpf::getUidCpuFreqTimes(uid), deltaOutContainerNativePtr);
+static jboolean addDeltaFromBpf(JNIEnv *env, jlong self, jint uid, jlong counterNativePtr,
+ jlong timestampMs, jlongArray deltaOut) {
+ return addCpuTimeInFreqDelta(env, uid, counterNativePtr, timestampMs,
+ android::bpf::getUidCpuFreqTimes(uid), deltaOut);
}
static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNativePtr,
jlong timestampMs, jobjectArray timeInFreqDataNanos,
- jlong deltaOutContainerNativePtr) {
+ jlongArray deltaOut) {
if (!timeInFreqDataNanos) {
- return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
- std::optional<std::vector<std::vector<uint64_t>>>(),
- deltaOutContainerNativePtr);
+ return addCpuTimeInFreqDelta(env, uid, counterNativePtr, timestampMs,
+ std::optional<std::vector<std::vector<uint64_t>>>(), deltaOut);
}
std::vector<std::vector<uint64_t>> timeInFreqData;
@@ -113,18 +115,16 @@ static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNati
}
timeInFreqData.push_back(cluster);
}
- return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional(timeInFreqData),
- deltaOutContainerNativePtr);
+ return addCpuTimeInFreqDelta(env, uid, counterNativePtr, timestampMs,
+ std::optional(timeInFreqData), deltaOut);
}
static const JNINativeMethod g_single_methods[] = {
{"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
-
- // @CriticalNative
- {"addDeltaFromBpf", "(IJJJ)Z", (void *)addDeltaFromBpf},
+ {"addDeltaFromBpf", "(IJJ[J)Z", (void *)addDeltaFromBpf},
// Used for testing
- {"addDeltaForTest", "(IJJ[[JJ)Z", (void *)addDeltaForTest},
+ {"addDeltaForTest", "(IJJ[[J[J)Z", (void *)addDeltaForTest},
};
int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index b3c41dfe81a1..7ffe0ed7c6cd 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -26,16 +26,40 @@
#include "core_jni_helpers.h"
namespace android {
+namespace battery {
+
+/**
+ * Implementation of Uint64Array that wraps a Java long[]. Since it uses the "critical"
+ * version of JNI array access (halting GC), any usage of this class must be extra quick.
+ */
+class JavaUint64Array : public Uint64Array {
+ JNIEnv *mEnv;
+ jlongArray mJavaArray;
+ uint64_t *mData;
+
+public:
+ JavaUint64Array(JNIEnv *env, jlongArray values) : Uint64Array(env->GetArrayLength(values)) {
+ mEnv = env;
+ mJavaArray = values;
+ mData = reinterpret_cast<uint64_t *>(mEnv->GetPrimitiveArrayCritical(mJavaArray, nullptr));
+ }
+
+ ~JavaUint64Array() override {
+ mEnv->ReleasePrimitiveArrayCritical(mJavaArray, mData, 0);
+ }
+
+ const uint64_t *data() const override {
+ return mData;
+ }
+};
static jlong native_init(jint stateCount, jint arrayLength) {
- battery::LongArrayMultiStateCounter *counter =
- new battery::LongArrayMultiStateCounter(stateCount, std::vector<uint64_t>(arrayLength));
+ auto *counter = new LongArrayMultiStateCounter(stateCount, Uint64Array(arrayLength));
return reinterpret_cast<jlong>(counter);
}
static void native_dispose(void *nativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
delete counter;
}
@@ -44,80 +68,63 @@ static jlong native_getReleaseFunc() {
}
static void native_setEnabled(jlong nativePtr, jboolean enabled, jlong timestamp) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
counter->setEnabled(enabled, timestamp);
}
static void native_setState(jlong nativePtr, jint state, jlong timestamp) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
counter->setState(state, timestamp);
}
static void native_copyStatesFrom(jlong nativePtrTarget, jlong nativePtrSource) {
- battery::LongArrayMultiStateCounter *counterTarget =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrTarget);
- battery::LongArrayMultiStateCounter *counterSource =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrSource);
+ auto *counterTarget = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrTarget);
+ auto *counterSource = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrSource);
counterTarget->copyStatesFrom(*counterSource);
}
-static void native_setValues(jlong nativePtr, jint state, jlong longArrayContainerNativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
- std::vector<uint64_t> *vector =
- reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
-
- counter->setValue(state, *vector);
+static void native_setValues(JNIEnv *env, jclass, jlong nativePtr, jint state, jlongArray values) {
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
+ counter->setValue(state, JavaUint64Array(env, values));
}
-static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr,
+static void native_updateValues(JNIEnv *env, jclass, jlong nativePtr, jlongArray values,
jlong timestamp) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
- std::vector<uint64_t> *vector =
- reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
-
- counter->updateValue(*vector, timestamp);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
+ counter->updateValue(JavaUint64Array(env, values), timestamp);
}
-static void native_incrementValues(jlong nativePtr, jlong longArrayContainerNativePtr,
+static void native_incrementValues(JNIEnv *env, jclass, jlong nativePtr, jlongArray values,
jlong timestamp) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
- std::vector<uint64_t> *vector =
- reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
-
- counter->incrementValue(*vector, timestamp);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
+ counter->incrementValue(JavaUint64Array(env, values), timestamp);
}
-static void native_addCounts(jlong nativePtr, jlong longArrayContainerNativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
- std::vector<uint64_t> *vector =
- reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
- counter->addValue(*vector);
+static void native_addCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values) {
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
+ counter->addValue(JavaUint64Array(env, values));
}
static void native_reset(jlong nativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
counter->reset();
}
-static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, jint state) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
- std::vector<uint64_t> *vector =
- reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
-
- *vector = counter->getCount(state);
+static void native_getCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values, jint state) {
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
+ ScopedLongArrayRW scopedArray(env, values);
+ auto *data = counter->getCount(state).data();
+ auto size = env->GetArrayLength(values);
+ auto *outData = scopedArray.get();
+ if (data == nullptr) {
+ memset(outData, 0, size * sizeof(uint64_t));
+ } else {
+ memcpy(outData, data, size * sizeof(uint64_t));
+ }
}
static jobject native_toString(JNIEnv *env, jclass, jlong nativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
return env->NewStringUTF(counter->toString().c_str());
}
@@ -137,20 +144,26 @@ static void throwWriteRE(JNIEnv *env, binder_status_t status) {
static void native_writeToParcel(JNIEnv *env, jclass, jlong nativePtr, jobject jParcel,
jint flags) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
ndk::ScopedAParcel parcel(AParcel_fromJavaParcel(env, jParcel));
uint16_t stateCount = counter->getStateCount();
THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), stateCount));
// LongArrayMultiStateCounter has at least state 0
- const std::vector<uint64_t> &anyState = counter->getCount(0);
+ const Uint64Array &anyState = counter->getCount(0);
THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeInt32(parcel.get(), anyState.size()));
for (battery::state_t state = 0; state < stateCount; state++) {
- THROW_AND_RETURN_ON_WRITE_ERROR(
- ndk::AParcel_writeVector(parcel.get(), counter->getCount(state)));
+ const Uint64Array &value = counter->getCount(state);
+ if (value.data() == nullptr) {
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeBool(parcel.get(), false));
+ } else {
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeBool(parcel.get(), true));
+ for (size_t i = 0; i < anyState.size(); i++) {
+ THROW_AND_RETURN_ON_WRITE_ERROR(AParcel_writeUint64(parcel.get(), value.data()[i]));
+ }
+ }
}
}
@@ -183,40 +196,37 @@ static jlong native_initFromParcel(JNIEnv *env, jclass, jobject jParcel) {
int32_t arrayLength;
THROW_AND_RETURN_ON_READ_ERROR(AParcel_readInt32(parcel.get(), &arrayLength));
- auto counter = std::make_unique<battery::LongArrayMultiStateCounter>(stateCount,
- std::vector<uint64_t>(
- arrayLength));
-
- std::vector<uint64_t> value;
- value.reserve(arrayLength);
-
+ auto counter =
+ std::make_unique<LongArrayMultiStateCounter>(stateCount, Uint64Array(arrayLength));
+ Uint64ArrayRW array(arrayLength);
for (battery::state_t state = 0; state < stateCount; state++) {
- THROW_AND_RETURN_ON_READ_ERROR(ndk::AParcel_readVector(parcel.get(), &value));
- counter->setValue(state, value);
+ bool hasValues;
+ THROW_AND_RETURN_ON_READ_ERROR(AParcel_readBool(parcel.get(), &hasValues));
+ if (hasValues) {
+ for (int i = 0; i < arrayLength; i++) {
+ THROW_AND_RETURN_ON_READ_ERROR(
+ AParcel_readUint64(parcel.get(), &(array.dataRW()[i])));
+ }
+ counter->setValue(state, array);
+ }
}
return reinterpret_cast<jlong>(counter.release());
}
static jint native_getStateCount(jlong nativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
return counter->getStateCount();
}
static jint native_getArrayLength(jlong nativePtr) {
- battery::LongArrayMultiStateCounter *counter =
- reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
// LongArrayMultiStateCounter has at least state 0
- const std::vector<uint64_t> &anyState = counter->getCount(0);
+ const Uint64Array &anyState = counter->getCount(0);
return anyState.size();
}
-static jlong native_init_LongArrayContainer(jint length) {
- return reinterpret_cast<jlong>(new std::vector<uint64_t>(length));
-}
-
static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
// @CriticalNative
{"native_init", "(II)J", (void *)native_init},
@@ -228,18 +238,18 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
{"native_setState", "(JIJ)V", (void *)native_setState},
// @CriticalNative
{"native_copyStatesFrom", "(JJ)V", (void *)native_copyStatesFrom},
- // @CriticalNative
- {"native_setValues", "(JIJ)V", (void *)native_setValues},
- // @CriticalNative
- {"native_updateValues", "(JJJ)V", (void *)native_updateValues},
- // @CriticalNative
- {"native_incrementValues", "(JJJ)V", (void *)native_incrementValues},
- // @CriticalNative
- {"native_addCounts", "(JJ)V", (void *)native_addCounts},
+ // @FastNative
+ {"native_setValues", "(JI[J)V", (void *)native_setValues},
+ // @FastNative
+ {"native_updateValues", "(J[JJ)V", (void *)native_updateValues},
+ // @FastNative
+ {"native_incrementValues", "(J[JJ)V", (void *)native_incrementValues},
+ // @FastNative
+ {"native_addCounts", "(J[J)V", (void *)native_addCounts},
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
- // @CriticalNative
- {"native_getCounts", "(JJI)V", (void *)native_getCounts},
+ // @FastNative
+ {"native_getCounts", "(J[JI)V", (void *)native_getCounts},
// @FastNative
{"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
// @FastNative
@@ -252,91 +262,12 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
{"native_getArrayLength", "(J)I", (void *)native_getArrayLength},
};
-/////////////////////// LongArrayMultiStateCounter.LongArrayContainer ////////////////////////
-
-static void native_dispose_LongArrayContainer(jlong nativePtr) {
- std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
- delete vector;
-}
-
-static jlong native_getReleaseFunc_LongArrayContainer() {
- return reinterpret_cast<jlong>(native_dispose_LongArrayContainer);
-}
-
-static void native_setValues_LongArrayContainer(JNIEnv *env, jclass, jlong nativePtr,
- jlongArray jarray) {
- std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
- ScopedLongArrayRO scopedArray(env, jarray);
- const uint64_t *array = reinterpret_cast<const uint64_t *>(scopedArray.get());
- uint8_t size = scopedArray.size();
-
- // Boundary checks are performed in the Java layer
- std::copy(array, array + size, vector->data());
-}
-
-static void native_getValues_LongArrayContainer(JNIEnv *env, jclass, jlong nativePtr,
- jlongArray jarray) {
- std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
- ScopedLongArrayRW scopedArray(env, jarray);
-
- // Boundary checks are performed in the Java layer
- std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get());
-}
-
-static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jclass, jlong nativePtr,
- jlongArray jarray, jintArray jindexMap) {
- std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
- ScopedLongArrayRW scopedArray(env, jarray);
- ScopedIntArrayRO scopedIndexMap(env, jindexMap);
-
- const uint64_t *data = vector->data();
- uint64_t *array = reinterpret_cast<uint64_t *>(scopedArray.get());
- const uint8_t size = scopedArray.size();
-
- for (int i = 0; i < size; i++) {
- array[i] = 0;
- }
-
- bool nonZero = false;
- for (size_t i = 0; i < vector->size(); i++) {
- jint index = scopedIndexMap[i];
- if (index < 0 || index >= size) {
- jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
- "Index %d is out of bounds: [0, %d]", index, size - 1);
- return false;
- }
-
- if (data[i] != 0L) {
- array[index] += data[i];
- nonZero = true;
- }
- }
-
- return nonZero;
-}
-
-static const JNINativeMethod g_LongArrayContainer_methods[] = {
- // @CriticalNative
- {"native_init", "(I)J", (void *)native_init_LongArrayContainer},
- // @CriticalNative
- {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc_LongArrayContainer},
- // @FastNative
- {"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer},
- // @FastNative
- {"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer},
- // @FastNative
- {"native_combineValues", "(J[J[I)Z", (void *)native_combineValues_LongArrayContainer},
-};
+} // namespace battery
int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) {
// 0 represents success, thus "|" and not "&"
return RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter",
- g_LongArrayMultiStateCounter_methods,
- NELEM(g_LongArrayMultiStateCounter_methods)) |
- RegisterMethodsOrDie(env,
- "com/android/internal/os/LongArrayMultiStateCounter"
- "$LongArrayContainer",
- g_LongArrayContainer_methods, NELEM(g_LongArrayContainer_methods));
+ battery::g_LongArrayMultiStateCounter_methods,
+ NELEM(battery::g_LongArrayMultiStateCounter_methods));
}
-
} // namespace android
diff --git a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
index 56d3fbba9458..b3bfd0bc5e09 100644
--- a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
@@ -28,7 +28,7 @@ namespace android {
namespace battery {
-typedef battery::MultiStateCounter<int64_t> LongMultiStateCounter;
+typedef battery::MultiStateCounter<int64_t, int64_t> LongMultiStateCounter;
template <>
bool LongMultiStateCounter::delta(const int64_t &previousValue, const int64_t &newValue,
@@ -47,12 +47,6 @@ void LongMultiStateCounter::add(int64_t *value1, const int64_t &value2, const ui
*value1 += value2;
}
}
-
-template <>
-std::string LongMultiStateCounter::valueToString(const int64_t &v) const {
- return std::to_string(v);
-}
-
} // namespace battery
static inline battery::LongMultiStateCounter *asLongMultiStateCounter(const jlong nativePtr) {
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 3747299ccab6..7fca1175f3d4 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -87,7 +87,6 @@ extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv* env);
-extern int register_android_os_Trace(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
@@ -133,7 +132,6 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
#endif
{"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
{"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
- {"android.os.Trace", REG_JNI(register_android_os_Trace)},
{"android.text.AndroidCharacter", REG_JNI(register_android_text_AndroidCharacter)},
{"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
{"android.util.Log", REG_JNI(register_android_util_Log)},
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 606e829c41fa..6af742fb23f4 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -104,6 +104,7 @@ message SecureSettingsProto {
optional SettingProto accessibility_single_finger_panning_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_gesture_targets = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto display_daltonizer_saturation_level = 58 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_key_gesture_targets = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 654d83c827c9..407790c89202 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -465,6 +465,7 @@ message WindowStateProto {
repeated .android.graphics.RectProto unrestricted_keep_clear_areas = 46;
repeated .android.view.InsetsSourceProto mergedLocalInsetsSources = 47;
optional int32 requested_visible_types = 48;
+ optional .android.graphics.RectProto dim_bounds = 49;
}
message IdentifierProto {
diff --git a/core/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto
index 97350ef90eec..fb907196bfc7 100644
--- a/core/proto/android/service/appwidget.proto
+++ b/core/proto/android/service/appwidget.proto
@@ -20,6 +20,8 @@ package android.service.appwidget;
option java_multiple_files = true;
option java_outer_classname = "AppWidgetServiceProto";
+import "frameworks/base/core/proto/android/widget/remoteviews.proto";
+
// represents the object holding the dump info of the app widget service
message AppWidgetServiceDumpProto {
repeated WidgetProto widgets = 1; // the array of bound widgets
@@ -38,3 +40,14 @@ message WidgetProto {
optional int32 maxHeight = 9;
optional bool restoreCompleted = 10;
}
+
+// represents a set of widget previews for a particular provider
+message GeneratedPreviewsProto {
+ repeated Preview previews = 1;
+
+ // represents a particular RemoteViews preview, which may be set for multiple categories
+ message Preview {
+ repeated int32 widget_categories = 1;
+ optional android.widget.RemoteViewsProto views = 2;
+ }
+} \ No newline at end of file
diff --git a/core/res/Android.bp b/core/res/Android.bp
index f6ca8218926c..0e4e22b09e24 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -158,6 +158,7 @@ android_app {
flags_packages: [
"android.app.appfunctions.flags-aconfig",
"android.app.contextualsearch.flags-aconfig",
+ "android.app.flags-aconfig",
"android.appwidget.flags-aconfig",
"android.content.pm.flags-aconfig",
"android.provider.flags-aconfig",
@@ -171,6 +172,9 @@ android_app {
"android.security.flags-aconfig",
"com.android.hardware.input.input-aconfig",
"aconfig_trade_in_mode_flags",
+ "art-aconfig-flags",
+ "ranging_aconfig_flags",
+ "aconfig_settingslib_flags",
],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0479318ad9ed..3e0c1200749e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2412,6 +2412,16 @@
android:label="@string/permlab_nearby_wifi_devices"
android:protectionLevel="dangerous" />
+ <!-- Required to be able to range to devices using generic ranging module.
+ @FlaggedApi("android.permission.flags.ranging_permission_enabled")
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.RANGING"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_ranging"
+ android:label="@string/permlab_ranging"
+ android:protectionLevel="dangerous"
+ android:featureFlag="android.permission.flags.ranging_permission_enabled"/>
+
<!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the
user from using them until they are unsuspended.
@hide
@@ -2622,13 +2632,22 @@
<!-- @SystemApi Allows access to perform vendor effects in the vibrator.
<p>Protection level: signature
- @FlaggedApi("android.os.vibrator.vendor_vibration_effects")
+ @FlaggedApi(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@hide
-->
<permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS"
android:protectionLevel="signature|privileged"
android:featureFlag="android.os.vibrator.vendor_vibration_effects" />
+ <!-- @SystemApi Allows access to start a vendor vibration session.
+ <p>Protection level: signature
+ @FlaggedApi(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @hide
+ -->
+ <permission android:name="android.permission.START_VIBRATION_SESSIONS"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.os.vibrator.vendor_vibration_effects" />
+
<!-- @SystemApi Allows access to the vibrator state.
<p>Protection level: signature
@hide
@@ -4444,6 +4463,18 @@
android:description="@string/permdesc_hideOverlayWindows"
android:protectionLevel="normal" />
+ <!-- Allows an app to enter Picture-in-Picture mode when the user is not explicitly requesting
+ it. This includes using {@link PictureInPictureParams.Builder#setAutoEnterEnabled} as well
+ as lifecycle methods such as {@link Activity#onUserLeaveHint} and {@link Activity#onPause}
+ to enter PiP when the user leaves the app.
+ This permission should only be used for certain PiP
+ <a href="{@docRoot}training/tv/get-started/multitasking#usage-types">usage types</a>.
+ @FlaggedApi(android.app.Flags.FLAG_ENABLE_TV_IMPLICIT_ENTER_PIP_RESTRICTION)
+ -->
+ <permission android:name="android.permission.TV_IMPLICIT_ENTER_PIP"
+ android:protectionLevel="normal"
+ android:featureFlag="android.app.enable_tv_implicit_enter_pip_restriction" />
+
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
<!-- ================================== -->
@@ -4950,6 +4981,27 @@
<permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS"
android:protectionLevel="signature|privileged|role" />
+ <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST)
+ Allows an application to access the Settings Preference services to read settings exposed
+ by the system Settings app and system apps that contribute settings surfaced by the
+ Settings app.
+ <p>This allows the calling application to read settings values through the host
+ application, agnostic of underlying storage. -->
+ <permission android:name="android.permission.READ_SYSTEM_PREFERENCES"
+ android:protectionLevel="signature|privileged|role"
+ android:featureFlag="com.android.settingslib.flags.settings_catalyst" />
+
+ <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST)
+ Allows an application to access the Settings Preference services to write settings
+ values exposed by the system Settings app and system apps that contribute settings surfaced
+ in the Settings app.
+ <p>This allows the calling application to write settings values
+ through the host application, agnostic of underlying storage.
+ <p>Protection Level: signature|privileged|appop - appop to be added in followup -->
+ <permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="com.android.settingslib.flags.settings_catalyst" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
@@ -8283,6 +8335,26 @@
android:protectionLevel="signature|knownSigner"
android:knownCerts="@array/config_healthConnectMigrationKnownSigners" />
+ <!-- @hide @SystemApi Allows permitted apps to back up Health Connect data and settings.
+ <p>Protection level: signature|knownSigner
+ @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled")
+ -->
+ <permission
+ android:name="android.permission.BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_backupHealthConnectDataAndSettingsKnownSigners"
+ android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" />
+
+ <!-- @hide @SystemApi Allows permitted apps to restore Health Connect data and settings.
+ <p>Protection level: signature|knownSigner
+ @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled")
+ -->
+ <permission
+ android:name="android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_restoreHealthConnectDataAndSettingsKnownSigners"
+ android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" />
+
<!-- @SystemApi Allows an app to query apps in clone profile. The permission is
bidirectional in nature, i.e. cloned apps would be able to query apps in root user.
The permission is not meant for 3P apps as of now.
@@ -8472,6 +8544,16 @@
android:protectionLevel="signature|privileged"
android:featureFlag="com.android.tradeinmode.flags.enable_trade_in_mode" />
+ <!-- @SystemApi
+ @FlaggedApi(com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS)
+ Ability to read program metadata and attach dynamic instrumentation.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.DYNAMIC_INSTRUMENTATION"
+ android:protectionLevel="signature"
+ android:featureFlag="com.android.art.flags.executable_method_file_offsets" />
+
<!--
@TestApi
Signature permission reserved for testing. This should never be used to
diff --git a/core/res/res/drawable-watch/ic_lock_bugreport.xml b/core/res/res/drawable-watch/ic_lock_bugreport.xml
index b664fe4f9c4e..35834be7b541 100644
--- a/core/res/res/drawable-watch/ic_lock_bugreport.xml
+++ b/core/res/res/drawable-watch/ic_lock_bugreport.xml
@@ -13,19 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="@android:color/white">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M20,10V8h-2.81c-0.45,-0.78 -1.07,-1.46 -1.82,-1.96L17,4.41L15.59,3l-2.17,2.17c-0.03,-0.01 -0.05,-0.01 -0.08,-0.01c-0.16,-0.04 -0.32,-0.06 -0.49,-0.09c-0.06,-0.01 -0.11,-0.02 -0.17,-0.03C12.46,5.02 12.23,5 12,5h0c-0.49,0 -0.97,0.07 -1.42,0.18l0.02,-0.01L8.41,3L7,4.41l1.62,1.63l0.01,0C7.88,6.54 7.26,7.22 6.81,8H4v2h2.09C6.03,10.33 6,10.66 6,11v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3h0c2.22,0 4.15,-1.21 5.19,-3H20v-2h-2.09l0,0c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1l0,0H20zM16,15c0,2.21 -1.79,4 -4,4c-2.21,0 -4,-1.79 -4,-4v-4c0,-2.21 1.79,-4 4,-4h0c2.21,0 4,1.79 4,4V15z"/>
- <path
- android:fillColor="@android:color/white"
- android:pathData="M10,14h4v2h-4z"/>
- <path
- android:fillColor="@android:color/white"
- android:pathData="M10,10h4v2h-4z"/>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal">
+<path android:fillColor="@android:color/white" android:pathData="M480.01,848.13Q412.37,848.13 354.6,814.34Q296.83,780.54 263.87,721.91L193.78,721.91Q175.97,721.91 163.92,709.86Q151.87,697.81 151.87,680Q151.87,662.19 163.92,650.14Q175.97,638.09 193.78,638.09L235.63,638.09Q232.81,619.12 232.34,600.16Q231.87,581.2 231.87,561.91L193.78,561.91Q175.97,561.91 163.92,549.86Q151.87,537.81 151.87,520Q151.87,502.19 163.92,490.14Q175.97,478.09 193.78,478.09L231.87,478.09Q231.87,458.8 232.34,439.84Q232.81,420.88 235.63,401.91L193.78,401.91Q175.97,401.91 163.92,389.86Q151.87,377.81 151.87,360Q151.87,342.19 163.92,330.14Q175.97,318.09 193.78,318.09L265.07,318.09Q278.11,294.13 295.97,273.77Q313.83,253.41 337.07,237.93L301.26,201.37Q289.54,189.65 289.66,172.32Q289.78,154.98 302.26,142.5Q313.98,130.78 331.7,130.78Q349.41,130.78 361.13,142.5L417.7,199.07Q447.13,188.63 477.92,188.39Q508.72,188.15 538.15,198.35L597.2,140.3Q608.79,128.59 626.19,128.59Q643.59,128.59 656.07,141.07Q667.78,152.78 667.78,170.5Q667.78,188.22 656.07,199.93L619.98,236.02Q644.41,251.98 663.51,273.03Q682.61,294.09 696.38,320L767.17,320Q784.41,320 796.27,331.86Q808.13,343.72 808.13,360.96Q808.13,378.2 796.27,390.05Q784.41,401.91 767.17,401.91L724.37,401.91Q727.19,420.88 727.66,439.84Q728.13,458.8 728.13,478.09L766.22,478.09Q784.03,478.09 796.08,490.14Q808.13,502.19 808.13,520Q808.13,537.81 796.08,549.86Q784.03,561.91 766.22,561.91L728.13,561.91Q728.13,581.2 727.51,600.12Q726.89,619.04 724.13,638.09L766.22,638.09Q784.03,638.09 796.08,650.14Q808.13,662.19 808.13,680Q808.13,697.81 796.08,709.86Q784.03,721.91 766.22,721.91L696.13,721.91Q663.17,780.54 605.42,814.34Q547.66,848.13 480.01,848.13ZM441.91,641.91L518.09,641.91Q535.9,641.91 547.95,629.86Q560,617.81 560,600Q560,582.19 547.95,570.14Q535.9,558.09 518.09,558.09L441.91,558.09Q424.1,558.09 412.05,570.14Q400,582.19 400,600Q400,617.81 412.05,629.86Q424.1,641.91 441.91,641.91ZM441.91,481.91L518.09,481.91Q535.9,481.91 547.95,469.86Q560,457.81 560,440Q560,422.19 547.95,410.14Q535.9,398.09 518.09,398.09L441.91,398.09Q424.1,398.09 412.05,410.14Q400,422.19 400,440Q400,457.81 412.05,469.86Q424.1,481.91 441.91,481.91Z"/>
</vector>
diff --git a/core/res/res/drawable-watch/ic_lock_power_off.xml b/core/res/res/drawable-watch/ic_lock_power_off.xml
index b437a4b70cca..c42d7d2e0293 100644
--- a/core/res/res/drawable-watch/ic_lock_power_off.xml
+++ b/core/res/res/drawable-watch/ic_lock_power_off.xml
@@ -14,13 +14,6 @@
~ limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="@android:color/white">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
-</vector>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal">
+<path android:fillColor="@android:color/white" android:pathData="M480,888.13Q395.09,888.13 320.65,856.03Q246.22,823.93 191.14,768.86Q136.07,713.78 103.97,639.35Q71.87,564.91 71.87,480Q71.87,406.52 96.01,340.78Q120.15,275.04 162.91,222.33Q175.11,206.65 192.52,207.41Q209.93,208.17 222.61,219.37Q235.28,230.57 239.26,248.84Q243.24,267.11 227.8,287.22Q197.48,327.26 180.17,376.09Q162.87,424.91 162.87,480Q162.87,613.04 254.91,705.09Q346.96,797.13 480,797.13Q613.04,797.13 705.09,705.09Q797.13,613.04 797.13,480Q797.13,424.91 779.83,376.09Q762.52,327.26 732.2,287.22Q716.76,267.11 720.74,248.84Q724.72,230.57 737.39,219.37Q750.07,208.17 767.48,207.41Q784.89,206.65 797.09,222.33Q839.85,275.04 863.99,340.78Q888.13,406.52 888.13,480Q888.13,564.91 856.03,639.35Q823.93,713.78 768.86,768.86Q713.78,823.93 639.35,856.03Q564.91,888.13 480,888.13ZM480,525.5Q460.85,525.5 447.67,512.33Q434.5,499.15 434.5,480L434.5,117.37Q434.5,98.22 447.67,85.04Q460.85,71.87 480,71.87Q499.15,71.87 512.33,85.04Q525.5,98.22 525.5,117.37L525.5,480Q525.5,499.15 512.33,512.33Q499.15,525.5 480,525.5Z"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/ic_restart.xml b/core/res/res/drawable-watch/ic_restart.xml
index 52933aae8fe0..ddcfd259d7b3 100644
--- a/core/res/res/drawable-watch/ic_restart.xml
+++ b/core/res/res/drawable-watch/ic_restart.xml
@@ -14,13 +14,6 @@
~ limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="@android:color/white">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02c-2.83,-0.48 -5,-2.94 -5,-5.91zM20,13c0,-4.42 -3.58,-8 -8,-8 -0.06,0 -0.12,0.01 -0.18,0.01l1.09,-1.09L11.5,2.5 8,6l3.5,3.5 1.41,-1.41 -1.08,-1.08c0.06,0 0.12,-0.01 0.17,-0.01 3.31,0 6,2.69 6,6 0,2.97 -2.17,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93z"/>
-</vector>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal">
+<path android:fillColor="@android:color/white" android:pathData="M385.83,834.46Q282.35,803.54 217.11,717.61Q151.87,631.67 151.87,520.48Q151.87,464.91 169.91,414.25Q187.96,363.59 220.8,320.83Q232.76,305.72 252.11,304.98Q271.46,304.24 286.85,319.63Q298.57,331.35 299.3,348.66Q300.04,365.98 288.8,381.41Q266.72,411.22 254.79,446.54Q242.87,481.87 242.87,520.48Q242.87,599.33 288.46,661.27Q334.04,723.22 406.41,746.7Q421.09,751.65 430.54,764.09Q440,776.52 440,790.96Q440,814.3 423.73,827.6Q407.46,840.89 385.83,834.46ZM574.17,834.46Q552.54,840.89 536.27,827.22Q520,813.54 520,790.2Q520,776.76 529.46,764.21Q538.91,751.65 553.59,746.7Q625.72,722.22 671.42,660.65Q717.13,599.09 717.13,520.48Q717.13,423.11 649.64,354.3Q582.15,285.5 485.02,283.59L481.07,283.59L497.3,299.83Q509.02,311.54 509.02,329.14Q509.02,346.74 497.3,358.46Q485.59,370.17 467.99,370.17Q450.39,370.17 438.67,358.46L348.46,268.24Q341.74,261.52 338.76,253.45Q335.78,245.37 335.78,236.41Q335.78,227.46 338.76,219.38Q341.74,211.3 348.46,204.59L438.67,114.13Q450.39,102.41 467.99,102.41Q485.59,102.41 497.3,114.13Q509.02,125.85 509.02,143.45Q509.02,161.04 497.3,172.76L477.72,192.35L481.91,192.35Q618.54,192.35 713.34,287.98Q808.13,383.61 808.13,520.48Q808.13,630.91 742.89,717.11Q677.65,803.3 574.17,834.46Z"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/ic_settings.xml b/core/res/res/drawable-watch/ic_settings.xml
new file mode 100644
index 000000000000..cef10e9eed71
--- /dev/null
+++ b/core/res/res/drawable-watch/ic_settings.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal">
+<path android:fillColor="@android:color/white" android:pathData="M428.46,888.13Q400.26,888.13 379.92,869.53Q359.59,850.93 355.59,823.74L346.59,757.5Q335.5,753.22 325.55,747.17Q315.61,741.13 306.04,734.33L244.28,760.33Q218.33,771.57 192.13,762.45Q165.93,753.33 151.46,729.13L99.91,638.76Q85.43,614.8 91.55,587.73Q97.67,560.65 119.63,543.17L172.39,503.17Q171.63,497.13 171.63,491.59Q171.63,486.04 171.63,480Q171.63,473.96 171.63,468.41Q171.63,462.87 172.39,456.83L119.63,417.07Q97.43,399.59 91.43,372.51Q85.43,345.43 99.91,321.24L151.46,231.11Q165.93,207.15 192.01,197.91Q218.09,188.67 244.04,199.91L306.52,225.91Q316.09,219.11 326.17,213.18Q336.26,207.26 346.59,202.98L355.59,136.5Q359.59,109.07 379.92,90.47Q400.26,71.87 428.46,71.87L531.54,71.87Q559.74,71.87 580.08,90.47Q600.41,109.07 604.41,136.5L613.41,202.98Q624.5,207.26 634.45,213.18Q644.39,219.11 653.96,225.91L715.72,199.91Q741.67,188.67 767.87,197.91Q794.07,207.15 808.54,231.11L860.09,321.24Q874.57,345.43 868.57,372.51Q862.57,399.59 840.37,417.07L787.37,456.83Q788.13,462.87 788.13,468.41Q788.13,473.96 788.13,480Q788.13,486.04 788.01,491.59Q787.89,497.13 786.37,503.17L839.37,542.93Q861.57,560.41 867.57,587.49Q873.57,614.57 859.09,638.76L806.54,729.13Q792.07,753.09 765.99,762.33Q739.91,771.57 713.96,760.33L653.48,734.33Q643.91,741.13 633.83,747.17Q623.74,753.22 613.41,757.5L604.41,823.74Q600.41,850.93 580.08,869.53Q559.74,888.13 531.54,888.13L428.46,888.13ZM481.28,620Q539.28,620 580.28,579Q621.28,538 621.28,480Q621.28,422 580.28,381Q539.28,340 481.28,340Q422.52,340 381.9,381Q341.28,422 341.28,480Q341.28,538 381.9,579Q422.52,620 481.28,620Z"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_unknown.xml b/core/res/res/drawable/ic_zen_mode_icon_star_badge.xml
index 04df5f91fd68..04df5f91fd68 100644
--- a/core/res/res/drawable/ic_zen_mode_type_unknown.xml
+++ b/core/res/res/drawable/ic_zen_mode_icon_star_badge.xml
diff --git a/core/res/res/layout/input_method_switch_item_new.xml b/core/res/res/layout/input_method_switch_item_new.xml
index f8710cc45358..7b241aff3fb1 100644
--- a/core/res/res/layout/input_method_switch_item_new.xml
+++ b/core/res/res/layout/input_method_switch_item_new.xml
@@ -18,18 +18,20 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item"
android:layout_width="match_parent"
- android:layout_height="72dp"
+ android:layout_height="wrap_content"
+ android:minHeight="72dp"
android:background="@drawable/input_method_switch_item_background"
android:gravity="center_vertical"
android:orientation="horizontal"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="8dp"
android:paddingStart="20dp"
- android:paddingEnd="24dp">
+ android:paddingEnd="24dp"
+ android:paddingVertical="8dp">
<LinearLayout
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start|center_vertical"
android:orientation="vertical">
@@ -39,11 +41,26 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
android:singleLine="true"
android:fontFamily="google-sans-text"
android:textColor="@color/input_method_switch_on_item"
android:textAppearance="?attr/textAppearanceListItem"/>
+ <TextView
+ android:id="@+id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:fontFamily="google-sans-text"
+ android:textColor="?attr/materialColorOnSurfaceVariant"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ android:textAllCaps="true"
+ android:visibility="gone"/>
+
</LinearLayout>
<ImageView
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index ccc584a0710b..9cfaca792f64 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die program toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"om interaksie met wi‑fi-toestelle in die omtrek te hê"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die program toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Voorkeur-NFC-betalingdiensinligting"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die program toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"beheer kortveldkommunikasie"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Naweek"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Geleentheid"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slaap"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Moenie Steur Nie (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Bestuur deur <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Af"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> tot <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Enige kalender"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Daar is \'n interne probleem met jou toestel en dit sal dalk onstabiel wees totdat jy \'n fabriekterugstelling doen."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Stuur en ontvang boodskappe sonder ’n selfoon- of wi-fi-netwerk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Maak Boodskappe oop"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe dit werk"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Skakel “Kies netwerk outomaties” aan"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Skakel “Kies netwerk outomaties” in Instellings aan sodat jou foon ’n netwerk kan vind wat met satelliet werk"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Skakel aan"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Gaan terug"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Stel Vingerafdrukslot weer op"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> kan nie meer herken word nie."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> kan nie meer herken word nie."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 22dc49ca7fb8..c30a5c853421 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"በአቅራቢያ ባሉ ልዕለ-ሰፊ ባንድ መሣሪያዎች መካከል ያለውን አንጻራዊ አቀማመጣቸውን ለማወቅ ንዲችል ለመተግበሪያው ይፍቀዱ"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"በአቅራቢያ ካሉ የWi‑Fi መሣሪያዎች ጋር መስተጋብር መፍጠር"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"መተግበሪያው በአቅራቢያ ያሉ የWi-Fi መሣሪያዎች አንጻራዊ ቦታን እንዲያሳውቅ፣ እንዲያገናኝ እና እንዲያውቅ ያስችለዋል"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ተመራጭ NFC የክፍያ አገልግሎት መረጃ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"እንደ የተመዘገቡ እርዳታዎች እና የጉዞ መሥመር መዳረሻ የመሳሰለ ተመራጭ nfc የክፍያ አገልግሎት መረጃን ለማግኘት ለመተግበሪያው ያፈቅድለታል።"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ቅርብ የግኑኙነትመስክ (NFC) ተቆጣጠር"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"የሳምንት እረፍት ቀናት"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ክስተት"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"መተኛት"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"አትረብሽ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> የሚተዳደር"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"በርቷል"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ጠፍቷል"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"፣ "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"ከ<xliff:g id="START">%1$s</xliff:g> እስከ <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ማንኛውም ቀን መቁጠሪያ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> አንዳንድ ድምጾችን እየዘጋ ነው"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ፣ የፋብሪካ ውሂብ ዳግም እስኪያስጀምሩት ድረስ ላይረጋጋ ይችላል።"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ያለ ሞባይል ወይም የWi-Fi አውታረ መረብ መልዕክቶችን ይላኩ እና ይቀበሉ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"መልዕክቶች ይክፈቱ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"እንዴት እንደሚሠራ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"«አውታረ መረብን በራስ-ሰር ምረጥ» የሚለውን ያብሩ"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ስልክዎ ከሳተላይት ጋር የሚሠራ አውታረ መረብ እንዲያገኝ በቅንብሮች ውስጥ «አውታረ መረብን በራስ-ሰር ምረጥ» የሚለውን ያብሩ"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"አብራ"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ወደኋላ ተመለስ"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"በጣት አሻራ መክፈቻን እንደገና ያዋቅሩ"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ከእንግዲህ መለየት አይችልም።"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> እና <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ከእንግዲህ መለየት አይችሉም።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1aaad07761e1..f2080cbbe3d9 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -616,6 +616,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"يسمح هذا الإذن للتطبيق بتحديد الموضع النسبي بين الأجهزة المجاورة التي تستخدم النطاق الواسع جدًا."</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏التفاعل مع أجهزة Wi‑Fi المجاورة"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏للسماح للتطبيق بعرض الإعلانات والاتصال بالأجهزة الأخرى وتحديد الموقع النسبي لأجهزة Wi-Fi المجاورة."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏يسمح هذا الإذن للتطبيق بالحصول على معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل، مثلاً المساعدات المسجّلة ووجهة المسار."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"التحكم في اتصال الحقل القريب"</string>
@@ -1942,13 +1946,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"نهاية الأسبوع"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"حدث"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"النوم"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"عدم الإزعاج (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"تحت إدارة \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"مفعَّل"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"غير مفعَّل"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"من <xliff:g id="START">%1$s</xliff:g> إلى <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"من <xliff:g id="START">%1$s</xliff:g> إلى <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"أي تقويم"</string>
<string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط على الإعدادات الأصلية."</string>
@@ -2430,15 +2434,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏يمكنك إرسال الرسائل واستلامها بدون شبكة الجوّال أو شبكة Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"فتح تطبيق \"الرسائل\""</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"طريقة العمل"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"تفعيل الإعداد \"اختيار الشبكة تلقائيًا\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"يمكنك تفعيل الإعداد \"اختيار الشبكة تلقائيًا\" من خلال \"الإعدادات\" لكي يتمكّن هاتفك من العثور على شبكة تعمل مع القمر الصناعي"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"تفعيل"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"رجوع"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"تتوفّر الآن ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"‏يمكنك مراسلة خدمات الطوارئ في حال عدم توفّر شبكة جوّال أو شبكة Wi-Fi. ولإجراء ذلك، يجب ضبط تطبيق \"رسائل Google\" ليصبح تطبيق المراسلة التلقائي."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ميزة \"اتصالات طوارئ بالقمر الصناعي\" غير متاحة"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ميزة \"اتصالات طوارئ بالقمر الصناعي\" غير متاحة على هذا الجهاز"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"لم يتم ضبط إعدادات ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"يُرجى التأكّد من أنّ جهازك متصل بالإنترنت ومحاولة ضبط الميزة مرة أخرى"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"ميزة \"اتصالات طوارئ بالقمر الصناعي\" غير متوفّرة"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"لا تتوفّر ميزة \"اتصالات طوارئ بالقمر الصناعي\" في هذا البلد أو هذه المنطقة"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"لم يتم ضبط إعدادات ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"‏للمراسلة عبر القمر الصناعي، عليك ضبط تطبيق \"رسائل Google\" ليصبح تطبيق المراسلة التلقائي"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"ميزة \"اتصالات طوارئ بالقمر الصناعي\" غير متوفّرة"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"لمعرفة ما إذا كانت ميزة \"اتصالات طوارئ بالقمر الصناعي\" متاحة في هذا البلد أو هذه المنطقة، فعِّل إعدادات الموقع الجغرافي"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ميزة \"المراسلة عبر القمر الاصطناعي\" متاحة"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"‏يمكنك إرسال رسائل عبر القمر الصناعي في حال عدم توفّر شبكة جوّال أو شبكة Wi-Fi. ولإجراء ذلك، يجب ضبط تطبيق \"رسائل Google\" ليصبح تطبيق المراسلة التلقائي."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ميزة \"المراسلة عبر القمر الاصطناعي\" غير متاحة"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ميزة \"المراسلة عبر القمر الاصطناعي\" غير متاحة على هذا الجهاز"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"لم يتم ضبط إعدادات ميزة \"المراسلة عبر القمر الاصطناعي\""</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"يُرجى التأكّد من أنّ جهازك متصل بالإنترنت ومحاولة ضبط الميزة مرة أخرى"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"ميزة \"المراسلة عبر القمر الاصطناعي\" غير متاحة"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"لا تتوفّر ميزة \"المراسلة عبر القمر الاصطناعي\" في هذا البلد أو هذه المنطقة"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"لم يتم ضبط إعدادات ميزة \"المراسلة عبر القمر الاصطناعي\""</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"‏للمراسلة عبر القمر الصناعي، عليك ضبط تطبيق \"رسائل Google\" ليصبح تطبيق المراسلة التلقائي"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"ميزة \"المراسلة عبر القمر الاصطناعي\" غير متاحة"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"لمعرفة ما إذا كانت ميزة \"المراسلة عبر القمر الاصطناعي\" متاحة في هذا البلد أو هذه المنطقة، فعِّل إعدادات الموقع الجغرافي"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"إعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\""</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"لا يمكن بعد الآن التعرّف على \"<xliff:g id="FINGERPRINT">%s</xliff:g>\"."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"لا يمكن بعد الآن التعرّف على \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" و\"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\"."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 74d8ed67543d..1f207d571c5a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"এপ্‌টোক নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচসমূহৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ক"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"নিকটৱৰ্তী ৱাই-ফাই ডিভাইচসমূহৰ সৈতে ভাব বিনিময় কৰক"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এপ্‌টোক বিজ্ঞাপন প্ৰচাৰাভিযান কৰিবলৈ, সংযোগ কৰিবলৈ আৰু নিকটৱৰ্তী ৱাই-ফাই ডিভাইচৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"অগ্ৰাধিকাৰ দিয়া NFC পৰিশোধ সেৱাৰ তথ্য"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"এপ্‌টোক অগ্ৰাধিকাৰ দিয়া nfc পৰিশোধ সেৱাৰ পঞ্জীকৃত সহায়কসমূহ আৰু পৰিশোধ কৰিব লগা লক্ষ্যস্থান দৰে তথ্য পাবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"সপ্তাহ অন্ত"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"অনুষ্ঠান"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"নিদ্ৰাৰত"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"অসুবিধা নিদিব (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ পৰিচালনা কৰা"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"অন আছে"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"অফ আছে"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>ৰ পৰা <xliff:g id="END">%2$s</xliff:g>লৈ"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"যিকোনো কেলেণ্ডাৰ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"আপোনাৰ ডিভাইচত এটা আভ্যন্তৰীণ সমস্যা আছে আৰু আপুনি ফেক্টৰী ডেটা ৰিছেট নকৰালৈকে ই সুস্থিৰভাৱে কাম নকৰিব পাৰে।"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"আপুনি কোনো ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নোহোৱাকৈ বাৰ্তা পঠিয়াওক আৰু লাভ কৰক"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খোলক"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ই কেনেকৈ কাম কৰে"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"স্বয়ংক্ৰিয়ভাৱে নেটৱৰ্ক বাছনি কৰক\" অন কৰক"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ছেটিঙত \"স্বয়ংক্ৰিয়ভাৱে নেটৱৰ্ক বাছনি কৰক\" অন কৰক, যাতে আপোনাৰ ফ’নটোৱে উপগ্ৰহৰ সৈতে কাম কৰা এটা নেটৱৰ্ক বিচাৰি পাব পাৰে"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"অন কৰক"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"উভতি যাওক"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"উপগ্ৰহ SOS সুবিধাটো এতিয়া উপলব্ধ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"যদি ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নাথাকিলেও আপুনি জৰুৰীকালীন সেৱাসমূহলৈ বার্তা পঠিয়াব পাৰে। Google Messages আপোনাৰ ডিফ’ল্ট বাৰ্তা আদান-প্ৰদান কৰা এপ্‌ হ’বই লাগিব।"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"উপগ্ৰহ SOS সুবিধা সমৰ্থিত নহয়"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"এই ডিভাইচটোত উপগ্ৰহ SOS সুবিধা সমৰ্থিত নহয়"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"উপগ্ৰহ SOS সুবিধা ছেট আপ কৰা হোৱা নাই"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"আপুনি ইণ্টাৰনেটৰ সৈতে সংযুক্ত হৈ থকাটো নিশ্চিত কৰক আৰু পুনৰ ছেটআপ কৰিবলৈ চেষ্টা কৰক"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"উপগ্ৰহ SOS সুবিধা উপলব্ধ নহয়"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"এই দেশ বা অঞ্চলত উপগ্ৰহ SOS সুবিধা উপলব্ধ নহয়"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"উপগ্ৰহ SOS সুবিধা ছেট আপ কৰা হোৱা নাই"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"উপগ্ৰহৰ জৰিয়তে বাৰ্তা পঠিয়াবলৈ, Google Messagesক আপোনাৰ ডিফ’ল্ট বাৰ্তা আদান-প্ৰদান কৰা এপ্‌ হিচাপে ছেট কৰক"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"উপগ্ৰহ SOS সুবিধা উপলব্ধ নহয়"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"এই দেশ বা অঞ্চলত উপগ্ৰহ SOS সুবিধা উপলব্ধ হয় নে নহয় পৰীক্ষা কৰিবলৈ, অৱস্থানৰ ছেটিং অন কৰক"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা উপলব্ধ"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নাথাকিলেও আপুনি উপগ্ৰহৰ জৰিয়তে বার্তা পঠিয়াব পাৰে। Google Messages আপোনাৰ ডিফ’ল্ট বাৰ্তা আদান-প্ৰদান কৰা এপ্‌ হ’বই লাগিব।"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা সমৰ্থিত নহয়"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"এই ডিভাইচটোত উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা সমৰ্থিত নহয়"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা ছেট আপ কৰা হোৱা নাই"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"আপুনি ইণ্টাৰনেটৰ সৈতে সংযুক্ত হৈ থকাটো নিশ্চিত কৰক আৰু পুনৰ ছেটআপ কৰিবলৈ চেষ্টা কৰক"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা উপলব্ধ নহয়"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"এই দেশ বা অঞ্চলত উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা উপলব্ধ নহয়"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা ছেট আপ কৰা হোৱা নাই"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"উপগ্ৰহৰ জৰিয়তে বাৰ্তা পঠিয়াবলৈ, Google Messagesক আপোনাৰ ডিফ’ল্ট বাৰ্তা আদান-প্ৰদান কৰা এপ্‌ হিচাপে ছেট কৰক"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা উপলব্ধ নহয়"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"এই দেশ বা অঞ্চলত উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা উপলব্ধ হয় নে নহয় পৰীক্ষা কৰিবলৈ, অৱস্থানৰ ছেটিং অন কৰক"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ফিংগাৰপ্ৰিণ্ট আনলক পুনৰ ছেট আপ কৰক"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> আৰু চিনাক্ত কৰিব নোৱাৰি।"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> আৰু <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> আৰু চিনাক্ত কৰিব নোৱাৰি।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 22e86dfc089f..07216f55bdcd 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tətbiqə yaxınlıqdakı Ultra Genişzolaqlı cihazları arasında nisbi mövqeyi təyin etməyə icazə verin"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yaxınlıqdakı Wi-Fi cihazları ilə əlaqə qurmaq"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Tətbiqə yaxınlıqdakı Wi-Fi cihazlarında reklam etmək, onlara qoşulmaq və nisbi mövqeyini təyin etmək icazəsi verir"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tərcih edilən NFC ödəniş xidməti məlumatı"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tətbiqə qeydiyyatdan keçmiş yardım və marşrut təyinatı kimi tərcih edilən nfc ödəniş xidməti məlumatını əldə etmək icazəsi verir."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication\'ı kontrol et"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Həftə sonu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tədbir"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Yuxu vaxtı"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Narahat Etməyin (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> idarə edir"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktiv"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Deaktiv"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"İstənilən təqvim"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bəzi səsləri səssiz rejimə salır"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızın daxili problemi var və istehsalçı sıfırlanması olmayana qədər qeyri-stabil ola bilər."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil və ya Wi-Fi şəbəkəsi olmadan mesajlar göndərin və qəbul edin"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajı açın"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Haqqında"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"Avtomatik şəbəkə seçin\" funksiyasını aktiv edin"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Telefonunuzun peyklə işləyən şəbəkə tapa bilməsi üçün Ayarlarda \"Avtomatik şəbəkə seçin\" funksiyasını aktiv edin"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktiv edin"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Geri qayıdın"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmaqla Kilidaçmanı yenidən ayarlayın"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> artıq tanınmır."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> və <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> artıq tanınmır."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index b9f909f74ca2..ac6a47fd8f84 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvoljava aplikaciji da određuje relativnu razdaljinu između uređaja ultra-širokog pojasa u blizini"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija sa WiFi uređajima u blizini"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i utvrđuje relativnu poziciju WiFi uređaja u blizini"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o željenoj NFC usluzi za plaćanje"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da preuzima informacije o željenoj NFC usluzi za plaćanje, poput registrovanih identifikatora aplikacija i odredišta preusmeravanja."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrola komunikacije u užem polju (Near Field Communication)"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne uznemiravaj (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Došlo je do internog problema u vezi sa uređajem i možda će biti nestabilan dok ne obavite resetovanje na fabrička podešavanja."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke bez mobilne ili WiFi mreže"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Princip rada"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Uključite opciju Automatski izaberi mrežu"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Uključite opciju Automatski izaberi mrežu u Podešavanjima da bi telefon mogao da pronađe mrežu koja radi sa satelitom"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Uključi"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazad"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Hitna pomoć preko satelita je sada dostupna"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Možete da šaljete poruke hitnim službama ako nemate pristup mobilnoj ni WiFi mreži. Google Messages mora da bude podrazumevana aplikacija za razmenu poruka."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Hitna pomoć preko satelita nije podržana"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Hitna pomoć preko satelita nije podržana na ovom uređaju"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Hitna pomoć preko satelita nije podešena"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Proverite da li ste povezani na internet i probajte ponovo da podesite"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Hitna pomoć preko satelita nije dostupna"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Hitna pomoć preko satelita nije dostupna u ovoj zemlji ili regionu"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Hitna pomoć preko satelita nije podešena"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Da biste slali poruke preko satelita, podesite Google Messages kao podrazumevanu aplikaciju za razmenu poruka"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Hitna pomoć preko satelita nije dostupna"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Da biste proverili da li je hitna pomoć preko satelita dostupna u ovoj zemlji ili regionu, uključite podešavanja lokacije"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Razmena poruka preko satelita je dostupna"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Možete da šaljete poruke preko satelita ako nemate pristup mobilnoj ni WiFi mreži. Google Messages mora da bude podrazumevana aplikacija za razmenu poruka."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Razmena poruka preko satelita nije podržana"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Razmena poruka preko satelita nije podržana na ovom uređaju"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Razmena poruka preko satelita nije podešena"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Proverite da li ste povezani na internet i probajte ponovo da podesite"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Razmena poruka preko satelita nije dostupna"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Razmena poruka preko satelita nije dostupna u ovoj zemlji ili regionu"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Razmena poruka preko satelita nije podešena"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Da biste slali poruke preko satelita, podesite Google Messages kao podrazumevanu aplikaciju za razmenu poruka"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Razmena poruka preko satelita nije dostupna"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Da biste proverili da li je razmena poruka preko satelita dostupna u ovoj zemlji ili regionu, uključite podešavanja lokacije"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo podesite otključavanje otiskom prsta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> više ne može da se prepozna."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> više ne mogu da se prepoznaju."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index b2520944e3ff..f492bfa4cc7b 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дазволіць праграме вызначаць адлегласць паміж прыладамі паблізу, якія выкарыстоўваюць звышшырокапалосную сувязь"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"узаемадзейнічаць з прыладамі з Wi‑Fi паблізу"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Праграма зможа адпраўляць даныя на прылады Wi-Fi паблізу, падключацца да іх і вызначаць іх месцазнаходжанне"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Інфармацыя пра прыярытэтны сэрвіс аплаты NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дазваляе праграме атрымаць доступ да інфармацыі пра прыярытэтны сэрвіс аплаты NFC, напрыклад зарэгістраваныя ідэнтыфікатары праграм і маршруты адпраўкі даных."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"кантроль Near Field Communication"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Выхадныя"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Падзея"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Рэжым сну"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Не турбаваць (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Пад кіраваннем праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Уключана"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Выключана"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любы каляндар"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> выключае некаторыя гукі"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"На вашай прыладзе ўзнікла ўнутраная праблема, і яна можа працаваць нестабільна, пакуль вы не зробіце скід да заводскіх налад."</string>
@@ -2428,15 +2432,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Вы можаце адпраўляць і атрымліваць паведамленні, калі падключэнне да мабільнай сеткі або сеткі Wi-Fi адсутнічае"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Адкрыць Паведамленні"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як гэта працуе"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Уключыце параметр \"Выбіраць сетку аўтаматычна\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Уключыце ў наладах параметр \"Выбіраць сетку аўтаматычна\", каб ваш тэлефон мог знаходзіць сетку, якая працуе са спадарожнікам"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Уключыць"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Экстраннае спадарожнікавае падключэнне зараз даступнае"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"У вас ёсць магчымасць адпраўляць паведамленні ў экстранныя службы, калі адсутнічае падключэнне да мабільнай сеткі ці сеткі Wi-Fi. Упэўніцеся, што ў якасці стандартнай выбрана праграма \"Google Паведамленні\"."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Экстраннае спадарожнікавае падключэнне не падтрымліваецца"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Экстраннае спадарожнікавае падключэнне не падтрымліваецца на гэтай прыладзе"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Экстраннае спадарожнікавае падключэнне не наладжана"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Праверце падключэнне да інтэрнэту і паўтарыце наладжванне"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Экстраннае спадарожнікавае падключэнне недаступнае"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Функцыя экстраннага спадарожнікавага падключэння недаступная ў гэтай краіне або рэгіёне"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Экстраннае спадарожнікавае падключэнне не наладжана"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Каб выкарыстоўваць абмен паведамленнямі па спадарожнікавай сувязі, праграма \"Google Паведамленні\" павінна быць выбрана як стандартная праграма абмену паведамленнямі"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Экстраннае спадарожнікавае падключэнне недаступнае"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Уключыце налады месцазнаходжання, каб праверыць, ці даступнае экстраннае спадарожнікавае падключэнне ў гэтай краіне ці рэгіёне"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Абмен паведамленнямі па спадарожнікавай сувязі даступны"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Вы можаце абменьвацца паведамленнямі па спадарожнікавай сувязі, калі падключэнне да мабільнай сеткі ці сеткі Wi-Fi адсутнічае. Упэўніцеся, што ў якасці стандартнай выбрана праграма \"Google Паведамленні\"."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Абмен паведамленнямі па спадарожнікавай сувязі не падтрымліваецца"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Абмен паведамленнямі па спадарожнікавай сувязі не падтрымліваецца на гэтай прыладзе"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Абмен паведамленнямі па спадарожнікавай сувязі не наладжаны"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Праверце падключэнне да інтэрнэту і паўтарыце наладжванне"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Абмен паведамленнямі па спадарожнікавай сувязі недаступны"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Абмен паведамленнямі па спадарожнікавай сувязі недаступны ў гэтай краіне або рэгіёне"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Абмен паведамленнямі па спадарожнікавай сувязі не наладжаны"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Каб выкарыстоўваць абмен паведамленнямі па спадарожнікавай сувязі, праграма \"Google Паведамленні\" павінна быць выбрана як стандартная праграма абмену паведамленнямі"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Абмен паведамленнямі па спадарожнікавай сувязі недаступны"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Каб праверыць, ці даступны абмен паведамленнямі па спадарожнікавай сувязі ў гэтай краіне ці рэгіёне, уключыце налады месцазнаходжання"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Наладзіць разблакіроўку адбіткам пальца паўторна"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Адбітак пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" больш не можа быць распазнаны."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Адбіткі пальцаў \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" больш не могуць быць распазнаны."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e5a08bb8c1fb..c8b171af5bc9 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Разрешаване на приложението да определя относителната позиция между устройствата с ултрашироколентови сигнали в близост"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"взаимодействие с устройствата с Wi-Fi в близост"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Разрешава на приложението да рекламира, да се свързва и да определя относителната позиция на устройствата с Wi-Fi в близост"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информация за предпочитаната услуга за плащане чрез NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дава възможност на приложението да получава информация за предпочитаната услуга за плащане чрез NFC, като например регистрирани помощни средства и местоназначение."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"контролиране на комуникацията в близкото поле"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Събота и неделя"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Събитие"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Време за сън"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Не безпокойте (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Управлява се от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Вкл."</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Изкл."</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"От <xliff:g id="START">%1$s</xliff:g> до <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Всички календари"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> заглушава някои звуци"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Възникна вътрешен проблем с устройството ви. То може да е нестабилно, докато не възстановите фабричните настройки."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Изпращайте и получавайте съобщения без мобилна или Wi-Fi мрежа"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отваряне на Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Начин на работа"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Включване на „Автоматично избиране на мрежа“"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Включете „Автоматично избиране на мрежа“ в „Настройки“, за да може телефонът ви да намери мрежа, която работи със сателит"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Включване"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Повторно настройване на „Отключване с отпечатък“"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> вече не може да се разпознае."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> вече не могат да бъдат разпознати."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index df8a90841215..0e103cc424b0 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"অ্যাপকে আশেপাশের Ultra-Wideband ডিভাইসগুলির আপেক্ষিক অবস্থান নির্ণয় করার অনুমতি দিন"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"আশপাশের ওয়াই-ফাই ডিভাইসের সাথে ইন্টার‍্যাক্ট করুন"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এটির ফলে অ্যাপ আশপাশের ওয়াই-ফাই ডিভাইসের তথ্য দেখতে, তাদের সাথে কানেক্ট করতে এবং তা কত দূরত্বে আছে সেটি জানতে পারবে"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"পছন্দের NFC পেমেন্ট পরিষেবার তথ্য"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"অ্যাপের মাধ্যমে পছন্দসই এনএফসি পেমেন্ট পরিষেবার তথ্য, যেমন রেজিস্ট্রার করার সহায়তা এবং রুট ডেস্টিনেশন সম্পর্কিত তথ্য অ্যাক্সেস করার অনুমতি দেয়।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"নিয়ার ফিল্ড কমিউনিকেশন নিয়ন্ত্রণ করে"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"সপ্তাহান্ত"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ইভেন্ট"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ঘুমানোর সময়"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"বিরক্ত করবে না (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ম্যানেজ করে"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"চালু আছে"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"বন্ধ আছে"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> থেকে <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"যেকোনও ক্যালেন্ডার"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> কিছু সাউন্ডকে মিউট করে দিচ্ছে"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"কোনও মেবাইল বা ওয়াই-ফাই নেটওয়ার্ক ছাড়াই মেসেজ পাঠান ও রিসিভ করুন"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খুলুন"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"এটি কীভাবে কাজ করে"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"নেটওয়ার্ক অটোমেটিক বেছে নিন\" বিকল্প চালু করুন"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"সেটিংসে \"নেটওয়ার্ক অটোমেটিক বেছে নিন\" বিকল্পটি চালু করলে আপনার ফোন এমন নেটওয়ার্ক বেছে নিতে পারবে যা স্যাটেলাইটের মাধ্যমে কাজ করে"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"চালু করুন"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ফিরে যান"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"স্যাটেলাইট SOS এখন উপলভ্য"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"কোনও মোবাইল বা ওয়াই-ফাই নেটওয়ার্ক না থাকলেও আপনি জরুরি পরিষেবাতে মেসেজ করতে পারবেন। Google Messages অবশ্যই আপনার ডিফল্ট মেসেজিং অ্যাপ হতে হবে।"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"স্যাটেলাইট SOS কাজ করে না"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"এই ডিভাইসে স্যাটেলাইট SOS কাজ করে না"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"স্যাটেলাইট SOS সেট আপ করা হয়নি"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"আপনার ডিভাইসে ইন্টারনেট কানেকশন আছে কিনা দেখে নিন এবং আবার সেটআপ করার চেষ্টা করুন"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"স্যাটেলাইট SOS উপলভ্য নেই"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"এই দেশ অথবা অঞ্চলে স্যাটেলাইট SOS উপলভ্য নেই"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"স্যাটেলাইট SOS সেট আপ করা নেই"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"স্যাটেলাইটের সাহায্যে মেসেজ পাঠাতে, আপনার ডিফল্ট মেসেজিং অ্যাপ হিসেবে Google Messages সেট করুন"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"স্যাটেলাইট SOS উপলভ্য নেই"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"এই দেশে বা অঞ্চলে স্যাটেলাইট SOS উপলভ্য আছে কিনা দেখতে, লোকেশন সেটিংস চালু করুন"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"\'স্যাটেলাইট মেসেজিং\' ফিচার উপলভ্য"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"কোনও মোবাইল বা ওয়াই-ফাই নেটওয়ার্ক না থাকলে, স্যাটেলাইটের মাধ্যমে মেসেজ করতে পারবেন। Google Messages অবশ্যই আপনার ডিফল্ট মেসেজিং অ্যাপ হতে হবে।"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"\'স্যাটেলাইট মেসেজিং\' ফিচার কাজ করে না"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"এই ডিভাইসে \'স্যাটেলাইট মেসেজিং\' ফিচার কাজ করে না"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"\'স্যাটেলাইট মেসেজিং\' ফিচার সেট আপ করা নেই"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"আপনার ডিভাইসে ইন্টারনেট কানেকশন আছে কিনা দেখে নিন এবং আবার সেটআপ করার চেষ্টা করুন"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"\'স্যাটেলাইট মেসেজিং\' ফিচার উপলভ্য নেই"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"এই দেশে বা অঞ্চলে \'স্যাটেলাইট মেসেজিং\' ফিচার উপলভ্য নেই"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"\'স্যাটেলাইট মেসেজিং\' ফিচার সেট আপ করা নেই"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"স্যাটেলাইটের সাহায্যে মেসেজ পাঠাতে, আপনার ডিফল্ট মেসেজিং অ্যাপ হিসেবে Google Messages সেট করুন"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"\'স্যাটেলাইট মেসেজিং\' ফিচার উপলভ্য নেই"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"\'স্যাটেলাইট মেসেজিং\' ফিচার এই দেশে বা অঞ্চলে উপলভ্য আছে কিনা দেখতে, লোকেশন সেটিংস চালু করুন"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"\'ফিঙ্গারপ্রিন্ট আনলক\' আবার সেট-আপ করুন"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> আর শনাক্ত করা যাবে না।"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ও <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> আর শনাক্ত করা যাবে না।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 35e30ba16e4c..202afdeb9438 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -76,7 +76,7 @@
<string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavke ID-a pozivaoca."</string>
<string name="auto_data_switch_title" msgid="3286350716870518297">"Prijenos podataka usmjeravanjem na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
<string name="auto_data_switch_content" msgid="803557715007110959">"Ove postavke možete uvijek promijeniti u Postavkama"</string>
- <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge prijenosa podataka na mobilnoj mreži"</string>
+ <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Nema usluge prenosa podataka na mobilnoj mreži"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Hitni pozivi su nedostupni"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Nema usluge govornih poziva"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Nema glasovne usluge ili hitnih poziva"</string>
@@ -89,7 +89,7 @@
<string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozorenja"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"Prosljeđivanje poziva"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"Način rada za hitni povratni poziv"</string>
- <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Status prijenosa podataka na mobilnoj mreži"</string>
+ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Status prenosa podataka na mobilnoj mreži"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS poruke"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Poruke govorne pošte"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"Pozivanje putem WiFi-ja"</string>
@@ -310,7 +310,7 @@
<string name="notification_channel_display" msgid="6905032605735615090">"Ekran"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> troši bateriju"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Broj aplikacija koje troše bateriju: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o potrošnji baterije i prijenosa podataka"</string>
+ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o potrošnji baterije i prenosa podataka"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Siguran način rada"</string>
<string name="android_system_label" msgid="5974767339591067210">"Sistem Android"</string>
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvolite aplikaciji da odredi relativni položaj između uređaja ultra širokog opsega u blizini"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"stupanje u interakciju s WiFi uređajima u blizini"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i određuje relativni položaj WiFi uređaja u blizini"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o preferiranoj usluzi plaćanja putem NFC-a"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da dobije informacije o preferiranoj usluzi plaćanja putem NFC-a kao što su registrirana pomagala i odredište rute."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"upravljanje NFC-om"</string>
@@ -812,7 +816,7 @@
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"pristupi DRM certifikatima"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Dozvoljava aplikaciji da obezbijedi i koristi DRM certifikate. Nije potrebno za obične aplikacije."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"prijem statusa prebacivanja preko Android prebacivanja"</string>
- <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Dozvoljava aplikaciji prijem informacija o trenutnim prijenosima putem funkcije Android Beam"</string>
+ <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Dozvoljava aplikaciji prijem informacija o trenutnim prenosima putem funkcije Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"ukloni DRM certifikate"</string>
<string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Dozvoljava aplikaciji da ukloni DRM certifikate. Nije potrebno za obične aplikacije."</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"poveži sa servisom za poruke operatera"</string>
@@ -1163,7 +1167,7 @@
<string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dan}one{# dan}few{# dana}other{# dana}}"</string>
<string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# godina}one{# godina}few{# godine}other{# godina}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problem sa prikazom video sadržaja"</string>
- <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Prijenos ovog video sadržaja ne može se izvršiti na ovom uređaju."</string>
+ <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Prenos ovog video sadržaja ne može se izvršiti na ovom uređaju."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Greška prilikom reproduciranja video sadržaja."</string>
<string name="VideoView_error_button" msgid="5138809446603764272">"Uredu"</string>
<string name="relative_time" msgid="8572030016028033243">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -1478,7 +1482,7 @@
<string name="ext_media_missing_message" msgid="4408988706227922909">"Ponovo umetnite uređaj"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Premješta se <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Premještanje podataka"</string>
- <string name="ext_media_move_success_title" msgid="4901763082647316767">"Prijenos sadržaja je završen"</string>
+ <string name="ext_media_move_success_title" msgid="4901763082647316767">"Prenos sadržaja je završen"</string>
<string name="ext_media_move_success_message" msgid="9159542002276982979">"Sadržaj je premješten na uređaj <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_failure_title" msgid="3184577479181333665">"Nije moguće premjestiti sadržaj"</string>
<string name="ext_media_move_failure_message" msgid="4197306718121869335">"Pokušajte ponovo premjestiti sadržaj"</string>
@@ -1620,9 +1624,9 @@
<string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disk"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB pohrana"</string>
<string name="extract_edit_menu_button" msgid="63954536535863040">"Uredi"</string>
- <string name="data_usage_warning_title" msgid="9034893717078325845">"Upozorenje o prijenosu podataka"</string>
+ <string name="data_usage_warning_title" msgid="9034893717078325845">"Upozorenje o prenosu podataka"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"Potrošili ste <xliff:g id="APP">%s</xliff:g> podataka"</string>
- <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Dostignuto ograničenje za prijenos podataka"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Dostignuto ograničenje za prenos podataka"</string>
<string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Dostignut limit WiFi podataka"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"Prijenos podataka je pauziran do kraja ciklusa"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Pređen limit mobilnih podataka"</string>
@@ -1915,7 +1919,7 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prijenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupati podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupati podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Uštedu podataka?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Traje jednu minutu (do {formattedTime})}one{Traje # min (do {formattedTime})}few{Traje # min (do {formattedTime})}other{Traje # min (do {formattedTime})}}"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne ometaj (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string>
@@ -2028,9 +2032,9 @@
<string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Trenutno ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na uređaju Android TV."</string>
<string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Trenutno ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na tabletu."</string>
<string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Trenutno ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na telefonu."</string>
- <string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prijenosa. Prvo dajte odobrenje na Android TV uređaju."</string>
- <string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prijenosa. Prvo dajte odobrenje na tabletu."</string>
- <string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prijenosa. Prvo dodajte odobrenje na telefonu."</string>
+ <string name="app_streaming_blocked_message_for_permission_request" product="tv" msgid="4706276040125072077">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prenosa. Prvo dajte odobrenje na Android TV uređaju."</string>
+ <string name="app_streaming_blocked_message_for_permission_request" product="tablet" msgid="1824604581465771629">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prenosa. Prvo dajte odobrenje na tabletu."</string>
+ <string name="app_streaming_blocked_message_for_permission_request" product="default" msgid="7755223160363292105">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prenosa. Prvo dodajte odobrenje na telefonu."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Ova aplikacija zahtijeva dodatnu sigurnost. Umjesto toga pokušajte na uređaju Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Ova aplikacija zahtijeva dodatnu sigurnost. Umjesto toga pokušajte na tabletu."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Ova aplikacija zahtijeva dodatnu sigurnost. Umjesto toga pokušajte na telefonu."</string>
@@ -2373,8 +2377,8 @@
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Provjerite aktivne aplikacije"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nije moguće pristupiti kameri telefona s uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nije moguće pristupiti kameri tableta s uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
- <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete pristupiti tokom prijenosa. Umjesto toga pokušajte na telefonu."</string>
- <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tokom prijenosa nije moguće gledati sliku u slici"</string>
+ <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete pristupiti tokom prenosa. Umjesto toga pokušajte na telefonu."</string>
+ <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tokom prenosa nije moguće gledati sliku u slici"</string>
<string name="system_locale_title" msgid="711882686834677268">"Sistemski zadano"</string>
<string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string>
<string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Odobrenje za profil pratećeg sata da upravlja satovima"</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke bez mobilne ili WiFi mreže"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvorite Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako ovo funkcionira"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Uključite \"Automatski odaberi mrežu\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Uključite \"Automatski odaberi mrežu\" u Postavkama da telefon može pronaći mrežu koja funkcionira sa satelitom"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Uključi"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazad"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Hitna pomoć putem satelita je sada dostupna"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Možete razmjenjivati poruke s hitnim službama ako nemate mobilnu ili WiFi mrežu. Google Messages mora biti zadana aplikacija za razmjenu poruka."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Hitna pomoć putem satelita nije podržana"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Hitna pomoć putem satelita nije podržana na uređaju"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Hitna pomoć putem satelita nije postavljena"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Provjerite jeste li povezani s internetom i ponovo pokušajte postaviti uslugu"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Hitna pomoć putem satelita nije dostupna"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Hitna pomoć putem satelita trenutno nije dostupna u ovoj zemlji ili regiji"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Hitna pomoć putem satelita nije postavljena"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Da razmjenjujete poruke putem satelita, postavite Google Messages kao zadanu aplikaciju za razmjenu poruka"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Hitna pomoć putem satelita nije dostupna"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Da provjerite je li hitna pomoć putem satelita dostupna u vašoj zemlji ili regiji, uključite postavke lokacije"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satelitska razmjena poruka je dostupna"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Možete razmjenjivati poruke putem satelita ako nemate mobilnu ili WiFi mrežu. Google Messages mora biti zadana aplikacija za razmjenu poruka."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satelitska razmjena poruka nije podržana"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satelitska razmjena poruka nije podržana na uređaju"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satelitska razmjena poruka nije postavljena"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Provjerite jeste li povezani s internetom i ponovo pokušajte postaviti uslugu"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satelitska razmjena poruka nije dostupna"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satelitska razmjena poruka nije dostupna u ovoj zemlji ili regiji"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satelitska razmjena poruka nije postavljena"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Da razmjenjujete poruke putem satelita, postavite Google Messages kao zadanu aplikaciju za razmjenu poruka"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satelitska razmjena poruka nije dostupna"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Da provjerite je li satelitska razmjena poruka dostupna u vašoj zemlji ili regiji, uključite postavke lokacije"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo postavite otključavanje otiskom prsta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> se više ne može prepoznati."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> se više ne mogu prepoznati."</string>
@@ -2445,7 +2469,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Vaš model lica se više ne može prepoznati. Ponovo postavite otključavanje licem."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Postavite"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne sada"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm za: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm za korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Prebaci na drugog korisnika"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Isključi zvuk"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Dodirnite da isključite zvuk"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 7c817b9c3965..8b28cd7891c8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permet que l\'aplicació determini la posició relativa entre els dispositius de banda ultraampla propers"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interaccionar amb els dispositius Wi‑Fi propers"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet que l\'aplicació s\'anunciï i es connecti als dispositius Wi‑Fi propers, i en determini la posició relativa"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informació preferent sobre el servei de pagament per NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet que l\'aplicació obtingui informació preferent sobre el servei de pagament per NFC, com ara complements registrats i destinacions de rutes."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicació de camp proper (NFC)"</string>
@@ -1929,7 +1933,7 @@
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Finalitza: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Finalitza: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Finalitza: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (propera alarma)"</string>
- <string name="zen_mode_forever" msgid="740585666364912448">"Fins que no el desactivis"</string>
+ <string name="zen_mode_forever" msgid="740585666364912448">"Fins que no ho desactivis"</string>
<string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Fins que desactivis el mode No molestis"</string>
<string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="8009920446193610996">"Replega"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cap de setmana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esdeveniment"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Mentre dormo"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"No molestis (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionat per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activat"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivat"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualsevol calendari"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> està silenciant alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"S\'ha produït un error intern al dispositiu i és possible que funcioni de manera inestable fins que restableixis les dades de fàbrica."</string>
@@ -2427,15 +2431,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envia i rep missatges sense una xarxa mòbil o Wi‑Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Obre Missatges"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Com funciona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activa l\'opció Selecciona la xarxa automàticament"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activa l\'opció Selecciona la xarxa automàticament a Configuració perquè el telèfon pugui trobar una xarxa que funcioni per satèl·lit"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activa"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Torna"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Torna a configurar Desbloqueig amb empremta digital"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ja no es pot reconèixer."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ja no es poden reconèixer."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a9766bf1fdeb..f3a020bbfce4 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikace bude moci zjišťovat vzájemnou pozici mezi ultra-širokopásmovými zařízeními v okolí"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakce se zařízeními Wi-Fi v okolí"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Umožňuje aplikaci inzerovat, připojovat se a odhadovat relativní polohu zařízení Wi-Fi v okolí"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informace o preferované platební službě NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Umožňuje aplikaci získat informace o preferované platební službě NFC, například o registrovaných pomůckách a cíli směrování."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ovládání technologie NFC"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Událost"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánek"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Nerušit (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Spravováno aplikací <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Zapnuto"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuto"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"V libovolném kalendáři"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"V zařízení došlo k internímu problému. Dokud neprovedete obnovení továrních dat, může být nestabilní."</string>
@@ -2428,15 +2432,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Odesílejte a přijímejte zprávy bez mobilní sítě nebo Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otevřít Zprávy"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to funguje"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Zapněte Vybírat síť automaticky"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Zapněte v Nastavení možnost Vybírat síť automaticky, aby telefon našel síť, která používá satelit"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Zapnout"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Zpět"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Opětovné nastavení odemknutí otiskem prstu"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> se nedaří rozpoznat."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> se nedaří rozpoznat."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 58e9e6f3efd7..b481682b0448 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -563,7 +563,7 @@
<string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"Adgang til skjulte profiler"</string>
<string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"Giver appen adgang til skjulte profiler."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"ændre størrelsen på din baggrund"</string>
- <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Tillader, at appen giver tips til systembaggrundens størrelse."</string>
+ <string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Tillader, at appen giver tip til systembaggrundens størrelse."</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"angive tidszone"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"Tillader, at appen kan ændre tidszonen på din tablet."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Tillader, at appen kan ændre tidszonen på din Android TV-enhed."</string>
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillad, at appen fastlægger den relative position mellem UWB-enheder (Ultra-Wideband) i nærheden"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagere med Wi‑Fi-enheder i nærheden"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Giver appen tilladelse til at informere om, oprette forbindelse til og fastslå den relative placering af Wi‑Fi-enheder i nærheden"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Foretrukne oplysninger vedrørende NFC-betalingstjeneste"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillader, at appen får foretrukne oplysninger vedrørende NFC-betalingstjeneste, f.eks. registrerede hjælpemidler og rutedestinationer."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"administrere Near Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Begivenhed"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Forstyr ikke (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administreres af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Til"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Fra"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> til <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle kalendere"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår nogle lyde fra"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Der er et internt problem med enheden, og den vil muligvis være ustabil, indtil du gendanner fabriksdataene."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send og modtag beskeder uden et mobil- eller Wi-Fi-netværk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Åbn Beskeder"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Sådan fungerer det"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Aktivér \"Vælg netværk automatisk\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Aktivér \"Vælg netværk automatisk\" under Indstillinger, så din telefon kan finde et netværk, der fungerer sammen med satellit"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktivér"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Gå tilbage"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer fingeroplåsning igen"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> kan ikke længere genkendes."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> kan ikke længere genkendes."</string>
@@ -2454,7 +2502,7 @@
<string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Sms"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musik"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
- <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Lommeregner"</string>
+ <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Lomme­regner"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kort"</string>
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Dine fingeraftryk kan ikke længere genkendes. Konfigurer fingeroplåsning igen."</string>
diff --git a/core/res/res/values-de-feminine/strings.xml b/core/res/res/values-de-feminine/strings.xml
new file mode 100644
index 000000000000..ef7f3bb8d193
--- /dev/null
+++ b/core/res/res/values-de-feminine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="owner_name" msgid="8713560351570795743">"Eigentümerin"</string>
+</resources>
diff --git a/core/res/res/values-de-masculine/strings.xml b/core/res/res/values-de-masculine/strings.xml
new file mode 100644
index 000000000000..f8c46e7fb174
--- /dev/null
+++ b/core/res/res/values-de-masculine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="owner_name" msgid="8713560351570795743">"Eigentümer"</string>
+</resources>
diff --git a/core/res/res/values-de-neuter/strings.xml b/core/res/res/values-de-neuter/strings.xml
new file mode 100644
index 000000000000..f8c46e7fb174
--- /dev/null
+++ b/core/res/res/values-de-neuter/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="owner_name" msgid="8713560351570795743">"Eigentümer"</string>
+</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index fa2c9cf73911..64afb5adffc4 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ermöglicht der App, die relative Distanz zwischen Ultrabreitband-Geräten in der Nähe zu bestimmen"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mit WLAN-Geräten in der Nähe interagieren"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Erlaubt der App, Inhalte an WLAN-Geräte in der Nähe zu senden, sich mit ihnen zu verbinden und ihre relative Position zu ermitteln"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informationen zum bevorzugten NFC-Zahlungsdienst"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ermöglicht der App, Informationen zum bevorzugten NFC-Zahlungsdienst abzurufen, etwa registrierte Hilfsmittel oder das Routenziel."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Nahfeldkommunikation steuern"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wochenende"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Termin"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Schlafen"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Bitte nicht stören (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Verwaltet von <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"An"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Aus"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> bis <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alle Kalender"</string>
<string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Du kannst ohne Mobilgerät oder WLAN Nachrichten senden und empfangen"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages öffnen"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"So funktionierts"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"„Netz automatisch auswählen“ aktivieren"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Aktiviere in den Einstellungen die Option „Netz automatisch auswählen“, damit dein Smartphone ein Netzwerk finden kann, das mit dem Satelliten funktioniert"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktivieren"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Zurück"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Entsperrung per Fingerabdruck neu einrichten"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> wird nicht mehr erkannt."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> und <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> werden nicht mehr erkannt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 9e360e8cc905..4ad85d97201a 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Επιτρέψτε στην εφαρμογή να προσδιορίζει τη σχετική θέση μεταξύ κοντινών συσκευών Ultra-Wideband"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"αλληλεπίδραση με κοντινές συσκευές Wi‑Fi"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Επιτρέπει στην εφαρμογή: προβολή διαφημίσεων, σύνδεση και καθορισμό της σχετικής τοποθεσίας των κοντινών συσκευών Wi‑Fi"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Πληροφορίες προτιμώμενης υπηρεσίας πληρωμών NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Επιτρέπει στην εφαρμογή να λαμβάνει πληροφορίες προτιμώμενης υπηρεσίας πληρωμής NFC, όπως καταχωρημένα βοηθήματα και προορισμό διαδρομής."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ελέγχει την Επικοινωνία κοντινού πεδίου (FNC)"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Σαββατοκύριακο"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Συμβάν"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ύπνος"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Μην ενοχλείτε (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Διαχείριση από <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ενεργός"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Ανενεργός"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> έως <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Οποιοδήποτε ημερολόγιο"</string>
<string name="muted_by" msgid="91464083490094950">"Το τρίτο μέρος <xliff:g id="THIRD_PARTY">%1$s</xliff:g> θέτει ορισμένους ήχους σε σίγαση"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας και ενδέχεται να είναι ασταθής μέχρι την επαναφορά των εργοστασιακών ρυθμίσεων."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Αποστολή και λήψη μηνυμάτων χωρίς δίκτυο κινητής τηλεφωνίας ή Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Άνοιγμα Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Πώς λειτουργεί"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Ενεργοποίηση της λειτουργίας Αυτόματη επιλογή δικτύου"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Ενεργοποιήστε τη λειτουργία Αυτόματη επιλογή δικτύου στις Ρυθμίσεις, ώστε το τηλέφωνό σας να μπορεί να βρει ένα δίκτυο που λειτουργεί με δορυφόρο"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ενεργοποίηση"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Επιστροφή"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Επαναρρύθμιση λειτουργίας Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Δεν είναι πλέον δυνατή η αναγνώριση του <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Δεν είναι πλέον δυνατή η αναγνώριση του <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> και του <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index d36ccf5a69a1..a82b567a087e 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Do Not Disturb (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Turn on \'Automatically select network\'"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Turn on \'Automatically select network\' in Settings so your phone can find a network that works with satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure that you\'re connected to the Internet and try setup again"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure that you\'re connected to the Internet and try setup again"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognised."</string>
@@ -2444,7 +2468,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3fac7360c8f2..b09a79a63039 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -612,6 +612,8 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby Ultra-Wideband devices"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect, and determine the relative position of nearby Wi‑Fi devices"</string>
+ <string name="permlab_ranging" msgid="2854543350668593390">"determine relative position between nearby devices"</string>
+ <string name="permdesc_ranging" msgid="6703905535621521710">"Allow the app to determine relative position between nearby devices"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC Payment Service Information"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred nfc payment service information like registered aids and route destination."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"control Near Field Communication"</string>
@@ -1938,6 +1940,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Do Not Disturb (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
@@ -2430,6 +2433,30 @@
<string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string>
<string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure you\'re connected to the internet and try setup again"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure you\'re connected to the internet and try setup again"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognized."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognized."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 48f0e1aea91a..bf3b985ae6e8 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Do Not Disturb (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Turn on \'Automatically select network\'"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Turn on \'Automatically select network\' in Settings so your phone can find a network that works with satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure that you\'re connected to the Internet and try setup again"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure that you\'re connected to the Internet and try setup again"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognised."</string>
@@ -2444,7 +2468,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 190e8abcff80..5c9c52148750 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Do Not Disturb (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Any calendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Turn on \'Automatically select network\'"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Turn on \'Automatically select network\' in Settings so your phone can find a network that works with satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure that you\'re connected to the Internet and try setup again"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure that you\'re connected to the Internet and try setup again"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognised."</string>
@@ -2444,7 +2468,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string>
diff --git a/core/res/res/values-es-rUS-feminine/strings.xml b/core/res/res/values-es-rUS-feminine/strings.xml
new file mode 100644
index 000000000000..bf181d022f9a
--- /dev/null
+++ b/core/res/res/values-es-rUS-feminine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="owner_name" msgid="8713560351570795743">"Propietaria"</string>
+</resources>
diff --git a/core/res/res/values-es-rUS-masculine/strings.xml b/core/res/res/values-es-rUS-masculine/strings.xml
new file mode 100644
index 000000000000..4b67970f668b
--- /dev/null
+++ b/core/res/res/values-es-rUS-masculine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="owner_name" msgid="8713560351570795743">"Propietario"</string>
+</resources>
diff --git a/core/res/res/values-es-rUS-neuter/strings.xml b/core/res/res/values-es-rUS-neuter/strings.xml
new file mode 100644
index 000000000000..4b67970f668b
--- /dev/null
+++ b/core/res/res/values-es-rUS-neuter/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="owner_name" msgid="8713560351570795743">"Propietario"</string>
+</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a48f5fa984b8..ab6f6ce07c1d 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la app determine la posición relativa con dispositivos Ultra Wideband cercanos"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos Wi-Fi cercanos"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que la app muestre anuncios, se conecte y determine la posición relativa de los dispositivos Wi-Fi cercanos"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre servicio de pago NFC preferido"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la app reciba información del servicio de pago NFC preferido, como el servicio de asistencia registrado y el destino de la ruta."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlar la Transmisión de datos en proximidad"</string>
@@ -1938,14 +1942,14 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Noche, en la semana"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sueño"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"No interrumpir (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administradas por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activado"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Cualquier calendario"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Existe un problema interno con el dispositivo, de modo que el dispositivo puede estar inestable hasta que restablezcas la configuración de fábrica."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía y recibe mensajes sin una red móvil ni Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensajes"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activa \"Seleccionar red de forma automática\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activa \"Seleccionar red de forma automática\" en la configuración para que tu teléfono pueda encontrar una red que funcione con satélite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activar"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Atrás"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"La función SOS por satélite ya está disponible"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Puedes enviar mensajes a los servicios de emergencia si no hay una red móvil o Wi-Fi. Tu app de mensajería predeterminada debe ser Mensajes de Google."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"No se admite la función SOS por satélite"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Este dispositivo no admite la función SOS por satélite"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"No se configuró la función SOS por satélite"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Asegúrate de tener conexión a Internet y vuelve a intentar la configuración"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"La función SOS por satélite no está disponible"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"La función SOS por satélite no está disponible en este país o región"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"No se configuró la función SOS por satélite"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensajes por satélite, establece Mensajes de Google como tu app de mensajería predeterminada"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"La función SOS por satélite no está disponible"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar si la función SOS por satélite está disponible en este país o región, activa la configuración de ubicación"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"La Mensajería satelital está disponible"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Puedes enviar mensajes por satélite si no hay una red móvil o Wi-Fi. Tu app de mensajería predeterminada debe ser Mensajes de Google."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"No se admite la Mensajería satelital"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Este dispositivo no admite la Mensajería satelital"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"No se configuró la Mensajería satelital"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Asegúrate de tener conexión a Internet y vuelve a intentar la configuración"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"La Mensajería satelital no disponible"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"La Mensajería satelital no está disponible en este país o región"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"No se configuró la Mensajería satelital"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensajes por satélite, establece Mensajes de Google como tu app de mensajería predeterminada"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"La Mensajería satelital no disponible"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar si la Mensajería satelital está disponible en este país o región, activa la configuración de ubicación"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vuelve a configurar el Desbloqueo con huellas dactilares"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ya no se puede reconocer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Ya no se pueden reconocer <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
@@ -2445,7 +2469,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Ya no se puede reconocer tu modelo de rostro. Vuelve a configurar el Desbloqueo facial."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ahora no"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambiar de usuario"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Silenciar"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Presiona para silenciar el sonido"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a573e9d42b1e..23e1ae635730 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la aplicación determine la posición relativa de los dispositivos de banda ultraancha cercanos"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos Wi-Fi cercanos"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite a la aplicación emitir y conectarse a dispositivos Wi-Fi cercanos y determinar su posición relativa"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre el servicio de pago por NFC preferido"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la aplicación obtenga información sobre el servicio de pago por NFC preferido, como identificadores de aplicación registrados y destinos de rutas."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicación de campo cercano (NFC)"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmiendo"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"No molestar (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionado por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Cualquier calendario"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Se ha producido un problema interno en el dispositivo y es posible que este no sea estable hasta que restablezcas el estado de fábrica."</string>
@@ -2427,15 +2431,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía y recibe mensajes sin una red móvil ni Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre Mensajes"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activa la opción Seleccionar red automáticamente"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activa la opción Seleccionar red automáticamente en Ajustes para que tu teléfono pueda encontrar una red que funcione con satélite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activar"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Volver"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura Desbloqueo con huella digital de nuevo"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ya no puede reconocerse."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ya no pueden reconocerse."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 22c777707dee..d22c93b30ac9 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Võimaldab rakendusel määrata lähedalasuvate ülilairibaühendust kasutavate seadmete suhtelise kauguse üksteisest"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Läheduses olevate WiFi-seadmetega suhtlemine"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lubab rakendusel läheduses olevatele WiFi-seadmetele reklaamida, nendega ühenduse luua ja määrata nende suhteline asend"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Eelistatud NFC-makseteenuse teave"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Võimaldab rakendusel hankida eelistatud NFC-makseteenuse teavet (nt registreeritud abi ja marsruudi sihtkoht)."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"lähiväljaside juhtimine"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nädalavahetus"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sündmus"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Magamine"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Mitte segada (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Haldab <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Sees"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Väljas"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> kuni <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Mis tahes kalender"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistab teatud helid"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Seadmes ilmnes sisemine probleem ja seade võib olla ebastabiilne seni, kuni lähtestate seadme tehase andmetele."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Sõnumite saatmine ja vastuvõtmine ilma mobiilside- või WiFi-võrguta"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ava rakendus Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Tööpõhimõtted"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Valiku „Vali võrk automaatselt“ sisselülitamine"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Lülitage seadetes sisse valik „Vali võrk automaatselt“, et telefon leiaks satelliidi abil töötava võrgu"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Lülita sisse"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Mine tagasi"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Seadistage sõrmejäljega avamine uuesti"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Sõrmejälge <xliff:g id="FINGERPRINT">%s</xliff:g> ei saa enam tuvastada."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Sõrmejälgi <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ei saa enam tuvastada."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 8d8ed8fb369b..914dfb72c06e 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehazteko baimena ematen dio aplikazioari"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"inguruko wifi-gailuekin interakzioan jardun"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Inguruko wifi-gailuetan iragartzeko, haiekin konektatzeko eta haien kokapena zehazteko baimena ematen dio aplikazioari"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa jasotzeko baimena ematen dio aplikazioari, hala nola erregistratutako laguntzaileak eta ibilbidearen helmuga."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrolatu Near Field Communication komunikazioa"</string>
@@ -1532,7 +1536,7 @@
<string name="wallpaper_binding_label" msgid="1197440498000786738">"Horma-papera"</string>
<string name="chooser_wallpaper" msgid="3082405680079923708">"Aldatu horma-papera"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"Jakinarazpen-hautemailea"</string>
- <string name="vr_listener_binding_label" msgid="8013112996671206429">"Errealitate birtualeko hautemailea"</string>
+ <string name="vr_listener_binding_label" msgid="8013112996671206429">"EBko hautemailea"</string>
<string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Baldintza-hornitzailea"</string>
<string name="notification_ranker_binding_label" msgid="432708245635563763">"Jakinarazpenen sailkapen-zerbitzua"</string>
<string name="vpn_title" msgid="5906991595291514182">"VPN eginbidea aktibatuta"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Asteburua"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Gertaera"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Lo egiteko"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ez molestatzeko modua (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Kudeatzailea: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktibatuta"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desaktibatuta"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Edozein egutegi"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Barneko arazo bat dago zure gailuan eta agian ezegonkor egongo da jatorrizko datuak berrezartzen dituzun arte."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Bidali eta jaso mezuak sare mugikorrik edo wifi-sarerik gabe"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ireki Mezuak"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Nola funtzionatzen du?"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Aktibatu \"Hautatu sarea automatikoki\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Aktibatu \"Hautatu sarea automatikoki\" ezarpenetan, telefonoak satelitearekin bateragarria den sare bat bilatu ahal izan dezan"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktibatu"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Egin atzera"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguratu berriro hatz-marka bidez desblokeatzeko eginbidea"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ez da ezagutzen jada."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> eta <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ez dira ezagutzen jada."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d6d950c0f12a..c3e4b4680752 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"به برنامه اجازه داده می‌شود موقعیت نسبی بین دستگاه‌های «فراپهن‌باند» اطراف را مشخص کند"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏برقراری تعامل با دستگاه‌های Wi-Fi اطراف"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏به برنامه اجازه می‌دهد در دستگاه‌های Wi-Fi اطراف تبلیغ کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را تشخیص دهد"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پرداخت NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏به برنامه اجازه می‌دهد اطلاعات ترجیحی سرویس پرداخت NFC، مانند کمک‌های ثبت‌شده و مقصد مسیر را دریافت کند."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"کنترل ارتباط راه نزدیک"</string>
@@ -1568,7 +1572,7 @@
<string name="gpsNotifMessage" msgid="7346649122793758032">"درخواست‌کننده <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="3719843080744112940">"بله"</string>
<string name="gpsVerifNo" msgid="1671201856091564741">"نه"</string>
- <string name="sync_too_many_deletes" msgid="6999440774578705300">"از حد مجاز حذف فراتر رفت"</string>
+ <string name="sync_too_many_deletes" msgid="6999440774578705300">"از حد حذف کردن فراتر رفتید"</string>
<string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"‏<xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> مورد حذف‌شده برای <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>، حساب <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> وجود دارد. می‎خواهید چه کار بکنید؟"</string>
<string name="sync_really_delete" msgid="5657871730315579051">"حذف موارد"</string>
<string name="sync_undo_deletes" msgid="5786033331266418896">"واگرد موارد حذف شده"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"آخر هفته"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"رویداد"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"خوابیدن"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"مزاحم نشوید (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"تحت‌مدیریت <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"روشن"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"خاموش"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"‫<xliff:g id="START">%1$s</xliff:g> تا <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"هر تقویمی"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> درحال قطع کردن بعضی از صداهاست"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی داده‌های کارخانه انجام نگیرد، بی‌ثبات بماند."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏ارسال و دریافت پیام بدون شبکه تلفن همراه یا Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"باز کردن «پیام‌نگار»"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"روش کار"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"روشن کردن «انتخاب خودکار شبکه»"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"برای اینکه تلفنتان بتواند شبکه‌ای که با ماهواره کار می‌کند پیدا کند، «انتخاب خودکار شبکه» را در «تنظیمات» روشن کنید"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"روشن کردن"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"برگشتن"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"راه‌اندازی مجدد «قفل‌گشایی با اثر انگشت»"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"‫<xliff:g id="FINGERPRINT">%s</xliff:g> دیگر قابل‌شناسایی نیست."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"‫<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> دیگر قابل‌شناسایی نیستند."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index afc20f6c0fcd..fb7c4fed3f47 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Sallii sovelluksen määrittää UVB-taajuutta käyttävien laitteiden sijainnin suhteessa toisiinsa"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"käyttää lähellä olevia Wi-Fi-laitteita"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Sallii sovelluksen ilmoittaa ja määrittää lähellä olevien Wi-Fi-laitteiden suhteellisen sijainnin sekä yhdistää niihin"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ensisijaiset NFC-maksupalvelutiedot"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Sallii sovelluksen noutaa tietoja rekisteröidyistä sovellustunnuksista, maksureitin kohteesta ja muita ensisijaisia NFC-maksupalvelutietoja."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"hallitse Near Field Communication -tunnistusta"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Viikonloppuna"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tapahtuma"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Nukkuminen"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Älä häiritse (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Ylläpitäjä: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Päällä"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Pois päältä"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kaikki kalenterit"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mykistää joitakin ääniä"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Laitteellasi on sisäinen ongelma, joka aiheuttaa epävakautta. Voit korjata tilanteen palauttamalla tehdasasetukset."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Lähetä ja vastaanota viestejä ilman mobiili- tai Wi-Fi-verkkoa"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Avaa Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Näin se toimii"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Laita \"Valitse verkko automaattisesti\" päälle"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Laita \"Valitse verkko automaattisesti\" päälle asetuksista, jotta puhelin voi löytää satelliitin kanssa toimivan verkon."</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Laita päälle"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Takaisin"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ota sormenjälkiavaus uudelleen käyttöön"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei enää ole tunnistettavissa."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> eivät enää ole tunnistettavissa."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6add38ad671a..18ef10995e90 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autorisez l\'appli à déterminer la position relative entre des appareils à bande ultralarge à proximité"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'appli de diffuser des annonces, de se connecter et de déterminer la position relative des appareils Wi-Fi à proximité"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information sur le service préféré de paiement CCP"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'appli d\'obtenir de l\'information sur le service préféré de paiement CCP comme les aides enregistrées et la route de destination."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"gérer la communication en champ proche"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semaine"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne pas déranger (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivée"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> à <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"N\'importe quel agenda"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à ses paramètres par défaut."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envoyez et recevez des messages sans réseau cellulaire ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activer « Sélectionner automatiquement le réseau »"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activez « Sélectionner automatiquement le réseau » dans les paramètres pour que votre téléphone puisse trouver un réseau qui fonctionne par satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activer"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retour"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS par satellite est maintenant accessible"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Vous pouvez envoyer un message aux services d\'urgence s\'il n\'y a pas de réseau mobile ou Wi-Fi. Messages de Google doit être votre appli de messagerie par défaut."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS par satellite n\'est pas prise en charge"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS par satellite n\'est pas prise en charge sur cet appareil"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS par satellite n\'est pas configurée"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Assurez-vous que vous êtes connecté à Internet et réessayez d\'effectuer la configuration"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS par satellite n\'est pas accessible"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS par satellite n\'est pas accessible dans ce pays ou cette région"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS par satellite n\'est pas configurée"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Pour envoyer des messages par satellite, définissez Messages de Google comme appli de messagerie par défaut"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS par satellite n\'est pas accessible"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Pour vérifier si SOS par satellite est accessible dans ce pays ou cette région, activez les paramètres de localisation"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"La messagerie par satellite est accessible"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Vous pouvez envoyer des messages par satellite s\'il n\'y a pas de réseau mobile ou Wi-Fi. Messages de Google doit être votre appli de messagerie par défaut."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"La messagerie par satellite n\'est pas prise en charge"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"La messagerie par satellite n\'est pas prise en charge sur cet appareil"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"La messagerie par satellite n\'est pas configurée"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Assurez-vous que vous êtes connecté à Internet et réessayez d\'effectuer la configuration"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"La messagerie par satellite n\'est pas accessible"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"La messagerie par satellite n\'est pas accessible dans ce pays ou cette région"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"La messagerie par satellite n\'est pas configurée"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Pour envoyer des messages par satellite, définissez Messages de Google comme appli de messagerie par défaut"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"La messagerie par satellite n\'est pas accessible"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Pour vérifier si la messagerie par satellite est accessible dans ce pays ou cette région, activez les paramètres de localisation"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurer le Déverrouillage par empreinte digitale à nouveau"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"L\'empreinte digitale <xliff:g id="FINGERPRINT">%s</xliff:g> ne peut plus être reconnue."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Les empreintes digitales <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne peuvent plus être reconnues."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9de43343ded8..bd064d1dc61e 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autoriser l\'appli à déterminer la position relative entre des appareils ultra-wideband à proximité"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'appli de déterminer la position approximative des appareils Wi‑Fi à proximité, de les afficher et de s\'y connecter"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informations sur le service de paiement NFC préféré"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'application d\'obtenir des informations sur le service de paiement NFC préféré, y compris les ID d\'applications et les destinations de routage enregistrés."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"contrôler la communication en champ proche"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Week-end"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne pas déranger (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> à <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Tous les agendas"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> coupe certains sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Un problème interne lié à votre appareil est survenu. Ce dernier risque d\'être instable jusqu\'à ce que vous rétablissiez la configuration d\'usine."</string>
@@ -2427,15 +2431,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envoyer et recevoir des messages sans réseau mobile ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activer \"Sélectionner automatiquement le réseau\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activez \"Sélectionner automatiquement le réseau\" dans Paramètres pour que votre téléphone puisse trouver un réseau compatible avec un satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activer"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retour"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Reconfigurer le déverrouillage par empreinte digitale"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne peut plus être reconnue."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne peuvent plus être reconnues."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 06c63ecd02ac..292a952b1d46 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -612,8 +612,12 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que a aplicación determine a posición relativa entre os dispositivos próximos que usen banda ultralarga"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos wifi próximos"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permítelle á aplicación enviar anuncios e conectarse a dispositivos wifi próximos, e determinar a súa posición relativa"</string>
- <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información do servizo de pago de NFC preferido"</string>
- <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a aplicación obteña información do servizo de pago de NFC preferido, como as axudas rexistradas e o destino da ruta."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
+ <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información do servizo de pagos de NFC preferido"</string>
+ <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a aplicación obteña información do servizo de pagos de NFC preferido, como as axudas rexistradas e o destino da ruta."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlar Near Field Communication"</string>
<string name="permdesc_nfc" msgid="8352737680695296741">"Permite á aplicación comunicarse con etiquetas, tarxetas e lectores Near Field Communication (NFC)."</string>
<string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"Evento de transacción no elemento seguro"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Mentres durmo"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Modo Non molestar (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Xestionada por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivada"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"De <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Calquera calendario"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando algúns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Produciuse un erro interno no teu dispositivo e quizais funcione de maneira inestable ata o restablecemento dos datos de fábrica."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía e recibe mensaxes sen ter acceso a redes de telefonía móbil ou wifi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensaxes"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona?"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activar Seleccionar rede automaticamente"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activa a opción Seleccionar rede automaticamente en Configuración para que o teléfono busque unha rede que funcione co satélite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activar"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Volver"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura de novo o desbloqueo dactilar"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> xa non se recoñece."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> xa non se recoñecen."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index b0194ca80400..70454a307791 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ઍપને નજીકના અલ્ટ્રા-વાઇડબૅન્ડ ડિવાઇસની વચ્ચેનું સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપો"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"નજીકના વાઇ-ફાઇ ડિવાઇસ સાથે ક્રિયાપ્રતિક્રિયા કરો"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ઍપને નજીકના વાઇ-ફાઇ ડિવાઇસની માહિતી બતાવવાની, તેની સાથે કનેક્ટ કરવાની અને તેની સંબંધિત સ્થિતિ નક્કી કરવાની મંજૂરી આપો"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"આ મંજૂરીને આપવાથી, ઍપ તમારી પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી મેળવી શકે છે, જેમ કે રજિસ્ટર થયેલી સહાય અને નિર્ધારિત સ્થાન."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"નિઅર ફીલ્ડ કમ્યુનિકેશન નિયંત્રિત કરો"</string>
@@ -1663,7 +1667,7 @@
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"હેડફોન"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"સિસ્ટમ"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"બ્લૂટૂથ ઑડિઓ"</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"બ્લૂટૂથ ઑડિયો"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"વાયરલેસ ડિસ્પ્લે"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"કાસ્ટ કરો"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"ઉપકરણ સાથે કનેક્ટ કરો"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"સપ્તાહાંત"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ઇવેન્ટ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"નિષ્ક્રિય"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ખલેલ પાડશો નહીં (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> દ્વારા મેનેજ કરવામાં આવે છે"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ચાલુ છે"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"બંધ છે"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>થી <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"કોઈપણ કૅલેન્ડર"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> અમુક અવાજોને મ્યૂટ કરે છે"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે અને જ્યાં સુધી તમે ફેક્ટરી ડેટા ફરીથી સેટ કરશો નહીં ત્યાં સુધી તે અસ્થિર રહી શકે છે."</string>
@@ -2058,7 +2062,7 @@
<string name="conference_call" msgid="5731633152336490471">"કોન્ફરન્સ કૉલ"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"ટૂલટિપ"</string>
<string name="app_category_game" msgid="4534216074910244790">"રમતો"</string>
- <string name="app_category_audio" msgid="8296029904794676222">"સંગીત અને ઑડિયો"</string>
+ <string name="app_category_audio" msgid="8296029904794676222">"મ્યુઝિક અને ઑડિયો"</string>
<string name="app_category_video" msgid="2590183854839565814">"મૂવી અને વીડિઓ"</string>
<string name="app_category_image" msgid="7307840291864213007">"ફોટો અને છબીઓ"</string>
<string name="app_category_social" msgid="2278269325488344054">"સામાજિક અને સંચાર"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"મોબાઇલ કે વાઇ-ફાઇ નેટવર્ક વિના મેસેજ મોકલો અને મેળવો"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ખોલો"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"તેની કામ કરવાની રીત"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"ઑટોમૅટિક રીતે નેટવર્ક પસંદ કરો\" ચાલુ કરો"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"સેટિંગમાં \"ઑટોમૅટિક રીતે નેટવર્ક પસંદ કરો\" ચાલુ કરો, જેથી તમારો ફોન સૅટલાઇટ સાથે કામ કરતું નેટવર્ક શોધી શકે"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ચાલુ કરો"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"પાછા જાઓ"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"હવે <xliff:g id="FINGERPRINT">%s</xliff:g> ઓળખી શકાતી નથી."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"હવે <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> અને <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ઓળખી શકાતી નથી."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7e27cff5ba13..5422f1befc40 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ऐप्लिकेशन को आस-पास मौजूद Ultra-Wideband डिवाइसों के बीच की दूरी का पता लगाने की अनुमति दें"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"आस-पास मौजूद वाई-फ़ाई डिवाइसों से इंटरैक्ट करें"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"इससे, ऐप्लिकेशन आस-पास मौजूद वाई-फ़ाई डिवाइसों की जानकारी दिखा पाएगा, उनसे कनेक्ट कर पाएगा, और उनकी दूरी पता लगा पाएगा"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC का इस्तेमाल करने वाली पैसे चुकाने की पसंदीदा सेवा की जानकारी"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"अगर ऐप्लिकेशन को अनुमति दी जाती है, तो वह पैसे चुकाने की आपकी उस पसंदीदा सेवा के बारे में जानकारी पा सकता है जो NFC का इस्तेमाल करती है. इसमें रजिस्टर किए गए डिवाइस और उनके आउटपुट के रूट जैसी जानकारी शामिल होती है."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"नियर फ़ील्‍ड कम्‍यूनिकेशन नियंत्रित करें"</string>
@@ -1937,15 +1941,15 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"हफ़्ते की रात"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"सप्ताहांत"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"इवेंट"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"सोने का समय"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"स्लीपिंग मोड"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"\'परेशान न करें\' सुविधा (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"मैनेज करने वाला ऐप्लिकेशन: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"चालू है"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"बंद है"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
- <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कोई भी कैलेंडर"</string>
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> से <xliff:g id="END">%2$s</xliff:g>"</string>
+ <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"किसी भी कैलेंडर के इवेंट के लिए"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> कुछ आवाज़ें म्‍यूट कर रहा है"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"आपके डिवाइस में कोई अंदरूनी समस्या है और यह तब तक ठीक नहीं होगी जब तक आप फ़ैक्‍टरी डेटा रीसेट नहीं करते."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"आपके डिवाइस के साथ कोई आंतरिक गड़बड़ी हुई. विवरणों के लिए अपने निर्माता से संपर्क करें."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल या वाई-फ़ाई नेटवर्क के बिना मैसेज भेजें और पाएं"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ऐप्लिकेशन खोलें"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यह सेटिंग कैसे काम करती है"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"नेटवर्क अपने-आप चुना जाए\" को चालू करें"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"सेटिंग में जाकर, \"नेटवर्क अपने-आप चुना जाए\" को चालू करें, ताकि आपका फ़ोन ऐसा नेटवर्क ढूंढ सके जो सैटलाइट के साथ काम करता है"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"चालू करें"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"रद्द करें"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"सैटलाइट एसओएस की सुविधा अब उपलब्ध है"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"मोबाइल या वाई-फ़ाई नेटवर्क न होने पर भी, आपातकालीन सेवाओं को मैसेज भेजा जा सकता है. इसके लिए, आपको Google Messages को अपने डिवाइस के डिफ़ॉल्ट मैसेजिंग ऐप्लिकेशन के तौर पर सेट करना होगा."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"सैटलाइट एसओएस की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"इस डिवाइस पर सैटलाइट एसओएस की सुविधा काम नहीं करती"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"सैटलाइट एसओएस सेट अप नहीं किया गया है"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"पक्का करें कि आपका डिवाइस, इंटरनेट से कनेक्ट है या नहीं. इसके बाद, फिर से सेटअप करने की कोशिश करें"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"सैटलाइट एसओएस की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"इस देश या इलाके में सैटलाइट एसओएस की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"सैटलाइट एसओएस की सुविधा सेट अप नहीं की गई"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"सैटलाइट से मैसेज भेजने के लिए, Google Messages को डिफ़ॉल्ट मैसेजिंग ऐप्लिकेशन के तौर पर सेट करें"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"सैटलाइट एसओएस की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"जगह की जानकारी की सेटिंग चालू करें. इससे यह देखा जा सकता है कि इस देश या इलाके में सैटलाइट एसओएस की सुविधा उपलब्ध है या नहीं"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा उपलब्ध है"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"मोबाइल या वाई-फ़ाई नेटवर्क न होने पर भी, सैटलाइट के ज़रिए मैसेज भेजा जा सकता है. इसके लिए, आपको Google Messages को अपने डिवाइस के डिफ़ॉल्ट मैसेजिंग ऐप्लिकेशन के तौर पर सेट करना होगा."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा काम नहीं करती"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"इस डिवाइस पर सैटलाइट के ज़रिए मैसेज भेजने की सुविधा काम नहीं करती"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा सेट अप नहीं की गई"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"पक्का करें कि आपका डिवाइस, इंटरनेट से कनेक्ट है या नहीं. इसके बाद, फिर से सेटअप करने की कोशिश करें"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"इस देश या इलाके में सैटलाइट के ज़रिए मैसेज भेजने की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा सेट अप नहीं की गई"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"सैटलाइट से मैसेज भेजने के लिए, Google Messages को डिफ़ॉल्ट मैसेजिंग ऐप्लिकेशन के तौर पर सेट करें"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा उपलब्ध नहीं है"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"जगह की जानकारी की सेटिंग चालू करें. इससे यह देखा जा सकता है कि इस देश या इलाके में सैटलाइट के ज़रिए मैसेज भेजने की सुविधा उपलब्ध है या नहीं"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फ़िंगरप्रिंट अनलॉक की सुविधा दोबारा सेट अप करें"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"अब <xliff:g id="FINGERPRINT">%s</xliff:g> की पहचान नहीं की जा सकती."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"अब <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> और <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> की पहचान नहीं की जा सकती."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1066559d8f7c..0594788fa9ce 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dopušta aplikaciji da odredi približni položaj između uređaja u blizini koji upotrebljavaju ultraširokopojasno povezivanje"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija s Wi-Fi uređajima u blizini"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji omogućuje oglašavanje, povezivanje i određivanje približnog položaja Wi-Fi uređaja u blizini"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o preferiranoj usluzi plaćanja NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Omogućuje aplikaciji primanje informacija o preferiranoj usluzi plaćanja NFC kao što su registrirana pomagala i odredište."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"upravljanje beskontaktnom komunikacijom (NFC)"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne uznemiravaj (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bilo koji kalendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Na vašem uređaju postoji interni problem i možda neće biti stabilan dok ga ne vratite na tvorničko stanje."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke kad nije dostupna mobilna ili Wi-Fi mreža"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Poruke"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako to funkcionira"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Uključite opciju Automatski odaberi mrežu"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Uključite opciju Automatski odaberi mrežu u postavkama da bi telefon mogao pronaći mrežu koja funkcionira sa satelitom"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Uključi"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Natrag"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS putem satelita sad je dostupan"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Možete slati poruke hitnim službama ako mobilna ili Wi-Fi mreža nije dostupna. Google poruke moraju biti vaša zadana aplikacija za slanje poruka."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS putem satelita nije podržan"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS putem satelita nije podržan na ovom uređaju"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS putem satelita nije postavljen"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Provjerite jeste li povezani s internetom i pokušajte ponovo s postavljanjem"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS putem satelita nije dostupan"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS putem satelita nije dostupan u ovoj državi ili regiji"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS putem satelita nije postavljen"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Da biste slali poruke putem satelita, postavite Google poruke kao zadanu aplikaciju za slanje poruka"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS putem satelita nije dostupan"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Da biste provjerili je li SOS putem satelita dostupan u ovoj državi ili regiji, uključite postavke lokacije"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Slanje poruka putem satelita je dostupno"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Možete slati poruke putem satelita ako mobilna ili Wi-Fi mreža nije dostupna. Google poruke moraju biti vaša zadana aplikacija za slanje poruka."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Slanje poruka putem satelita nije podržano"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Slanje poruka putem satelita nije podržano na ovom uređaju"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Slanje poruka putem satelita nije postavljeno"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Provjerite jeste li povezani s internetom i pokušajte ponovo s postavljanjem"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Slanje poruka putem satelita nije dostupno"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Slanje poruka putem satelita nije dostupno u ovoj državi ili regiji"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Slanje poruka putem satelita nije postavljeno"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Da biste slali poruke putem satelita, postavite Google poruke kao zadanu aplikaciju za slanje poruka"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Slanje poruka putem satelita nije dostupno"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Da biste provjerili je li slanje poruka putem satelita dostupno u ovoj državi ili regiji, uključite postavke lokacije"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovno postavite otključavanje otiskom prsta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> više se ne prepoznaje."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> više se ne prepoznaju."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index c76d9f493c30..8adb234830aa 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Az alkalmazás meghatározhatja a közeli, ultraszélessávú eszközök közötti relatív pozíciót"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"műveletek végrehajtása a közeli Wi‑Fi-eszközökkel"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Engedélyezi az alkalmazás számára, hogy közzétegye és meghatározza a közeli Wi-Fi-eszközök viszonylagos helyzetét, és csatlakozzon hozzájuk."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferált NFC fizetési szolgáltatási információk"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lehetővé teszi az alkalmazás számára preferált NFC fizetési szolgáltatási információk (pl. regisztrált alkalmazásazonosítók és útvonali cél) lekérését."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFC technológia vezérlése"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hétvége"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esemény"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Alvás"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne zavarjanak (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Kezelő: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Bekapcsolva"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kikapcsolva"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bármilyen naptár"</string>
<string name="muted_by" msgid="91464083490094950">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> lenémít néhány hangot"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Belső probléma van az eszközzel, és instabil lehet, amíg vissza nem állítja a gyári adatokat."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Küldhet és fogadhat üzeneteket mobil- és Wi-Fi-hálózat nélkül is"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"A Messages megnyitása"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hogyan működik?"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Kapcsolja be a „Hálózat automatikus kiválasztása” beállítást"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Kapcsolja be a „Hálózat automatikus kiválasztása” beállítást a Beállításokban, hogy a telefon megtalálja a műholddal működő hálózatot"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Bekapcsolás"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Vissza"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"A Feloldás ujjlenyomattal funkció újbóli beállítása"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A következő már nem felismerhető: <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"A következők már nem felismerhetők: <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> és <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index c91918da268e..4a16d2f37194 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Թույլատրել հավելվածին որոշել գերլայնաշերտ կապի տեխնոլոգիան աջակցող մոտակա սարքերի միջև հարաբերական դիրքավորումը"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"փոխներգործել մոտակա Wi‑Fi սարքերի հետ"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Թույլ է տալիս հավելվածին տվյալներ փոխանցել մոտակա Wi‑Fi սարքերին, միանալ դրանց և որոշել դրանց մոտավոր դիրքը։"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Տեղեկություններ NFC վճարային ծառայության մասին"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Թույլ է տալիս հավելվածին ստանալ նախընտրելի NFC վճարային ծառայության մասին տեղեկություններ (օր․՝ գրանցված լրացուցիչ սարքերի և երթուղու նպատակակետի մասին տվյալներ)։"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"վերահսկել Մոտ Տարածությամբ Հաղորդակցումը"</string>
@@ -1112,7 +1116,7 @@
<string name="menu_sym_shortcut_label" msgid="4037566049061218776">"Sym+"</string>
<string name="menu_function_shortcut_label" msgid="2367112760987662566">"Function+"</string>
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"բացակ"</string>
- <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"մուտք"</string>
+ <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ջնջել"</string>
<string name="search_go" msgid="2141477624421347086">"Որոնել"</string>
<string name="search_hint" msgid="455364685740251925">"Որոնում..."</string>
@@ -1600,7 +1604,7 @@
<string name="keyboardview_keycode_done" msgid="2524518019001653851">"Պատրաստ է"</string>
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Ռեժիմի փոփոխում"</string>
<string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string>
- <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Մուտք"</string>
+ <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string>
<string name="activitychooserview_choose_application" msgid="3500574466367891463">"Ընտրել ծրագիր"</string>
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Չհաջողվեց գործարկել <xliff:g id="APPLICATION_NAME">%s</xliff:g> ծրագիրը"</string>
<string name="shareactionprovider_share_with" msgid="2753089758467748982">"Կիսվել"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Շաբաթ-կիրակի"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Միջոցառում"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնի ժամ"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Չանհանգստացնել (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Կառավարվում է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Միացված է"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Անջատված է"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Ցանկացած օրացույց"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ն անջատում է որոշ ձայներ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Ուղարկեք և ստացեք հաղորդագրություններ առանց բջջային կամ Wi-Fi ցանցի"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ինչպես է դա աշխատում"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Միացրեք «Ավտոմատ ընտրել ցանցը»"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Կարգավորումներում միացրեք «Ավտոմատ ընտրել ցանցը», որպեսզի ձեր հեռախոսը կարողանա արբանյակի հետ աշխատող ցանց գտնել"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Միացնել"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Հետ"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Նորից կարգավորեք մատնահետքով ապակողպումը"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> մատնահետքն այլևս չի կարող ճանաչվել։"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> և <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> մատնահետքերն այլևս չեն կարող ճանաչվել։"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 498463083d68..e90137fd761f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Mengizinkan aplikasi menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan perangkat Wi-Fi di sekitar"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Mengizinkan aplikasi menampilkan, menghubungkan, dan menentukan posisi relatif perangkat Wi-Fi di sekitar"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasi Layanan Pembayaran NFC Pilihan"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Mengizinkan aplikasi untuk mendapatkan informasi layanan pembayaran NFC pilihan seperti bantuan terdaftar dan tujuan rute."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrol NFC"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Akhir pekan"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Jangan Ganggu (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Dikelola oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktif"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Nonaktif"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> hingga <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kalender mana saja"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mematikan beberapa suara"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mengirim dan menerima pesan tanpa jaringan seluler atau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Message"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara kerjanya"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Aktifkan \"Pilih jaringan secara otomatis\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Aktifkan \"Pilih jaringan secara otomatis\" di Setelan agar ponsel dapat menemukan jaringan yang berfungsi dengan satelit"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktifkan"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Kembali"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Siapkan Buka dengan Sidik Jari lagi"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak dapat dikenali lagi."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak dapat dikenali lagi."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 47a6c3584f52..9ca5731ad1eb 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Leyfa forritinu að ákvarða fjarlægð milli nálægra tækja með ofurbreiðband"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"eiga í samskiptum við nálæg WiFi-tæki"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Leyfir forritinu að auglýsa, tengja og áætla staðsetningu nálægra WiFi-tækja"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Upplýsingar um valda NFC-greiðsluþjónustu"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gerir forritinu kleift að fá valda NFC-greiðsluþjónustu, svo sem skráða aðstoð og áfangastað leiðar."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"stjórna nándarsamskiptum (NFC)"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helgi"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Viðburður"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Svefn"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ónáðið ekki (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Stýrt af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Kveikt"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Slökkt"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> til <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Öll dagatöl"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> þaggar í einhverjum hljóðum"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Innra vandamál kom upp í tækinu og það kann að vera óstöðugt þangað til þú núllstillir það."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Senda og fá skilaboð án tengingar við farsímakerfi eða Wi-Fi-net"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Opna Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Svona virkar þetta"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Kveiktu á „Velja net sjálfkrafa“"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Kveiktu á „Velja net sjálfkrafa“ í stillingunum til að gera símanum kleift að finna net sem virkar með gervihnetti"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Kveikja"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Til baka"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Nú er gervihnattar-SOS tiltækt"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Þú getur sent neyðarþjónustu skilaboð, jafnvel þótt farsímakerfi eða WiFi-tenging sé ekki til staðar. Google Messages verður að vera stillt sem sjálfgefið skilaboðaforrit."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Gervihnattar-SOS er ekki stutt"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Gervihnattar-SOS er ekki stutt í þessu tæki"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Gervihnattar-SOS er ekki uppsett"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Gakktu úr skugga um að þú sért með nettengingu og reyndu uppsetningu aftur"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Gervihnattar-SOS er ekki tiltækt"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Gervihnattar-SOS er ekki tiltækt í þessu landi eða landsvæði"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Gervihnattar-SOS er ekki uppsett"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Til að senda skilaboð í gegnum gervihnött skaltu stilla Google Messages sem sjálfgefið skilaboðaforrit"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Gervihnattar-SOS er ekki tiltækt"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Til að athuga hvort gervihnattar-SOS eru tiltæk í þessu landi eða á þessu svæði skaltu kveikja á staðsetningarstillingum"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Skilaboð í gegnum gervihnött eru tiltæk"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Þú getur sent skilaboð um gervihnött ef það er ekkert farsímakerfi eða WiFi-net. Google Messages verður að vera stillt sem sjálfgefið skilaboðaforrit."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Skilaboð í gegnum gervihnött eru ekki studd"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Skilaboð í gegnum gervihnött eru ekki studd í þessu tæki"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Skilaboð í gegnum gervihnött eru ekki uppsett"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Gakktu úr skugga um að þú sért með nettengingu og reyndu uppsetningu aftur"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Skilaboð í gegnum gervihnött eru ekki tiltæk"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Skilaboð í gegnum gervihnött eru ekki tiltæk í þessu landi eða á þessu svæði"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Skilaboð í gegnum gervihnött eru ekki uppsett"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Til að senda skilaboð í gegnum gervihnött skaltu stilla Google Messages sem sjálfgefið skilaboðaforrit"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Skilaboð í gegnum gervihnött eru ekki tiltæk"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Til að athuga hvort skilaboð í gegnum gervihnött eru tiltæk í þessu landi eða á þessu svæði skaltu kveikja á staðsetningarstillingum"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setja upp fingrafarskenni aftur"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ekki er hægt að bera kennsl á <xliff:g id="FINGERPRINT">%s</xliff:g> lengur."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Ekki er hægt að bera kennsl á <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> lengur."</string>
@@ -2444,7 +2468,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Ekki er hægt að bera kennsl á andlitslíkanið þitt lengur. Settu upp andlitskenni aftur."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Setja upp"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ekki núna"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Viðvörun fyrir: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Viðvörun: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Skipta um notanda"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Þagga"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Ýttu til að þagga hljóð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4070f1160aec..ac0e4580b7a0 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Consenti all\'app di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Interazione con dispositivi Wi-Fi vicini"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Consente all\'app di trasmettere annunci e connettersi a dispositivi Wi‑Fi vicini e di stabilirne la posizione relativa."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informazioni del servizio di pagamento NFC preferito"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Consente all\'app di recuperare informazioni del servizio di pagamento NFC preferito, quali destinazione della route e identificatori applicazione registrati."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controllo Near Field Communication"</string>
@@ -1939,6 +1943,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fine settimana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Notte"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Non disturbare (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestione: app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
@@ -2431,6 +2436,54 @@
<string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Attiva"</string>
<string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Indietro"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
+ <skip />
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
+ <skip />
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Riconfigura lo Sblocco con l\'Impronta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> non può più essere riconosciuto."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non possono più essere riconosciuti."</string>
@@ -2440,7 +2493,7 @@
<string name="face_dangling_notification_msg" msgid="746235263598985384">"Il tuo modello del volto non può più essere riconosciuto. Riconfigura lo Sblocco con il Volto."</string>
<string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configura"</string>
<string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Non ora"</string>
- <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Sveglia per: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Sveglia per <xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambia utente"</string>
<string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Disattiva audio"</string>
<string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tocca per disattivare l\'audio"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 8b0dd8069f58..386f5318f53a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"‏האפליקציה תזהה את המיקום היחסי בין מכשירים קרובים שמשדרים בטכנולוגיית Ultra Wideband ‏(UWB)"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏אינטראקציה עם מכשירי Wi-Fi בקרבת מקום"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏האפליקציה תוכל לפרסם במכשירי Wi-Fi בקרבת מקום, להתחבר אליהם ולהעריך את המיקום היחסי שלהם"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏פרטים על שירות תשלום מועדף ב-NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏מאפשרת לאפליקציה לקבל פרטים על שירות תשלום מועדף ב-NFC, כמו עזרים רשומים ויעד של נתיב."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"שליטה בתקשורת מטווח קצר"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"סוף השבוע"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"אירוע"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"שינה"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"נא לא להפריע (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"בניהול של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"מצב פעיל"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"מצב מושבת"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"‫<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"‫<xliff:g id="START">%1$s</xliff:g> עד <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"כל יומן"</string>
<string name="muted_by" msgid="91464083490094950">"חלק מהצלילים מושתקים על ידי <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"קיימת בעיה פנימית במכשיר שלך, וייתכן שהוא לא יתפקד כראוי עד שיבוצע איפוס לנתוני היצרן."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏אפשר לשלוח ולקבל הודעות ללא רשת סלולרית או Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"‏לפתיחת Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"איך זה עובד"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"הפעלת האפשרות \'בחירה אוטומטית של הרשת\'"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"כדי למצוא רשת שעובדת עם לוויין בטלפון, צריך להפעיל את האפשרות \'בחירה אוטומטית של הרשת\' בהגדרות"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"הפעלה"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"חזרה"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"התכונה \"תקשורת לוויינית למצב חירום\" זמינה עכשיו"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"‏אפשר לשלוח הודעות לשירותי החירום כשאין חיבור לרשת סלולרית או לרשת Wi-Fi. חובה להגדיר את Google Messages כאפליקציית ברירת המחדל להודעות."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"התכונה \"תקשורת לוויינית למצב חירום\" לא נתמכת"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"המכשיר הזה לא תומך בתכונה \"תקשורת לוויינית למצב חירום\""</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"התכונה \"תקשורת לוויינית למצב חירום\" לא מוגדרת"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"צריך לוודא שיש חיבור לאינטרנט ולנסות שוב את תהליך ההגדרה"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"התכונה \"תקשורת לוויינית למצב חירום\" לא זמינה"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"התכונה \"תקשורת לוויינית למצב חירום\" לא זמינה במדינה הזו או באזור הזה"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"התכונה \"תקשורת לוויינית למצב חירום\" לא מוגדרת"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"‏כדי להעביר הודעות באמצעות לוויין, צריך להגדיר את Google Messages כאפליקציית ברירת המחדל להודעות"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"התכונה \"תקשורת לוויינית למצב חירום\" לא זמינה"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"כדי לבדוק אם התכונה \"תקשורת לוויינית למצב חירום\" זמינה במדינה או באזור שלך, צריך להפעיל את הגדרות המיקום"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"התכונה \"העברת הודעות באמצעות לוויין\" זמינה"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"‏כשאין חיבור לרשת סלולרית או לרשת Wi-Fi, אפשר להעביר הודעות בתקשורת לוויינית. חובה להגדיר את Google Messages כאפליקציית ברירת המחדל להודעות."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"התכונה \"העברת הודעות באמצעות לוויין\" לא נתמכת"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"המכשיר הזה לא תומך בתכונה \"העברת הודעות באמצעות לוויין\""</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"התכונה \"העברת הודעות באמצעות לוויין\" לא מוגדרת"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"צריך לוודא שיש חיבור לאינטרנט ולנסות שוב את תהליך ההגדרה"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"התכונה \"העברת הודעות באמצעות לוויין\" לא זמינה"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"התכונה \"העברת הודעות באמצעות לוויין\" לא זמינה במדינה הזו או באזור הזה"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"התכונה \"העברת הודעות באמצעות לוויין\" לא מוגדרת"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"‏כדי להעביר הודעות באמצעות לוויין, צריך להגדיר את Google Messages כאפליקציית ברירת המחדל להודעות"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"התכונה \"העברת הודעות באמצעות לוויין\" לא זמינה"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"כדי לבדוק אם התכונה \"העברת הודעות באמצעות לוויין\" זמינה במדינה או באזור שלך, צריך להפעיל את הגדרות המיקום"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"הגדרה חוזרת של \'ביטול הנעילה בטביעת אצבע\'"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"כבר לא ניתן לזהות את <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"כבר לא ניתן לזהות את <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ואת <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2f389293d843..339144611afd 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"付近の Ultra Wideband デバイス間の相対位置の特定をアプリに許可します"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"付近の Wi-Fi デバイスとの通信"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"付近の Wi-Fi デバイスについて、情報の表示、接続、相対位置の確認をアプリに許可します"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"優先される NFC お支払いサービスの情報"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"登録されている支援やルートの目的地など、優先される NFC お支払いサービスの情報を取得することをアプリに許可します。"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFCの管理"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"予定モード"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"おやすみモード"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"サイレント モード(<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> によって管理されています"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ON"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"OFF"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"すべてのカレンダー"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> により一部の音はミュートに設定"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"デバイスで内部的な問題が発生しました。データが初期化されるまで不安定になる可能性があります。"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"モバイル ネットワークや Wi-Fi ネットワークがなくてもメッセージを送受信できます"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"メッセージ アプリを開く"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"仕組み"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"[ネットワークを自動的に選択] を ON にする"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"設定で [ネットワークを自動的に選択] を ON にすると、衛星と通信可能なネットワークをスマートフォンが検出できるようになります"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ON にする"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"戻る"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"衛星 SOS を利用できるようになりました"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"モバイル ネットワークや Wi-Fi ネットワークがなくても、緊急サービスにメッセージを送信できます。Google メッセージがデフォルトのメッセージ アプリに設定されている必要があります。"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"衛星 SOS に対応していません"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"このデバイスは衛星 SOS に対応していません"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"衛星 SOS が設定されていません"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"インターネットに接続していることを確認してから、もう一度設定してみてください"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"衛星 SOS を利用できません"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"この国または地域では衛星 SOS を利用できません"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"衛星 SOS が設定されていません"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"衛星経由でメッセージを送受信するには、Google メッセージをデフォルトのメッセージ アプリに設定してください"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"衛星 SOS を利用できません"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"この国または地域で衛星 SOS を利用できるかどうかを確認するには、位置情報の設定を ON にしてください"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"衛星通信メッセージを利用できます"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"モバイル ネットワークや Wi-Fi ネットワークがなくても、衛星経由でメッセージを送受信できます。Google メッセージがデフォルトのメッセージ アプリに設定されている必要があります。"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"衛星通信メッセージに対応していません"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"このデバイスは衛星通信メッセージに対応していません"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"衛星通信メッセージが設定されていません"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"インターネットに接続していることを確認してから、もう一度設定してみてください"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"衛星通信メッセージを利用できません"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"この国または地域では衛星通信メッセージを利用できません"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"衛星通信メッセージが設定されていません"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"衛星経由でメッセージを送受信するには、Google メッセージをデフォルトのメッセージ アプリに設定してください"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"衛星通信メッセージを利用できません"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"この国または地域で衛星通信メッセージを利用できるかどうかを確認するには、位置情報の設定を ON にしてください"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"指紋認証をもう一度設定してください"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>を認識できなくなりました。"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>と<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>を認識できなくなりました。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 2d9aecd908ca..58f78901ef5e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ნებას რთავს აპს, დაადგინოს შედარებითი პოზიცია ახლომახლო ულტრაფართო სიხშირის მოწყობილობების შესახებ"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ინტერაქცია ახლომახლო Wi-Fi მოწყობილობებთან"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"საშუალებას აძლევს აპს, განაცხადოს ახლომახლო Wi-Fi მოწყობილობების შესახებ, დაუკავშირდეს მათ და განსაზღვროს მათი შედარებითი პოზიცია"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"უპირატესი NFC გადახდის სერვისის ინფორმაცია"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"საშუალებას აძლევს აპს, მიიღოს უპირატესი NFC გადახდის სერვისის ინფორმაცია, მაგალითად, რეგისტრირებული დახმარება და დანიშნულება."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ახლო მოქმედების რადიოკავშირი (NFC) მართვა"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"შაბათ-კვირა"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"მოვლენა"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ძილისას"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"არ შემაწუხოთ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"მართავს <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ჩართული"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"გამორთული"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ნებისმიერი კალენდარი"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ხმას ადუმებს"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"შეტყობინებების გაგზავნა და მიღება მობილური ან Wi-Fi ქსელის გარეშე"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages-ის გახსნა"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"მუშაობის პრინციპი"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"„ქსელის ავტომატურად არჩევის“ ჩართვა"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ჩართეთ „ქსელის ავტომატურად არჩევა“ პარამეტრებში, რათა თქვენმა ტელეფონმა სატელიტთან თავსებადი ქსელის პოვნა შეძლოს"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ჩართვა"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"უკან დაბრუნება"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"სატელიტური SOS ახლა ხელმისაწვდომია"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"შეგიძლიათ, შეტყობინება გაუგზავნოთ გადაუდებელი დახმარების სამსახურებს, თუ მობილური ან Wi-Fi ქსელი არ არის ხელმისაწვდომი. საჭიროა, რომ Google Messages იყოს თქვენი ნაგულისხმევი შეტყობინებების აპი."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"სატელიტური SOS არ არის მხარდაჭერილი"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"სატელიტური SOS არ არის მხარდაჭერილი ამ მოწყობილობაზე"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"სატელიტური SOS არ არის დაყენებული"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"დარწმუნდით, რომ დაკავშირებული ხართ ინტერნეტთან და ცადეთ ხელახლა დაყენება"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"სატელიტური SOS არ არის ხელმისაწვდომი"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"სატელიტური SOS ამ ქვეყანასა თუ რეგიონში არ არის ხელმისაწვდომი"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"სატელიტური SOS არ არის დაყენებული"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"შეტყობინებების სატელიტური მიმოცვლისთვის თქვენს ნაგულისხმევ შეტყობინებების აპად დააყენეთ Google Messages"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"სატელიტური SOS არ არის ხელმისაწვდომი"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ამ ქვეყანაში ან რეგიონში სატელიტური SOS-ის ხელმისაწვდომობის შესამოწმებლად ჩართეთ მდებარეობის პარამეტრები"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"შეტყობინებების სატელიტური მიმოცვლა ხელმისაწვდომია"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"შეტყობინების გაგზავნა სატელიტის საშუალებით შეგიძლიათ, თუ მობილურზე ან Wi-Fi ქსელზე წვდომა არ გაქვთ. საჭიროა, რომ Google Messages იყოს თქვენი ნაგულისხმევი შეტყობინებების აპი."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"შეტყობინებების სატელიტური მიმოცვლა მხარდაჭერილი არ არის"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ამ მოწყობილობაზე შეტყობინებების სატელიტური მიმოცვლა არ არის მხარდაჭერილი"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"შეტყობინებების სატელიტური მიმოცვლა დაყენებული არ არის"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"დარწმუნდით, რომ დაკავშირებული ხართ ინტერნეტთან და ცადეთ ხელახლა დაყენება"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"შეტყობინებების სატელიტური მიმოცვლა მიუწვდომელია"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ამ ქვეყანაში ან რეგიონში შეტყობინებების სატელიტური მიმოცვლა მიუწვდომელია"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"შეტყობინებების სატელიტური მიმოცვლა დაყენებული არ არის"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"შეტყობინებების სატელიტური მიმოცვლისთვის თქვენს ნაგულისხმევ შეტყობინებების აპად დააყენეთ Google Messages"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"შეტყობინებების სატელიტური მიმოცვლა მიუწვდომელია"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ამ ქვეყანაში ან რეგიონში შეტყობინებების სატელიტური მიმოცვლის ხელმისაწვდომობის შესამოწმებლად ჩართეთ მდებარეობის პარამეტრები"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ანაბეჭდით განბლოკვის ხელახლა დაყენება"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>-ის ამოცნობა ვეღარ ხერხდება."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>-ისა და <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>-ის ამოცნობა ვეღარ ხერხდება."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6986a7b914a5..737f950567a1 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Қолданбаға маңайдағы кең жолақты құрылғылардың бір-біріне қатысты орнын анықтауға мүмкіндік береді."</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"маңайдағы Wi-Fi құрылғыларымен байланысу"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Қолданба маңайдағы Wi‑Fi құрылғыларына жарнама беріп, оларға қосылып, шамамен орналасқан жерін анықтай алады."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Таңдаулы NFC төлеу қызметі туралы ақпарат"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Қолданба тіркелген көмектер және баратын жер маршруты сияқты таңдаулы NFC төлеу қызметі туралы ақпаратты ала алатын болады."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFC функциясын басқару"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Демалыс күндері"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Іс-шара"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ұйқы режимі"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Мазаламау (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> басқарады."</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Қосулы"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Өшірулі"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кез келген күнтізбе"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіруде"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Хабарландыруларды мобильдік желіге немесе Wi-Fi желісіне қосылмай жіберіңіз және алыңыз."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"Желіні автоматты түрде таңдау\" опциясын қосу"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Телефоныңыз жерсерікпен жұмыс істейтін желіні таба алуы үшін, \"Параметрлер\" бөлімінен \"Желіні автоматты түрде таңдау\" опциясын қосыңыз."</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Қосу"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Артқа"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Саусақ ізімен ашу функциясын қайта реттеу"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> бұдан былай танылмайды."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> және <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> бұдан былай танылмайды."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index b2bb78b611d5..0ecad8f60fe1 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"អនុញ្ញាតឱ្យ​កម្មវិធី​កំណត់ចម្ងាយ​ពាក់ព័ន្ធ​រវាងឧបករណ៍ Ultra-Wideband ដែលនៅជិត"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ធ្វើអន្តរកម្ម​ជាមួយឧបករណ៍ Wi‑Fi ដែលនៅជិត"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"អនុញ្ញាតឱ្យ​កម្មវិធី​ផ្សាយពាណិជ្ជកម្ម ភ្ជាប់ និងកំណត់ទីតាំង​ពាក់ព័ន្ធរបស់​ឧបករណ៍ Wi‑Fi ដែលនៅជិត"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ព័ត៌មានអំពី​សេវាបង់ប្រាក់តាម NFC ជាអាទិភាព"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"អនុញ្ញាតឱ្យ​កម្មវិធី​ទទួលបាន​ព័ត៌មានអំពី​សេវាបង់ប្រាក់តាម nfc ជាអាទិភាព​ដូចជា គោលដៅផ្លូវ និង​ព័ត៌មាន​កំណត់អត្តសញ្ញាណ​កម្មវិធី ដែលបានចុះឈ្មោះ​ជាដើម។"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ពិនិត្យ​ការ​ទាក់ទង​នៅ​ក្បែរ (NFC)"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ចុងសប្ដាហ៍"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ព្រឹត្តិការណ៍"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"កំពុងដេក"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"កុំ​រំខាន (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"គ្រប់គ្រងដោយ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"បើក"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"បិទ"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ដល់ <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ប្រតិទិនណាមួយ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> កំពុង​បិទសំឡេង​មួយចំនួន"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ហើយវាអ្នកមិនមានស្ថេរភាព រហូតទាល់តែអ្នកកំណត់ដូចដើមវិញទាំងស្រុង។"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ផ្ញើ និងទទួលសារដោយគ្មានបណ្ដាញ Wi-Fi ឬបណ្ដាញទូរសព្ទចល័ត"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"បើក​កម្មវិធី Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"របៀបដែលវាដំណើរការ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"បើក \"ជ្រើសរើស​បណ្ដាញ​ដោយស្វ័យប្រវត្តិ\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"បើក \"ជ្រើសរើស​បណ្ដាញ​ដោយស្វ័យប្រវត្តិ\" នៅក្នុង​ការកំណត់​ ដើម្បីឱ្យ​ទូរសព្ទ​របស់អ្នកអាចស្វែងរកបណ្ដាញ ដែល​ដំណើរការតាមរយៈផ្កាយរណប"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"បើក"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ថយ​ក្រោយ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ឥឡូវនេះ អាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបបានហើយ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"អ្នកអាចផ្ញើសារទៅសេវាសង្គ្រោះបន្ទាន់ ប្រសិនបើមិនមានបណ្ដាញឧបករណ៍​ចល័ត ឬ Wi-Fi ទេ។ Google Messages ត្រូវតែជាកម្មវិធី​ផ្ញើសារលំនាំដើមរបស់អ្នក។"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបបានទេ"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបនៅលើឧបករណ៍នេះបានទេ"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ការប្រកាសអាសន្នតាមផ្កាយរណបមិនត្រូវបានរៀបចំទេ"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"សូមប្រាកដថា អ្នកបានភ្ជាប់អ៊ីនធឺណិត រួចសាកល្បងរៀបចំម្ដងទៀត"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបបានទេ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណនៅក្នុងប្រទេស ឬតំបន់នេះបានទេ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"មិនបានរៀបចំការប្រកាសអាសន្នតាមផ្កាយរណបទេ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"ដើម្បីផ្ញើសារតាមផ្កាយរណប សូមកំណត់ Google Messages ជាកម្មវិធីផ្ញើសារលំនាំដើមរបស់អ្នក"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបបានទេ"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ដើម្បីពិនិត្យមើលថាតើអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបនៅក្នុងប្រទេស ឬតំបន់នេះបានឬអត់ សូមបើកការ​កំណត់​ទីតាំង​"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"អាចផ្ញើ​សារតាមផ្កាយរណបបាន"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"អ្នកអាចផ្ញើសារតាមផ្កាយរណប ប្រសិនបើមិនមានបណ្ដាញឧបករណ៍​ចល័ត ឬ Wi-Fi ទេ។ Google Messages ត្រូវតែជាកម្មវិធី​ផ្ញើសារលំនាំដើមរបស់អ្នក។"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"មិនអាចផ្ញើ​សារតាមផ្កាយរណបបានទេ"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"មិនអាចផ្ញើ​សារតាមផ្កាយរណបនៅលើឧបករណ៍នេះបានទេ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"មិនបានរៀបចំការ​ផ្ញើ​សារតាមផ្កាយរណបទេ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"សូមប្រាកដថា អ្នកបានភ្ជាប់អ៊ីនធឺណិត រួចសាកល្បងរៀបចំម្ដងទៀត"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"មិនអាចផ្ញើ​សារតាមផ្កាយរណបបានទេ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"មិនអាចផ្ញើ​សារតាមផ្កាយរណបនៅក្នុងប្រទេស ឬតំបន់នេះបានទេ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"មិនបានរៀបចំការ​ផ្ញើ​សារតាមផ្កាយរណបទេ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"ដើម្បីផ្ញើសារតាមផ្កាយរណប សូមកំណត់ Google Messages ជាកម្មវិធីផ្ញើសារលំនាំដើមរបស់អ្នក"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"មិនអាចផ្ញើ​សារតាមផ្កាយរណបបានទេ"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ដើម្បីពិនិត្យមើលថាតើអាចផ្ញើ​សារតាមផ្កាយរណបនៅក្នុងប្រទេស ឬតំបន់នេះបានឬអត់ សូមបើកការ​កំណត់​ទីតាំង​"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"រៀបចំការដោះសោ​ដោយស្កេន​ស្នាមម្រាមដៃម្ដងទៀត"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"លែងអាចសម្គាល់ <xliff:g id="FINGERPRINT">%s</xliff:g> បានទៀតហើយ។"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"លែងអាចសម្គាល់ <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> និង <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> បានទៀតហើយ។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 8332cfcbd2ec..c64f6ec1e7a7 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ಸಮೀಪದಲ್ಲಿರುವ ಅಲ್ಟ್ರಾ-ವೈಡ್‌ಬ್ಯಾಂಡ್ ಸಾಧನಗಳ ನಡುವೆ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಿ"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ಹತ್ತಿರದ ವೈ -ಫೈ ಸಾಧನಗಳ ಜೊತೆಗೆ ಸಂವಹನ ನಡೆಸಿ"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ಹತ್ತಿರದ ವೈ -ಫೈ ಸಾಧನಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ಸೂಚಿಸಲು, ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವಾ ಮಾಹಿತಿ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ನೋಂದಾಯಿತ ಆ್ಯಪ್ ಗುರುತಿಸುವಿಕೆಗಳು ಮತ್ತು ಮಾರ್ಗ ಗಮ್ಯಸ್ಥಾನಗಳಂತಹ ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವೆಗಳ ಬಗ್ಗೆ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ಸಮೀಪ ಕ್ಷೇತ್ರ ಸಂವಹನವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ವಾರಾಂತ್ಯ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ಈವೆಂಟ್"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ನಿದ್ರೆಯ ಸಮಯ"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ಆನ್ ಆಗಿದೆ"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ಆಫ್ ಆಗಿದೆ"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ನಿಂದ <xliff:g id="END">%2$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ಯಾವುದೇ ಕ್ಯಾಲೆಂಡರ್"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದ್ದಾರೆ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ ಹಾಗೂ ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾವನ್ನು ರೀಸೆಟ್ ಮಾಡುವವರೆಗೂ ಅದು ಅಸ್ಥಿರವಾಗಬಹುದು."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲದೆಯೇ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಿ ಮತ್ತು ಸ್ವೀಕರಿಸಿ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ\" ಅನ್ನು ಆನ್ ಮಾಡಿ"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ \"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನೆಟ್‌ವರ್ಕ್ ಆಯ್ಕೆಮಾಡಿ\" ಅನ್ನು ಆನ್ ಮಾಡಿ ಇದರಿಂದ ನಿಮ್ಮ ಫೋನ್ ಸ್ಯಾಟಲೈಟ್ ಜೊತೆಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುವ ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ಹುಡುಕಬಹುದು"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ಆನ್ ಮಾಡಿ"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ಹಿಂದಿರುಗಿ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ಸ್ಯಾಟಲೈಟ್‌ SOS ಈಗ ಲಭ್ಯವಿದೆ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲದಿದ್ದರೆ ನೀವು ತುರ್ತು ಸೇವೆಗಳಿಗೆ ಸಂದೇಶ ಕಳುಹಿಸಬಹುದು. Google Messages ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಆ್ಯಪ್‌ ಆಗಿರಬೇಕು."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ಸ್ಯಾಟಲೈಟ್‌ SOS ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ಈ ಸಾಧನದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್ SOS ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ಸ್ಯಾಟಲೈಟ್ SOS ಅನ್ನು ಸೆಟಪ್‌ ಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ನೀವು ಇಂಟರ್ನೆಟ್‌ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಿರಿ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಮತ್ತು ಪುನಃ ಸೆಟಪ್‌ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ಈ ದೇಶ ಅಥವಾ ಪ್ರದೇಶದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್‌ SOS ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ಸ್ಯಾಟಲೈಟ್‌ SOS ಸೆಟಪ್‌ ಆಗಿಲ್ಲ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"ಸ್ಯಾಟಲೈಟ್‌ ಮೂಲಕ ಸಂದೇಶ ಕಳುಹಿಸಲು, Google Messages ಅನ್ನು ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಮೆಸೇಜಿಂಗ್ ಆ್ಯಪ್‌ನಂತೆ ಸೆಟ್‌ ಮಾಡಿ"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ಈ ದೇಶ ಅಥವಾ ಪ್ರದೇಶದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಲು, ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಆನ್‌ ಮಾಡಿ"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಲಭ್ಯವಿದೆ"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ಮೊಬೈಲ್‌ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲದಿದ್ದರೆ ನೀವು ಸ್ಯಾಟಲೈಟ್ ಮೂಲಕ ಸಂದೇಶ ಕಳುಹಿಸಬಹುದು. Google Messages ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಆ್ಯಪ್‌ ಆಗಿರಬೇಕು."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಗೆ ಬೆಂಬಲವಿಲ್ಲ"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ಈ ಸಾಧನದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್‌ ಸೆಟಪ್‌ ಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ನೀವು ಇಂಟರ್ನೆಟ್‌ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಿರಿ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಮತ್ತು ಪುನಃ ಸೆಟಪ್‌ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಕಳುಹಿಸುವಿಕೆ ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ಈ ದೇಶ ಅಥವಾ ಪ್ರದೇಶದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್‌ ಸೆಟಪ್‌ ಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"ಸ್ಯಾಟಲೈಟ್‌ ಮೂಲಕ ಸಂದೇಶ ಕಳುಹಿಸಲು, Google Messages ಅನ್ನು ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಮೆಸೇಜಿಂಗ್ ಆ್ಯಪ್‌ನಂತೆ ಸೆಟ್‌ ಮಾಡಿ"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಕಳುಹಿಸುವಿಕೆ ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ಈ ದೇಶ ಅಥವಾ ಪ್ರದೇಶದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್‌ ಮೆಸೇಜಿಂಗ್‌ ಲಭ್ಯವಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಲು, ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಆನ್‌ ಮಾಡಿ"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ಅನ್ನು ಇನ್ನು ಮುಂದೆ ಗುರುತಿಸಲಾಗುವುದಿಲ್ಲ."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ಅನ್ನು ಇನ್ನು ಮುಂದೆ ಗುರುತಿಸಲಾಗುವುದಿಲ್ಲ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 393c956c6fc5..d63d43e4cddd 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"앱이 근처의 초광대역 기기 간 상대적 위치를 파악하도록 허용"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"근처 Wi‑Fi 기기와 상호작용"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"앱이 광역 신호를 보내 근처에 있는 Wi‑Fi 기기의 상대적인 위치를 확인하고 연결할 수 있도록 허용합니다."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"기본 NFC 결제 서비스 정보"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"앱이 등록된 AID와 경로 목적지 같은 기본 NFC 결제 서비스 정보를 확인하도록 허용합니다."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFC(Near Field Communication) 제어"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"주말"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"캘린더 일정"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"수면 시간"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"방해 금지 모드(<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"관리자: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"사용"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"사용 중지"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>~<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"모든 캘린더"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"모바일 또는 Wi-Fi 네트워크 없이 메시지 주고받기"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"메시지 열기"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"작동 방식"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\'네트워크 자동 선택\' 사용 설정"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"휴대전화에서 위성과 연결되는 네트워크를 찾을 수 있도록 \'설정\'에서 \'네트워크 자동 선택\'을 사용 설정하세요"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"사용"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"뒤로"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"이제 위성 긴급 SOS를 사용할 수 있음"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"모바일 또는 Wi-Fi 네트워크가 없는 경우 응급 서비스로 메시지를 보낼 수 있습니다. Google 메시지가 기본 메시지 앱으로 설정되어 있어야 합니다."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"위성 긴급 SOS가 지원되지 않음"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"이 기기에서는 위성 긴급 SOS가 지원되지 않습니다."</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"위성 긴급 SOS가 설정되지 않음"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"인터넷에 연결되어 있는지 확인한 후 다시 설정해 보세요."</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"위성 긴급 SOS를 사용할 수 없음"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"이 국가 또는 지역에서는 위성 긴급 SOS를 사용할 수 없습니다."</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"위성 긴급 SOS가 설정되지 않음"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"위성으로 메시지를 보내려면 Google 메시지를 기본 메시지 앱으로 설정하세요."</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"위성 긴급 SOS를 사용할 수 없음"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"이 국가 또는 지역에서 위성 긴급 SOS를 사용할 수 있는지 확인하려면 위치 설정을 사용 설정하세요."</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"위성 메시지 사용 가능"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"모바일 또는 Wi-Fi 네트워크가 없는 경우 위성으로 메시지를 보낼 수 있습니다. Google 메시지가 기본 메시지 앱으로 설정되어 있어야 합니다."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"위성 메시지가 지원되지 않음"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"이 기기에서는 위성 메시지가 지원되지 않습니다."</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"위성 메시지가 설정되지 않음"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"인터넷에 연결되어 있는지 확인한 후 다시 설정해 보세요."</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"위성 메시지를 사용할 수 없음"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"이 국가 또는 지역에서는 위성 메시지를 사용할 수 없습니다."</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"위성 메시지가 설정되지 않음"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"위성으로 메시지를 보내려면 Google 메시지를 기본 메시지 앱으로 설정하세요."</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"위성 메시지를 사용할 수 없음"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"이 국가 또는 지역에서 위성 메시지를 사용할 수 있는지 확인하려면 위치 설정을 사용 설정하세요."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"지문 잠금 해제 다시 설정"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> 지문을 더 이상 인식할 수 없습니다."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> 및 <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> 지문을 더 이상 인식할 수 없습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index e44538ba3c9d..e09036a1b679 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Колдонмо кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктай алат"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Жакын жердеги Wi‑Fi түзмөктөрүнө байланышуу"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Колдонмо жакын жердеги Wi-Fi түзмөктөргө туташып, алардын жайгашкан жерин аныктап, ар кандай нерселерди өткөрө алат."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Тандалган NFC төлөм кызматы жөнүндө маалымат"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Колдонмого катталган жардам же көздөлгөн жерге маршрут сыяктуу тандалган nfc төлөм кызматы жөнүндө маалыматты алууга уруксат берүү."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication көзөмөлү"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Дем алыш"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Иш-чара"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Уйку режими"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Тынчымды алба (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> башкарат"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Күйүк"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Өчүк"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Бардык жылнаамалар"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> айрым үндөрдү өчүрүүдө"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Түзмөгүңүздө ички көйгөй бар жана ал баштапкы абалга кайтарылмайынча туруктуу иштебей коюшу мүмкүн."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Мобилдик же Wi-Fi тармагына туташпай эле билдирүүлөрдү жөнөтүп, алыңыз"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Жазышуулар колдонмосун ачуу"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ал кантип иштейт"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"Тармакты автоматтык түрдө тандоо\" параметрин күйгүзүңүз"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Телефонуңуз спутник менен иштеген тармакты табышы үчүн, параметрлерден \"Тармакты автоматтык түрдө тандоону\" күйгүзүңүз"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Күйгүзүү"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Артка кайтуу"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Манжа изи менен ачуу функциясын кайра тууралаңыз"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> мындан ары таанылбайт."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> жана <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> мындан ары таанылбайт."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 15138bb10a36..a9ebcd765502 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ອະນຸຍາດໃຫ້ແອັບກຳນົດຕຳແໜ່ງທີ່ສຳພັນກັນລະຫວ່າງອຸປະກອນ Ultra-Wideband ທີ່ຢູ່ໃກ້ຄຽງ"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ໂຕ້ຕອບກັບອຸປະກອນ Wi‑Fi ທີ່ຢູ່ໃກ້ຄຽງ"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ອະນຸຍາດໃຫ້ແອັບໂຄສະນາ, ເຊື່ອມຕໍ່ ແລະ ກຳນົດຕຳແໜ່ງສຳພັນຂອງອຸປະກອນ Wi-Fi ທີ່ຢູ່ໃກ້ຄຽງໄດ້"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ຂໍ້ມູນບໍລິການການຈ່າຍເງິນ NFC ທີ່ຕ້ອງການ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ອະນຸຍາດໃຫ້ແອັບຮັບຂໍ້ມູນບໍລິການການຈ່າຍເງິນ NFC ທີ່ຕ້ອງການໄດ້ ເຊັ່ນ: ການຊ່ວຍເຫຼືອແບບລົງທະບຽນ ແລະ ປາຍທາງເສັ້ນທາງ."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ຄວບຄຸມ Near Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ທ້າຍອາທິດ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ການນັດໝາຍ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ການນອນ"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ຫ້າມລົບກວນ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ຈັດການໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ເປີດຢູ່"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ປິດຢູ່"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ຫາ <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ປະ​ຕິ​ທິນ​ໃດ​ກໍໄດ້"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ປິດສຽງບາງຢ່າງໄວ້"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ມີ​ບັນ​ຫາ​ພາຍ​ໃນ​ກັບ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ, ແລະ​ມັນ​ອາດ​ຈະ​ບໍ່​ສະ​ຖຽນ​ຈົນ​ກວ່າ​ທ່ານ​ຕັ້ງ​ເປັນ​ຂໍ້​ມູນ​ໂຮງ​ງານ​ຄືນ​ແລ້ວ."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ຮັບ ແລະ ສົ່ງຂໍ້ຄວາມໂດຍບໍ່ຕ້ອງໃຊ້ເຄືອຂ່າຍໂທລະສັບມືຖື ຫຼື Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"ເປີດ Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ມັນເຮັດວຽກແນວໃດ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"ເປີດໃຊ້ \"ເລືອກເຄືອຂ່າຍອັດຕະໂນມັດ\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ເປີດໃຊ້ \"ເລືອກເຄືອຂ່າຍອັດຕະໂນມັດ\" ໃນການຕັ້ງຄ່າເພື່ອໃຫ້ໂທລະສັບຂອງທ່ານສາມາດຊອກຫາເຄືອຂ່າຍທີ່ໃຊ້ໄດ້ກັບດາວທຽມ"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ເປີດໃຊ້"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ກັບຄືນ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ຕອນນີ້ SOS ດາວທຽມພ້ອມໃຫ້ບໍລິການແລ້ວ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"ທ່ານສາມາດສົ່ງຂໍ້ຄວາມໄປຫາບໍລິການສຸກເສີນໄດ້ໃນກໍລະນີທີ່ບໍ່ມີເຄືອຂ່າຍມືຖື ຫຼື Wi-Fi. ຕ້ອງຕັ້ງຄ່າ Google Messages ເປັນແອັບຮັບສົ່ງຂໍ້ຄວາມເລີ່ມຕົ້ນຂອງທ່ານ."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ບໍ່ຮອງຮັບ SOS ດາວທຽມ"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບ SOS ດາວທຽມ"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ບໍ່ໄດ້ຕັ້ງຄ່າ SOS ດາວທຽມ"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ກະລຸນາກວດສອບໃຫ້ໝັ້ນໃຈວ່າທ່ານໄດ້ເຊື່ອມຕໍ່ອິນເຕີເນັດແລ້ວ ແລະ ລອງຕັ້ງຄ່າອີກຄັ້ງ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS ດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS ດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການໃນປະເທດ ຫຼື ພາກພື້ນນີ້"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ບໍ່ໄດ້ຕັ້ງຄ່າ SOS ດາວທຽມ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"ເພື່ອຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ, ໃຫ້ຕັ້ງ Google Messages ເປັນແອັບຮັບສົ່ງຂໍ້ຄວາມເລີ່ມຕົ້ນຂອງທ່ານ"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS ດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການ"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ເພື່ອກວດສອບວ່າ SOS ດາວທຽມພ້ອມໃຫ້ບໍລິການໃນປະເທດ ຫຼື ພາກພື້ນນີ້ຫຼືບໍ່, ໃຫ້ເປີດການຕັ້ງຄ່າສະຖານທີ່"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມພ້ອມໃຫ້ບໍລິການ"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ທ່ານສາມາດຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມໄດ້ໃນກໍລະນີທີ່ບໍ່ມີເຄືອຂ່າຍມືຖື ຫຼື Wi-Fi. ຕ້ອງຕັ້ງຄ່າ Google Messages ເປັນແອັບຮັບສົ່ງຂໍ້ຄວາມເລີ່ມຕົ້ນຂອງທ່ານ."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ບໍ່ຮອງຮັບການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ບໍ່ໄດ້ຕັ້ງຄ່າການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ກະລຸນາກວດສອບໃຫ້ໝັ້ນໃຈວ່າທ່ານໄດ້ເຊື່ອມຕໍ່ອິນເຕີເນັດແລ້ວ ແລະ ລອງຕັ້ງຄ່າອີກຄັ້ງ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"ການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການໃນປະເທດ ຫຼື ພາກພື້ນນີ້"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"ບໍ່ໄດ້ຕັ້ງຄ່າການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"ເພື່ອຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ, ໃຫ້ຕັ້ງ Google Messages ເປັນແອັບຮັບສົ່ງຂໍ້ຄວາມເລີ່ມຕົ້ນຂອງທ່ານ"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"ການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການ"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ເພື່ອກວດສອບວ່າການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມພ້ອມໃຫ້ບໍລິການໃນປະເທດ ຫຼື ພາກພື້ນນີ້ຫຼືບໍ່, ໃຫ້ເປີດການຕັ້ງຄ່າສະຖານທີ່"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍລາຍນິ້ວມືຄືນໃໝ່"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"ບໍ່ສາມາດຈຳແນກ <xliff:g id="FINGERPRINT">%s</xliff:g> ໄດ້ອີກຕໍ່ໄປ."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"ບໍ່ສາມາດຈຳແນກ <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ແລະ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ໄດ້ອີກຕໍ່ໄປ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 51aa8a19efef..21fddd010ca4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Leisti programai nustatyti apytikslę netoliese esančių itin plataus dažnio juostos įrenginių poziciją"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"sąveikauti su „Wi‑Fi“ įrenginiais netoliese"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Leidžiama programai reklamuoti, prisijungti ir nustatyti apytikslę netoliese esančių „Wi-Fi“ įrenginių poziciją"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Pageidaujama ARL mokėjimo paslaugos informacija"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Programai leidžiama gauti pageidaujamą ARL mokamos paslaugos informaciją, pvz., užregistruotą pagalbą ir maršrutų tikslus."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"valdyti artimo lauko perdavimą (angl. „Near Field Communication“)"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Savaitgalį"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Įvykis"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Miegas"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Netrukdymo režimas („<xliff:g id="APP_NAME">%1$s</xliff:g>“)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Tvarko „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Įjungti"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Išjungti"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bet kuris kalendorius"</string>
<string name="muted_by" msgid="91464083490094950">"„<xliff:g id="THIRD_PARTY">%1$s</xliff:g>“ nutildo kai kuriuos garsus"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Iškilo vidinė su jūsų įrenginiu susijusi problema, todėl įrenginys gali veikti nestabiliai, kol neatkursite gamyklinių duomenų."</string>
@@ -2428,15 +2432,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Siųskite ir gaukite pranešimus be mobiliojo ryšio ar „Wi-Fi“ tinklo"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Atidaryti programą „Messages“"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kaip tai veikia"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Parinkties „Automatiškai pasirinkti tinklą“ įjungimas"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Įjunkite „Automatiškai pasirinkti tinklą“ nustatymuose, kad telefonas galėtų rasti su palydovu suderinamą tinklą"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Įjungti"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Grįžti"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Prisijungimas prie palydovo kritiniu atveju nepasiekiamas"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Galite siųsti pranešimus pagalbos tarnyboms, kai nėra mobiliojo ryšio ar „Wi-Fi“ tinklo. „Google Messages“ turi būti jūsų numatytoji pranešimų programa."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Prisijungimo prie palydovo kritiniu atveju funkcija nepalaikoma"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Šiame įrenginyje nepalaikoma prisijungimo prie palydovo kritiniu atveju funkcija"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Prisijungimo prie palydovo kritiniu atveju funkcija nenustatyta"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Įsitikinkite, kad esate prisijungę prie interneto ir bandykite nustatyti dar kartą"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Prisijungimo prie palydovo kritiniu atveju funkcija nepasiekiama"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Prisijungimo prie palydovo kritiniu atveju funkcija nepasiekiama šioje šalyje arba regione"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Prisijungimo prie palydovo kritiniu atveju funkcija nenustatyta"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Jei norite siųsti pranešimus per palydovą, nustatykite „Google Messages“ kaip numatytąją pranešimų programą"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Prisijungimo prie palydovo kritiniu atveju funkcija nepasiekiama"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Jei norite patikrinti, ar prisijungimo prie palydovo kritiniu atveju funkcija pasiekiama šioje šalyje ar regione, įjunkite vietovės nustatymus"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Pasiekiama susirašinėjimo palydoviniais pranešimais paslauga"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Galite siųsti pranešimus palydoviniu ryšiu, kai nėra mobiliojo ryšio ar „Wi-Fi“ tinklo. „Google Messages“ turi būti jūsų numatytoji pranešimų programa."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Susirašinėjimo palydoviniais pranešimais paslauga nepalaikoma"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Šiame įrenginyje nepalaikoma susirašinėjimo palydoviniais pranešimais paslauga"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Susirašinėjimo palydoviniais pranešimais paslauga nenustatyta"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Įsitikinkite, kad esate prisijungę prie interneto ir bandykite nustatyti dar kartą"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Susirašinėjimo palydoviniais pranešimais paslauga nepasiekiama"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Susirašinėjimo palydoviniais pranešimais paslauga nepasiekiama šioje šalyje ar regione"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Susirašinėjimo palydoviniais pranešimais paslauga nenustatyta"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Jei norite siųsti pranešimus per palydovą, nustatykite „Google Messages“ kaip numatytąją pranešimų programą"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Susirašinėjimo palydoviniais pranešimais paslauga nepasiekiama"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Jei norite patikrinti, ar susirašinėjimo palydoviniais pranešimais paslauga pasiekiama šioje šalyje ar regione, įjunkite vietovės nustatymus"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Atrakinimo piršto atspaudu nustatymas dar kartą"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nebegalima atpažinti."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ir <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nebegalima atpažinti."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 43de9f54e560..15ee04a5f1d7 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Atļaut lietotnei noteikt relatīvo atrašanās vietu starp tuvumā esošām ultraplatjoslas ierīcēm"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mijiedarbība ar tuvumā esošām Wi‑Fi ierīcēm"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Atļauj lietotnei nodot datus tuvumā esošām Wi‑Fi ierīcē, izveidot savienojumu ar tām un noteikt to relatīvo pozīciju."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informācija par vēlamo NFC maksājumu pakalpojumu"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ļauj lietotnei iegūt informāciju par vēlamo NFC maksājumu pakalpojumu, piemēram, par reģistrētajiem lietojumprogrammu ID un maršruta galamērķi."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrolē tuvlauka saziņu"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nedēļas nogalē"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Pasākums"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Gulēšana"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Netraucēt (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Pārvalda <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ieslēgta"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izslēgta"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"no <xliff:g id="START">%1$s</xliff:g> līdz <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Jebkurš kalendārs"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izslēdz noteiktas skaņas"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Jūsu ierīcē ir radusies iekšēja problēma, un ierīce var darboties nestabili. Lai to labotu, veiciet rūpnīcas datu atiestatīšanu."</string>
@@ -2427,15 +2431,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Sūtiet un saņemiet ziņojumus bez mobilā vai Wi‑Fi tīkla."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Atvērt lietotni Ziņojumi"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Darbības principi"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Ieslēdziet iestatījumu “Automātiski atlasīt tīklu”"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Iestatījumos ieslēdziet iespēju “Automātiski atlasīt tīklu”, lai tālrunis varētu atrast tīklu, kas ir saderīgs ar satelītu."</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ieslēgt"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Atpakaļ"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vēlreiz iestatiet autorizāciju ar pirksta nospiedumu"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Pirksta nospiedumu (<xliff:g id="FINGERPRINT">%s</xliff:g>) vairs nevar atpazīt."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Pirkstu nospiedumus (<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> un <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>) vairs nevar atpazīt."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index ea380371f083..b5187726028b 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозволува апликацијата да ја одреди релативната положба помеѓу уредите со ултраширок појас во близина"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"да има интеракција со уредите со Wi‑Fi во близина"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозволува апликацијата да рекламира, да се поврзува и да ја одредува релативната положба на уреди со Wi‑Fi во близина"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информации за претпочитаната услуга за плаќање преку NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозволува апликацијата да добие информации за претпочитаната услуга за плаќање преку NFC, како регистрирани помагала и дестинација на маршрутата."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"контролирај комуникација на блиско поле"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Настан"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спиење"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Не вознемирувај (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Управувано од <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Вклучено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Исклучено"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> до <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Кој било календар"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> исклучи некои звуци"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Настана внатрешен проблем со уредот и може да биде нестабилен сè додека не ресетирате на фабричките податоци."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Испраќајте и примајте пораки без мобилна или Wi-Fi мрежа"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отворете ја Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Дознајте како функционира"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Вклучете „Избирај мрежа автоматски“"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Вклучете „Избирај мрежа автоматски“ во „Поставки“ за да може телефонот да најде мрежа што функционира со сателит"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Вклучи"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Врати се назад"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поставете „Отклучување со отпечаток“ повторно"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> веќе не може да се препознае."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> веќе не може да се препознаат."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ae6505dbf952..aabeb9b98646 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങളുമായി ഇടപഴകുക"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങൾ കാണിക്കാനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും ആപ്പിനെ അനുവദിക്കൂ"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"തിരഞ്ഞെടുത്ത NFC പേയ്‌മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"റൂട്ട് ലക്ഷ്യസ്ഥാനം, രജിസ്‌റ്റർ ചെയ്തിരിക്കുന്ന സഹായങ്ങൾ എന്നിവ പോലുള്ള, തിരഞ്ഞെടുത്ത NFC പേയ്‌മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ ലഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"സമീപ ഫീൽഡുമായുള്ള ആശയവിനിമയം നിയന്ത്രിക്കുക"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"വാരാന്ത്യം"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ഇവന്റ്"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ഉറക്കം"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ശല്യപ്പെടുത്തരുത് (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യുന്നത്"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ഓണാണ്"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ഓഫാണ്"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> മുതൽ <xliff:g id="END">%2$s</xliff:g> വരെ"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"എല്ലാ കലണ്ടറിലും"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ചില ശബ്‌ദങ്ങൾ മ്യൂട്ട് ചെയ്യുന്നു"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്‌നമുണ്ട്, ഫാക്‌ടറി വിവര പുനഃസജ്ജീകരണം ചെയ്യുന്നതുവരെ ഇതു അസ്ഥിരമായിരിക്കാനിടയുണ്ട്."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്‌വർക്ക് ഇല്ലാതെ സന്ദേശങ്ങൾ അയയ്ക്കുകയും സ്വീകരിക്കുകയും ചെയ്യുക"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages തുറക്കുക"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ഇത് പ്രവർത്തിക്കുന്നത് എങ്ങനെയാണ്"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"സ്വയമേവ നെറ്റ്‌വർക്ക് തിരഞ്ഞെടുക്കുക\" ഓണാക്കുക"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ക്രമീകരണങ്ങളിൽ \"സ്വയമേവ നെറ്റ്‌വർക്ക് തിരഞ്ഞെടുക്കുക\" ഓണാക്കുക, അതുവഴി നിങ്ങളുടെ ഫോണിന് ഉപഗ്രഹത്തിനൊപ്പം പ്രവർത്തിക്കുന്ന ഒരു നെറ്റ്‌വർക്ക് കണ്ടെത്താനാകും"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ഓണാക്കുക"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"മടങ്ങുക"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"സാറ്റലൈറ്റ് SOS ഇപ്പോൾ ലഭ്യമാണ്"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്‌വർക്ക് ലഭ്യമല്ലെങ്കിൽ നിങ്ങൾക്ക് അടിയന്തര സേവനങ്ങൾക്ക് സന്ദേശമയയ്ക്കാം. നിങ്ങളുടെ ഡിഫോൾട്ട് സന്ദേശമയയ്ക്കൽ ആപ്പ് Google Messages ആയിരിക്കണം."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"സാറ്റലൈറ്റ് SOS പിന്തുണയ്ക്കുന്നില്ല"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ഈ ഉപകരണത്തിൽ സാറ്റലൈറ്റ് SOS പിന്തുണയ്ക്കുന്നില്ല"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"സാറ്റലൈറ്റ് SOS സജ്ജീകരിച്ചിട്ടില്ല"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"നിങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്‌തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കിയ ശേഷം സജ്ജീകരിക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"സാറ്റലൈറ്റ് SOS ലഭ്യമല്ല"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ഈ രാജ്യത്ത് അല്ലെങ്കിൽ പ്രദേശത്ത് സാറ്റലൈറ്റ് SOS ലഭ്യമല്ല"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"സാറ്റലൈറ്റ് SOS സജ്ജീകരിച്ചിട്ടില്ല"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"സാറ്റലൈറ്റ് വഴി സന്ദേശമയയ്ക്കാൻ, നിങ്ങളുടെ ഡിഫോൾട്ട് സന്ദേശമയയ്ക്കൽ ആപ്പായി Google Messages സജ്ജീകരിക്കുക"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"സാറ്റലൈറ്റ് SOS ലഭ്യമല്ല"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ഈ രാജ്യത്ത് അല്ലെങ്കിൽ പ്രദേശത്ത് സാറ്റലൈറ്റ് SOS ലഭ്യമാണോയെന്ന് പരിശോധിക്കുന്നതിന്, ലൊക്കേഷൻ ക്രമീകരണം ഓണാക്കുക"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ ലഭ്യമാണ്"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്‌വർക്ക് ഇല്ലെങ്കിൽ നിങ്ങൾക്ക് സാറ്റലൈറ്റ് വഴി സന്ദേശമയയ്ക്കാം. നിങ്ങളുടെ ഡിഫോൾട്ട് സന്ദേശമയയ്ക്കൽ ആപ്പ് Google Messages ആയിരിക്കണം."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ പിന്തുണയ്ക്കുന്നില്ല"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ഈ ഉപകരണത്തിൽ സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ പിന്തുണയ്ക്കുന്നില്ല"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ സജ്ജീകരിച്ചിട്ടില്ല"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"നിങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്‌തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കിയ ശേഷം സജ്ജീകരിക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ ലഭ്യമല്ല"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ഈ രാജ്യത്ത് അല്ലെങ്കിൽ പ്രദേശത്ത് സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ ലഭ്യമല്ല"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ സജ്ജീകരിച്ചിട്ടില്ല"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"സാറ്റലൈറ്റ് വഴി സന്ദേശമയയ്ക്കാൻ, നിങ്ങളുടെ ഡിഫോൾട്ട് സന്ദേശമയയ്ക്കൽ ആപ്പായി Google Messages സജ്ജീകരിക്കുക"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ ലഭ്യമല്ല"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ഈ രാജ്യത്ത് അല്ലെങ്കിൽ പ്രദേശത്ത് സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ ലഭ്യമാണോയെന്ന് പരിശോധിക്കാൻ, ലൊക്കേഷൻ ക്രമീകരണം ഓണാക്കുക"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ഇനി തിരിച്ചറിയാനാകില്ല."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> എന്നിവ ഇനി തിരിച്ചറിയാനാകില്ല."</string>
@@ -2454,7 +2478,7 @@
<string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"സംഗീതം"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"കലണ്ടർ"</string>
- <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"കാൽക്കുലേറ്റർ"</string>
+ <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ആപ്പുകൾ"</string>
<string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റുകൾ ഇനി തിരിച്ചറിയാനാകില്ല. ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 3d2eb04b5356..c625d31dbd79 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Аппад ойролцоох ультра өргөн зурвасын төхөөрөмжүүдийн хоорондох холбоотой байрлалыг тодорхойлохыг зөвшөөрөх"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ойролцоох Wi-Fi төхөөрөмжүүдтэй харилцах"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Аппад ойролцоох Wi-Fi төхөөрөмжүүдтэй холбоотой байрлалыг мэдэгдэх, холбох, тодорхойлохыг зөвшөөрнө"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Сонгосон NFC төлбөрийн үйлчилгээний мэдээлэл"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Бүртгүүлсэн төхөөрөмж болон маршрутын хүрэх цэг зэрэг сонгосон nfc төлбөрийн үйлчилгээний мэдээллийг авахыг аппад зөвшөөрдөг."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ойролцоо талбарын холбоог удирдах"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Амралтын өдөр"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Үйл явдал"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Унтлагын цаг"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Бүү саад бол (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g>-с удирддаг"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Асаалттай"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Унтраалттай"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-с <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Дурын календарь"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> зарим дууны дууг хааж байна"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Таны төхөөрөмжид дотоод алдаа байна.Та төхөөрөмжөө үйлдвэрээс гарсан төлөвт шилжүүлэх хүртэл таны төхөөрөмж чинь тогтворгүй байж болох юм."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Хөдөлгөөнт холбооны эсвэл Wi-Fi сүлжээгүйгээр мессеж илгээх болон хүлээн авах"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Мессежийг нээх"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Энэ хэрхэн ажилладаг вэ?"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"Сүлжээг автоматаар сонгох\"-ыг асаах"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Тохиргоонд \"Сүлжээг автоматаар сонгох\"-ыг асааснаар таны утас хиймэл дагуултай ажилладаг сүлжээг олох боломжтой"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Асаах"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Буцах"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Хурууны хээгээр түгжээ тайлахыг дахин тохируулна уу"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>-г цаашид таних боломжгүй."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> болон <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>-г цаашид таних боломжгүй."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9243f47d9246..3240d5914056 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ॲपला जवळच्या अल्ट्रा-वाइडबँड डिव्हाइसदरम्यानचे संबंधित स्थान निर्धारित करण्याची अनुमती द्या"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"जवळपासच्या वाय-फाय डिव्हाइसशी संवाद साधा"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ॲपला जाहिरात करण्याची, कनेक्ट करण्याची आणि जवळपासच्या वाय-फाय डिव्हाइसचे संबंधित स्थान निर्धारित करण्याची परवानगी देते"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"प्राधान्यकृत NFC पेमेंट सेवा माहिती"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"नोंदणीकृत एड्स आणि मार्ग गंतव्यस्थान सारखी प्राधान्यकृत एनएफसी पेमेंट सेवेची माहिती मिळवण्यासाठी अ‍ॅपला अनुमती देते."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"फील्ड जवळील कम्युनिकेशन नियंत्रित करा"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"आठवड्याच्या शेवटी"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"इव्‍हेंट"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"झोपताना"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"व्यत्यय आणू नका (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> द्वारे व्यवस्थापित"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"सुरू आहे"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"बंद आहे"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ते <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कोणतेही कॅलेंडर"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्‍वनी म्‍यूट करत आहे"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"आपल्‍या डिव्‍हाइसमध्‍ये अंतर्गत समस्‍या आहे आणि तुमचा फॅक्‍टरी डेटा रीसेट होईपर्यंत ती अस्‍थिर असू शकते."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल किंवा वाय-फाय नेटवर्कशिवाय मेसेज पाठवणे आणि मिळवणे"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages उघडा"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ते कसे काम करते"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"नेटवर्क आपोआप निवडा\" सुरू करा"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"सेटिंग्ज मध्ये \"नेटवर्क आपोआप निवडा\" सुरू करा, जेणेकरून तुमचा फोन सॅटेलाइटसोबत काम करणारे नेटवर्क शोधू शकेल"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"सुरू करा"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"मागे जा"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"सॅटेलाइट SOS आता उपलब्ध आहे"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"कोणतेही मोबाइल किंवा वाय-फाय नेटवर्क उपलब्ध नसल्यास, तुम्ही आणीबाणी सेवांना मेसेज पाठवू शकता. Google Messages हे तुमचे डीफॉल्ट मेसेजिंग ॲप असणे आवश्यक आहे."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"सॅटेलाइट SOS ला सपोर्ट नाही"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"या डिव्हाइसवर सॅटेलाइट SOS ला सपोर्ट नाही"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"सॅटेलाइट SOS सेट केलेले नाही"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"तुम्ही इंटरनेटशी कनेक्ट केले असल्याची खात्री करा आणि पुन्हा सेटअप करून पहा"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"सॅटेलाइट SOS उपलब्ध नाही"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"या देशात किंवा प्रदेशात सॅटेलाइट SOS उपलब्ध नाही"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"सॅटेलाइट SOS सेट केलेले नाही"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"सॅटेलाइटद्वारे मेसेज करण्यासाठी, Google Messages ला तुमचे डीफॉल्ट मेसेजिंग ॲप म्हणून सेट करा"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"सॅटेलाइट SOS उपलब्ध नाही"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"या देशात किंवा प्रदेशात सॅटेलाइट SOS उपलब्ध आहे का हे तपासण्यासाठी, स्थान सेटिंग्ज सुरू करा"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"सॅटेलाइट मेसेजिंग उपलब्ध आहे"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"मोबाईल किंवा वाय-फाय नेटवर्क नसल्यास, तुम्ही सॅटेलाइटद्वारे मेसेज पाठवू शकता. Google Messages हे तुमचे डीफॉल्ट मेसेजिंग ॲप असणे आवश्यक आहे."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"सॅटेलाइट मेसेजिंगला सपोर्ट नाही"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"या डिव्हाइसवर सॅटेलाइट मेसेजिंगला सपोर्ट नाही"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"सॅटेलाइट मेसेजिंग सेट केलेले नाही"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"तुम्ही इंटरनेटशी कनेक्ट केले असल्याची खात्री करा आणि पुन्हा सेटअप करून पहा"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"सॅटेलाइट मेसेजिंग उपलब्ध नाही"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"या देशामध्ये किंवा प्रदेशामध्ये सॅटेलाइट मेसेजिंग उपलब्ध नाही"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"सॅटेलाइट मेसेजिंग सेट केलेले नाही"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"सॅटेलाइटद्वारे मेसेज करण्यासाठी, Google Messages ला तुमचे डीफॉल्ट मेसेजिंग ॲप म्हणून सेट करा"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"सॅटेलाइट मेसेजिंग उपलब्ध नाही"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"या देशात किंवा प्रदेशात सॅटेलाइट मेसेजिंग उपलब्ध आहे का हे तपासण्यासाठी, स्थान सेटिंग्ज सुरू करा"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिंट अनलॉक पुन्हा सेट करा"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> यापुढे ओळखता येणार नाही."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> आणि <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> यापुढे ओळखता येणार नाहीत."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 9bda1735561e..bd780ae0be92 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Benarkan apl menentukan kedudukan relatif antara peranti Ultrajalur Lebar berdekatan"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan peranti Wi-Fi berdekatan"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Membenarkan apl mengiklankan, menyambung dan menentukan kedudukan relatif peranti Wi-Fi berdekatan"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maklumat Perkhidmatan Pembayaran NFC Pilihan"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Membenarkan apl mendapatkan maklumat perkhidmatan pembayaran nfc pilihan seperti bantuan berdaftar dan destinasi laluan."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"mengawal Komunikasi Medan Dekat"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hujung minggu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Jangan Ganggu (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Diurus oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Hidup"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Mati"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> hingga <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Sebarang kalendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> meredamkan sesetengah bunyi"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Terdapat masalah dalaman dengan peranti anda. Peranti mungkin tidak stabil sehingga anda membuat tetapan semula data kilang."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Hantar dan terima mesej tanpa rangkaian mudah alih atau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara ciri ini berfungsi"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Hidupkan \"Pilih rangkaian secara automatik\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Hidupkan \"Pilih rangkaian secara automatik\" dalam Tetapan supaya telefon anda boleh menemukan rangkaian yang berfungsi dengan satelit"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Hidupkan"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Kembali"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS via Satelit kini tersedia"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Anda boleh menghantar mesej kepada perkhidmatan kecemasan jika tiada rangkaian mudah alih atau Wi-Fi. Google Messages mestilah ditetapkan sebagai apl pemesejan lalai anda."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS via Satelit tidak disokong"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS via Satelit tidak disokong pada peranti ini"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS via Satelit tidak disediakan"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Pastikan anda disambungkan kepada Internet dan cuba buat persediaan sekali lagi"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via Satelit tidak tersedia"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS via Satelit tidak tersedia di negara atau rantau ini"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via Satelit tidak disediakan"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Untuk menghantar mesej melalui satelit, tetapkan Google Messages sebagai apl pemesejan lalai anda"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via Satelit tidak tersedia"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Untuk menyemak sama ada SOS via Satelit tersedia di negara atau rantau ini, hidupkan tetapan lokasi"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Permesejan satelit tersedia"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Anda boleh menghantar mesej melalui satelit jika tiada rangkaian mudah alih atau Wi-Fi. Google Messages mestilah ditetapkan sebagai apl pemesejan lalai anda."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Permesejan satelit tidak disokong"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Permesejan satelit tidak disokong pada peranti ini"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Permesejan satelit tidak disediakan"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Pastikan anda disambungkan kepada Internet dan cuba buat persediaan sekali lagi"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Permesejan satelit tidak tersedia"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Permesejan satelit tidak tersedia di negara atau rantau ini"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Permesejan satelit tidak disediakan"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Untuk menghantar mesej melalui satelit, tetapkan Google Messages sebagai apl pemesejan lalai anda"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Permesejan satelit tidak tersedia"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Untuk menyemak sama ada permesejan satelit tersedia di negara atau rantau ini, hidupkan tetapan lokasi"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Sediakan Buka Kunci Cap Jari sekali lagi"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak dapat dicam lagi."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak dapat dicam lagi."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2581a74c6b4e..e4c2acaba9f5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"အနီးရှိ ‘အလွန်ကျယ်ပြန့်သော လှိုင်းအလျားသုံးစက်များ’ ကြား မှန်းခြေနေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုမည်"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"အနီးရှိ Wi-Fi စက်များနှင့် ပြန်လှန်တုံ့ပြန်ခြင်း"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ကြော်ငြာရန်၊ ချိတ်ဆက်ရန်နှင့် အနီးတစ်ဝိုက်ရှိ Wi-Fi စက်များ၏ နေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ဦးစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"အက်ပ်အား ဦစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များဖြစ်သည့် မှတ်ပုံတင်ထားသော အကူအညီများနှင့် သွားလာရာ လမ်းကြောင်းတို့ကို ရယူရန် ခွင့်ပြုသည်။"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communicationအား ထိန်းချုပ်ရန်"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"စနေ၊ တနင်္ဂနွေ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"အစီအစဉ်"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"အိပ်နေချိန်"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"မနှောင့်ယှက်ရ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> က စီမံသည်"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ဖွင့်"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ပိတ်"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"၊ "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> မှ <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"မည်သည့်ပြက္ခဒိန်မဆို"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> သည် အချို့အသံကို ပိတ်နေသည်"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေပြီး၊ မူလစက်ရုံထုတ်အခြေအနေအဖြစ် ပြန်လည်ရယူနိုင်သည်အထိ အခြေအနေမတည်ငြိမ်နိုင်ပါ။"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက်မရှိဘဲ မက်ဆေ့ဂျ်များ ပို့နိုင်၊ လက်ခံနိုင်သည်"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ဖွင့်ရန်"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"အလုပ်လုပ်ပုံ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"“ကွန်ရက် အလိုအလျောက်ရွေးရန်” ကို ဖွင့်ပါ"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"သင့်ဖုန်းက ဂြိုဟ်တုဖြင့် အလုပ်လုပ်သော ကွန်ရက်ကို ရှာနိုင်ရန်အတွက် ဆက်တင်များတွင် “ကွန်ရက် အလိုအလျောက်ရွေးရန်” ကို ဖွင့်ပါ"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ဖွင့်ရန်"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"နောက်သို့"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS ကို ယခုသုံးနိုင်ပြီ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက် မရှိပါက အရေးပေါ်ဝန်ဆောင်မှု ဌာနများသို့ မက်ဆေ့ဂျ်ပို့နိုင်သည်။ Google Messages သည် သင်၏ မူရင်းမက်ဆေ့ဂျ်ပို့ရန်အက်ပ် ဖြစ်ရမည်။"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS ကို ပံ့ပိုးမထားပါ"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS ကို ဤစက်တွင် ပံ့ပိုးမထားပါ"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS ကို စနစ်ထည့်သွင်းမထားပါ"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"အင်တာနက်နှင့် ချိတ်ဆက်ထားခြင်း ရှိ၊ မရှိ စစ်ဆေးပြီး စနစ်ထပ်မံထည့်သွင်းကြည့်ပါ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS မရနိုင်ပါ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS ကို ဤနိုင်ငံ (သို့) ဒေသတွင် မရနိုင်ပါ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS ကို စနစ်ထည့်သွင်းမထားပါ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ရန် Google Messages ကို သင်၏ မက်ဆေ့ဂျ်ပို့ရန် မူရင်းအက်ပ်အဖြစ် သတ်မှတ်ပါ"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS မရနိုင်ပါ"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"satellite SOS ကို ဤနိုင်ငံ (သို့) ဒေသတွင် ရနိုင်ခြင်းရှိ၊ မရှိ စစ်ဆေးရန် တည်နေရာပြ ဆက်တင်များကို ဖွင့်ပါ"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်း ရနိုင်သည်"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက် မရှိသည့်အခါ ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့နိုင်သည်။ Google Messages သည် သင်၏ မူရင်းမက်ဆေ့ဂျ်ပို့ရန်အက်ပ် ဖြစ်ရမည်။"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို ပံ့ပိုးမထားပါ"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို ဤစက်တွင် ပံ့ပိုးမထားပါ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို စနစ်ထည့်သွင်းမထားပါ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"အင်တာနက်နှင့် ချိတ်ဆက်ထားခြင်း ရှိ၊ မရှိ စစ်ဆေးပြီး စနစ်ထပ်မံထည့်သွင်းကြည့်ပါ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို မရနိုင်ပါ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို ဤနိုင်ငံ (သို့) ဒေသတွင် မရနိုင်ပါ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို စနစ်ထည့်သွင်းမထားပါ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ရန် Google Messages ကို သင်၏ မက်ဆေ့ဂျ်ပို့ရန် မူရင်းအက်ပ်အဖြစ် သတ်မှတ်ပါ"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို မရနိုင်ပါ"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို ဤနိုင်ငံ (သို့) ဒေသတွင် ရနိုင်ခြင်းရှိ၊ မရှိ စစ်ဆေးရန် တည်နေရာပြ ဆက်တင်များကို ဖွင့်ပါ"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ ကို စနစ်ထပ်မံထည့်သွင်းပါ"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ကို မသိရှိနိုင်တော့ပါ။"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> နှင့် <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ကို မသိရှိနိုင်တော့ပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 22288642cb79..ec4cd961faee 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"tillate at appen fastslår den relative posisjonen mellom enheter i nærheten som bruker ultrabredbånd"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"samhandle med wifi-enheter i nærheten"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lar appen annonsere, koble til og fastslå den relative posisjonen til wifi-enheter i nærheten"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasjon om prioritert NFC-betalingstjeneste"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gir appen tilgang til informasjon om prioritert NFC-betalingstjeneste, for eksempel registrerte hjelpemidler og destinasjon."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontroller overføring av data med NFC-teknologi"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helg"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Aktivitet"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ikke forstyrr (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administreres av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> til <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Hvilken som helst kalender"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår av noen lyder"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Det har oppstått et internt problem på enheten din, og den kan være ustabil til du tilbakestiller den til fabrikkdata."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send og motta meldinger uten mobil- eller wifi-nettverk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Åpne Meldinger"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Slik fungerer det"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Slå på «Velg nettverk automatisk»"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Slå på «Velg nettverk automatisk» i innstillingene, slik at telefonen kan finne et nettverk som fungerer med satellitt"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Slå på"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Gå tilbake"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer opplåsingen med fingeravtrykk på nytt"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> gjenkjennes ikke lenger."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> gjenkjennes ikke lenger."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 5684d780be8f..0dc0bd3d04a9 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -293,7 +293,7 @@
<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_updates" msgid="7907863984825495278">"अद्यावधिकहरू"</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>
<string name="notification_channel_network_available" msgid="6083697929214165169">"नेटवर्क उपलब्ध छ"</string>
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"यो एपलाई नजिकै रहेका अल्ट्रा-वाइडब्यान्ड चल्ने डिभाइसहरूबिचको तुलनात्मक स्थान पत्ता लगाउन दिनुहोस्"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Wi-Fi चल्ने नजिकै रहेका डिभाइसहरूमा चलाउन दिन्छ"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"यसले एपलाई Wi-Fi चल्ने नजिकै रहेका डिभाइसहरूमा विज्ञापन गर्न, कनेक्ट गर्न र सापेक्ष स्थिति निर्धारण गर्न दिन्छ"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC भुक्तानी सेवासम्बन्धी रुचाइएको जानकारी"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"यसले एपलाई दर्ता गरिएका सहायता तथा मार्गको गन्तव्य जस्ता रुचाइएका NFC भुक्तानी सेवासम्बन्धी जानकारी प्राप्त गर्न दिन्छ।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"नजिक क्षेत्र संचार नियन्त्रणहरू"</string>
@@ -807,7 +811,7 @@
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"सञ्जाल अवस्थाका पर्यवेक्षणका लागि सुन्नुहोस्"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"सञ्जाल अवस्थाका पर्यवेक्षण सुन्नका लागि एपलाई अनुमति दिन्छ।सामान्य एपलाई चाँहिदै नचाँहिन सक्छ।"</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिभाइस क्यालिब्रेसन परिवर्तन गर्नुहोस्"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"एपलाई टच स्क्रीनको प्यारामिटरहरू क्यालिब्रेसन परिमार्जन गर्न अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै आवश्यक पर्दैन।"</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"एपलाई टच स्क्रिनको प्यारामिटरहरू क्यालिब्रेसन परिमार्जन गर्न अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै आवश्यक पर्दैन।"</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM प्रमाणपत्रको पहुँच"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM प्रमाणपत्रहरू प्रावधान र प्रयोग गर्ने निवेदनको अनुमति दिन्छ। साधारण एपहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam स्थानान्तरण अवस्था प्राप्त गर्नुहोस्"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"शनिवार"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"कार्यक्रम"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"शयन"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Do Not Disturb (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले व्यवस्थापन गरेको"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"अन छ"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"अफ छ"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> देखि <xliff:g id="END">%2$s</xliff:g> सम्म"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"कुनै पनि पात्रो"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल वा Wi-Fi नेटवर्कविनै म्यासेजहरू पठाउनुहोस् र प्राप्त गर्नुहोस्"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages खोल्नुहोस्"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यसले काम गर्ने तरिका"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"स्वतः नेटवर्क चयन गर्नुहोस्\" अन गर्नुहोस्"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"तपाईंको फोनले स्याटलाइटसँग काम गर्ने नेटवर्क भेट्टाउन सकोस् भन्नका लागि सेटिङमा गई \"स्वतः नेटवर्क चयन गर्नुहोस्\" अन गर्नुहोस्"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"अन गर्नुहोस्"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"पछाडि जानुहोस्"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"स्याटलाइट SOS अब उपलब्ध भएको छ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"मोबाइल वा Wi-Fi नेटवर्क नभएका खण्डमा तपाईं आपत्‍कालीन सेवामा म्यासेज पठाउन सक्नुहुन्छ। Google Messages अनिवार्य रूपमा तपाईंको डिफल्ट म्यासेजिङ एप हुनु पर्छ।"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"स्याटलाइट SOS प्रयोग गर्न मिल्दैन"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"यो डिभाइसमा स्याटलाइट SOS प्रयोग गर्न मिल्दैन"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"स्याटलाइट SOS सेटअप गरिएको छैन"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"तपाईंको डिभाइस इन्टरनेटमा कनेक्ट गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस् र फेरि सेटअप गरी हेर्नुहोस्"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"स्याटलाइट SOS उपलब्ध छैन"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"यो देश वा क्षेत्रमा स्याटलाइट SOS उपलब्ध छैन"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"स्याटलाइट SOS सेटअप गरिएको छैन"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"स्याटलाइटमार्फत म्यासेज पठाउन Google Messages लाई डिफल्ट म्यासेजिङ एपका रूपमा सेट गर्नुहोस्"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"स्याटलाइट SOS उपलब्ध छैन"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"यो देश वा क्षेत्रमा स्याटलाइट SOS उपलब्ध छ कि छैन भन्ने कुरा जाँच्न लोकेसन सेटिङ अन गर्नुहोस्"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा उपलब्ध छ"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"तपाईं मोबाइल वा Wi-Fi नेटवर्क उपलब्ध नभएका खण्डमा स्याटलाइटमार्फत म्यासेज पठाउन सक्नुहुन्छ। Google Messages अनिवार्य रूपमा तपाईंको डिफल्ट म्यासेजिङ एप हुनु पर्छ।"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा प्रयोग गर्न मिल्दैन"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"यो डिभाइसमा स्याटलाइटमार्फत म्यासेज पठाउने सुविधा प्रयोग गर्न मिल्दैन"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा सेटअप गरिएको छैन"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"तपाईंको डिभाइस इन्टरनेटमा कनेक्ट गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस् र फेरि सेटअप गरी हेर्नुहोस्"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा उपलब्ध छैन"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"यो देश वा क्षेत्रमा स्याटलाइटमार्फत म्यासेज पठाउने सुविधा उपलब्ध छैन"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा सेटअप गरिएको छैन"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"स्याटलाइटमार्फत म्यासेज पठाउन Google Messages लाई डिफल्ट म्यासेजिङ एपका रूपमा सेट गर्नुहोस्"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा उपलब्ध छैन"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"यो देश वा क्षेत्रमा स्याटलाइटमार्फत म्यासेज पठाउने सुविधा उपलब्ध छ कि छैन भन्ने कुरा जाँच्न लोकेसन सेटिङ अन गर्नुहोस्"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिन्ट अनलक फेरि सेटअप गर्नुहोस्"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> अब पहिचान गर्न सकिँदैन।"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> र <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> अब पहिचान गर्न सकिँदैन।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index ac18bcf26f10..e55334740778 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"De app toestaan om de relatieve positie tussen ultrabreedbandapparaten in de buurt te bepalen"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactie met wifi-apparaten in de buurt"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Hiermee kan de app uitzenden, verbindingen maken en de relatieve positie bepalen van wifi-apparaten in de buurt"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informatie over voorkeursservice voor NFC-betaling"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Hiermee kun je zorgen dat de app informatie krijgt over de voorkeursservice voor NFC-betaling, zoals geregistreerde hulpmiddelen en routebestemmingen."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication regelen"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Afspraak"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slapen"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Niet storen (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Beheerd door <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Uit"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> tot <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Elke agenda"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Stuur en krijg berichten zonder mobiel of wifi-netwerk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Berichten openen"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe het werkt"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Netwerk automatisch selecteren aanzetten"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Zet Netwerk automatisch selecteren aan in Instellingen zodat je telefoon een netwerk kan vinden dat werkt met satellieten"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aanzetten"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Terug"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS via satelliet is nu beschikbaar"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Je kunt berichten sturen naar de hulpdiensten als er geen mobiel of wifi-netwerk beschikbaar is. Google Berichten moet je standaard berichten-app zijn."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS via satelliet wordt niet ondersteund"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS via satelliet wordt niet ondersteund op dit apparaat"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS via satelliet is niet ingesteld"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Zorg dat je verbinding hebt met internet en probeer het opnieuw"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via satelliet is niet beschikbaar"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS via satelliet is niet beschikbaar in dit land of deze regio"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via satelliet niet ingesteld"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Stel Google Berichten in als je standaard berichten-app om via satelliet berichten te sturen"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via satelliet is niet beschikbaar"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Zet locatie-instellingen aan om te checken of SOS via satelliet beschikbaar is in dit land of deze regio"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellietberichten beschikbaar"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Je kunt via satelliet berichten sturen als er geen mobiel of wifi-netwerk beschikbaar is. Google Berichten moet je standaard berichten-app zijn."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellietberichten niet ondersteund"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellietberichten worden niet ondersteund op dit apparaat"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellietberichten niet ingesteld"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Zorg dat je verbinding hebt met internet en probeer het opnieuw"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellietberichten niet beschikbaar"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellietberichten zijn niet beschikbaar in dit land of deze regio"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellietberichten niet ingesteld"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Stel Google Berichten in als je standaard berichten-app om via satelliet berichten te sturen"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellietberichten niet beschikbaar"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Zet locatie-instellingen aan om te checken of satellietberichten beschikbaar zijn in dit land of deze regio"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ontgrendelen met vingerafdruk weer instellen"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> wordt niet meer herkend."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> worden niet meer herkend."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index c860657df157..2515bb5052eb 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ଆଖପାଖର ଅଲଟ୍ରା-ୱାଇଡବ୍ୟାଣ୍ଡ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ଆଖପାଖର ୱାଇ-ଫାଇ ଡିଭାଇସଗୁଡ଼ିକ ସହ ଇଣ୍ଟରାକ୍ଟ କରନ୍ତୁ"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ଆଖପାଖର ୱାଇ-ଫାଇ ଡିଭାଇସରେ ବିଜ୍ଞାପନ ଦେବା, ତା ସହ ସଂଯୋଗ କରିବା ଓ ତା’ର ଆପେକ୍ଷିକ ଅବସ୍ଥିତି ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ପସନ୍ଦର NFC ପେମେଣ୍ଟ ସେବା ସୂଚନା"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ପଞ୍ଜିକୃତ ଯନ୍ତ୍ର ଏବଂ ମାର୍ଗ ଲକ୍ଷସ୍ଥଳ ପରି ପସନ୍ଦର nfc ପେମେଣ୍ଟ ସେବା ସୂଚନା ପାଇବାକୁ ଆପ୍ ଅନୁମତି କରିଥାଏ।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ନିଅର୍ ଫିଲ୍ଡ କମ୍ୟୁନିକେଶନ୍ ଉପରେ ନିୟନ୍ତ୍ରଣ ରଖନ୍ତୁ"</string>
@@ -1112,7 +1116,7 @@
<string name="menu_sym_shortcut_label" msgid="4037566049061218776">"Sym+"</string>
<string name="menu_function_shortcut_label" msgid="2367112760987662566">"Function+"</string>
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"ସ୍ପେସ୍‍"</string>
- <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"ଏଣ୍ଟର୍"</string>
+ <string name="menu_enter_shortcut_label" msgid="6709499510082897320">"ଏଣ୍ଟର"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"ଡିଲିଟ କରନ୍ତୁ"</string>
<string name="search_go" msgid="2141477624421347086">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
<string name="search_hint" msgid="455364685740251925">"ସର୍ଚ୍ଚ କରନ୍ତୁ…"</string>
@@ -1600,7 +1604,7 @@
<string name="keyboardview_keycode_done" msgid="2524518019001653851">"ହୋଇଗଲା"</string>
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"ମୋଡ୍‍ ପରିବର୍ତ୍ତନ"</string>
<string name="keyboardview_keycode_shift" msgid="3026509237043975573">"ଶିଫ୍ଟ"</string>
- <string name="keyboardview_keycode_enter" msgid="168054869339091055">"ଏଣ୍ଟର୍‌"</string>
+ <string name="keyboardview_keycode_enter" msgid="168054869339091055">"ଏଣ୍ଟର"</string>
<string name="activitychooserview_choose_application" msgid="3500574466367891463">"ଗୋଟିଏ ଆପ୍‍ ବାଛନ୍ତୁ"</string>
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ଲଞ୍ଚ କରାଯାଇପାରିଲା ନାହିଁ"</string>
<string name="shareactionprovider_share_with" msgid="2753089758467748982">"ଏହାଙ୍କ ସହ ସେୟାର୍‍ କରନ୍ତୁ"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ସପ୍ତାହାନ୍ତ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ଇଭେଣ୍ଟ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ଶୋଇବା"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ଚାଲୁ ଅଛି"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ବନ୍ଦ ଅଛି"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>ରୁ <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ଯେକୌଣସି କ୍ୟାଲେଣ୍ଡର୍‌"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> କିଛି ସାଉଣ୍ଡକୁ ମ୍ୟୁଟ୍ କରୁଛି"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ଏକ ସମସ୍ୟା ରହିଛି ଏବଂ ଆପଣ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଅସ୍ଥିର ରହିପାରେ।"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ଏକ ମୋବାଇଲ କିମ୍ବା ୱାଇ-ଫାଇ ନେଟୱାର୍କ ବିନା ମେସେଜ ପଠାନ୍ତୁ ଏବଂ ପାଆନ୍ତୁ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ଖୋଲନ୍ତୁ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ଏହା କିପରି କାମ କରେ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"ସ୍ୱତଃ ନେଟୱାର୍କକୁ ଚୟନ କରନ୍ତୁ\"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ସେଟିଂସରେ \"ସ୍ୱତଃ ନେଟୱାର୍କକୁ ଚୟନ କରନ୍ତୁ\"କୁ ଚାଲୁ କରନ୍ତୁ, ଯାହା ଫଳରେ ଆପଣଙ୍କ ଫୋନ ସେଟେଲାଇଟ ସହିତ କାର୍ଯ୍ୟ କରୁଥିବା ଏକ ନେଟୱାର୍କକୁ ଖୋଜିପାରିବ"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ଚାଲୁ କରନ୍ତୁ"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>କୁ ଆଉ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ।"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ଏବଂ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>କୁ ଆଉ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 63ebeb8d567d..574d991b9dfc 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਅਲਟ੍ਰਾ-ਵਾਈਡਬੈਂਡ ਡੀਵਾਈਸਾਂ ਦੇ ਵਿਚਾਲੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ਨਜ਼ਦੀਕੀ ਵਾਈ-ਫਾਈ ਡੀਵਾਈਸਾਂ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰੋ"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਵਾਈ-ਫਾਈ ਡੀਵਾਈਸਾਂ \'ਤੇ ਵਿਗਿਆਪਨ ਦੇਣ, ਕਨੈਕਟ ਕਰਨ ਅਤੇ ਉਨ੍ਹਾਂ ਦੀ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ਐਪ ਨੂੰ ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਰਜਿਸਟਰ ਕੀਤੇ ਸਾਧਨ ਅਤੇ ਮੰਜ਼ਿਲ ਰਸਤਾ।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ਹਫ਼ਤੇ ਦਾ ਅੰਤਲਾ ਦਿਨ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ਇਵੈਂਟ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ਸੌਣ ਵੇਲੇ"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ਚਾਲੂ ਹੈ"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ਬੰਦ ਹੈ"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> ਤੋਂ <xliff:g id="END">%2$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ਕੋਈ ਵੀ ਕੈਲੰਡਰ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ਕੁਝ ਧੁਨੀਆਂ ਨੂੰ ਮਿਊਟ ਕਰ ਰਹੀ ਹੈ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਹੈ ਅਤੇ ਇਹ ਅਸਥਿਰ ਹੋ ਸਕਦੀ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਨਹੀਂ ਕਰਦੇ।"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਤੋਂ ਬਿਨਾਂ ਸੁਨੇਹੇ ਭੇਜੋ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ਐਪ ਖੋਲ੍ਹੋ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਨੈੱਟਵਰਕ ਚੁਣੋ\" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ \"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਨੈੱਟਵਰਕ ਚੁਣੋ\" ਨੂੰ ਚਾਲੂ ਕਰੋ, ਤਾਂ ਜੋ ਤੁਹਾਡਾ ਫ਼ੋਨ ਅਜਿਹਾ ਨੈੱਟਵਰਕ ਲੱਭ ਸਕੇ ਜੋ ਸੈਟੇਲਾਈਟ ਨਾਲ ਕੰਮ ਕਰਦਾ ਹੋਵੇ"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ਚਾਲੂ ਕਰੋ"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ਵਾਪਸ ਜਾਓ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਹੁਣ ਉਪਲਬਧ ਹੈ"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾ ਹੋਣ \'ਤੇ, ਤੁਸੀਂ ਐਮਰਜੈਂਸੀ ਸੇਵਾਵਾਂ ਨੂੰ ਸੁਨੇਹਾ ਭੇਜ ਸਕਦੇ ਹੋ। Google Messages ਤੁਹਾਡੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੁਨੇਹਾ ਐਪ ਹੋਣੀ ਲਾਜ਼ਮੀ ਹੈ।"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟ ਅੱਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਹੋ ਅਤੇ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਇਸ ਦੇਸ਼ ਜਾਂ ਖੇਤਰ ਵਿੱਚ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟ ਅੱਪ ਨਹੀਂ ਹੋਇਆ"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"ਸੈਟੇਲਾਈਟ ਰਾਹੀਂ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, Google Messages ਨੂੰ ਆਪਣੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੁਨੇਹਾ ਐਪ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ਇਸਦੀ ਜਾਂਚ ਕਰਨ ਲਈ ਕਿ ਇਸ ਦੇਸ਼ ਜਾਂ ਖੇਤਰ ਵਿੱਚ ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਹੈ, ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਹੈ"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾ ਹੋਣ \'ਤੇ, ਤੁਸੀਂ ਸੈਟੇਲਾਈਟ ਰਾਹੀਂ ਸੁਨੇਹੇ ਭੇਜ ਸਕਦੇ ਹੋ। Google Messages ਤੁਹਾਡੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੁਨੇਹਾ ਐਪ ਹੋਣੀ ਲਾਜ਼ਮੀ ਹੈ।"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਹੋ ਅਤੇ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"ਇਸ ਦੇਸ਼ ਜਾਂ ਖੇਤਰ ਵਿੱਚ ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"ਸੈਟੇਲਾਈਟ ਰਾਹੀਂ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, Google Messages ਨੂੰ ਆਪਣੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੁਨੇਹਾ ਐਪ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"ਇਹ ਜਾਂਚ ਕਰਨ ਲਈ ਕਿ ਇਸ ਦੇਸ਼ ਜਾਂ ਖੇਤਰ ਵਿੱਚ ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਹੈ ਜਾਂ ਨਹੀਂ, ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> ਦੀ ਹੁਣ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ਦੀ ਹੁਣ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index fa2b493f843b..f267877b3a18 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -590,7 +590,7 @@
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Pozwala aplikacji na odbieranie pakietów wysyłanych przez sieć Wi-Fi do wszystkich urządzeń, a nie tylko do Twojego tabletu, przy użyciu adresów połączeń grupowych. Powoduje większe zapotrzebowanie na energię niż w trybie innym niż grupowy."</string>
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Pozwala aplikacji odbierać pakiety wysyłane przez sieć Wi-Fi do wszystkich urządzeń (a nie tylko do Twojego urządzenia z Androidem TV) przy użyciu adresów połączeń grupowych. Powoduje większe zapotrzebowanie na energię niż w innych trybach."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Pozwala aplikacji na odbieranie pakietów wysyłanych przez sieć Wi-Fi do wszystkich urządzeń, a nie tylko do Twojego telefonu, przy użyciu adresów połączeń grupowych. Powoduje większe zapotrzebowanie na energię niż w trybie innym niż grupowy."</string>
- <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"uzyskiwanie dostępu do ustawień Bluetooth"</string>
+ <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"uzyskiwanie dostępu do ustawień Bluetootha"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Pozwala aplikacji na konfigurowanie lokalnego tabletu z funkcją Bluetooth oraz na wykrywanie urządzeń zdalnych i parowanie z nimi."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Pozwala aplikacji konfigurować Bluetootha na urządzeniu z Androidem TV oraz wykrywać urządzenia zdalne i przeprowadzać parowanie z nimi."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Pozwala aplikacji na konfigurowanie lokalnego telefonu z funkcją Bluetooth oraz na wykrywanie urządzeń zdalnych i parowanie z nimi."</string>
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Zezwól na określanie przez aplikację względnego położenia urządzeń ultraszerokopasmowych w pobliżu"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcje z urządzeniami Wi-Fi w pobliżu"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Zezwala aplikacji na przesyłanie informacji o sobie, łączenie się z urządzeniami Wi‑Fi w pobliżu i określanie ich względnego położenia"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacje o preferowanych usługach płatniczych NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Pozwala aplikacji uzyskiwać informacje o preferowanych usługach płatniczych NFC, np. zarejestrowanych pomocach i miejscach docelowych tras."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrolowanie łączności Near Field Communication"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Wydarzenie"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sen"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Nie przeszkadzać (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Zarządzana przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Włączono"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Wyłączono"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"Od <xliff:g id="START">%1$s</xliff:g> do <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Dowolny kalendarz"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> wycisza niektóre dźwięki"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"W Twoim urządzeniu wystąpił problem wewnętrzny. Może być ono niestabilne, dopóki nie przywrócisz danych fabrycznych."</string>
@@ -2428,15 +2432,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Wysyłaj i odbieraj wiadomości bez sieci komórkowej czy Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otwórz Wiadomości"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to działa"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Włącz „Automatycznie wybieraj sieć”"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"W Ustawieniach włącz opcję „Automatycznie wybieraj sieć”, aby telefon mógł znaleźć sieć współpracującą z satelitą"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Włącz"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Wróć"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satelitarne połączenie alarmowe jest już dostępne"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Możesz wysłać wiadomość do służb ratunkowych, jeśli masz problem z połączeniem się z siecią komórkową lub Wi-Fi. Wiadomości Google muszą być domyślną aplikacją do SMS-ów."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satelitarne połączenie alarmowe nie jest obsługiwane"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"To urządzenie nie obsługuje satelitarnego połączenia alarmowego"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satelitarne połączenie alarmowe nie jest skonfigurowane"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Upewnij się, że masz połączenie z internetem, i spróbuj ponownie"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satelitarne połączenie alarmowe nie jest dostępne"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satelitarne połączenie alarmowe nie jest dostępne w tym kraju lub regionie"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satelitarne połączenie alarmowe nie zostało skonfigurowane"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Aby wysyłać wiadomości przez satelitę, ustaw Wiadomości Google jako domyślną aplikację do obsługi SMS-ów"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satelitarne połączenie alarmowe nie jest dostępne"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Aby sprawdzić, czy satelitarne połączenie alarmowe jest dostępne w tym kraju lub regionie, włącz ustawienia lokalizacji"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Przesyłanie wiadomości przez satelitę jest dostępne"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Możesz wysyłać wiadomości przez satelitę, jeśli nie masz dostępu do sieci komórkowej lub Wi-Fi. Wiadomości Google muszą być domyślną aplikacją do SMS-ów."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Przesyłanie wiadomości przez satelitę nie jest obsługiwane"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"To urządzenie nie obsługuje przesyłania wiadomości przez satelitę"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Nie skonfigurowano przesyłania wiadomości przez satelitę"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Upewnij się, że masz połączenie z internetem, i spróbuj ponownie"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Przesyłanie wiadomości przez satelitę jest niedostępne"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Przesyłanie wiadomości przez satelitę nie jest dostępne w tym kraju lub regionie"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Nie skonfigurowano przesyłania wiadomości przez satelitę"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Aby wysyłać wiadomości przez satelitę, ustaw Wiadomości Google jako domyślną aplikację do obsługi SMS-ów"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Przesyłanie wiadomości przez satelitę jest niedostępne"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Aby sprawdzić, czy przesyłanie wiadomości przez satelitę jest dostępne w tym kraju lub regionie, włącz ustawienia lokalizacji"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Skonfiguruj ponownie odblokowywanie odciskiem palca"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ten odcisk palca (<xliff:g id="FINGERPRINT">%s</xliff:g>) nie jest już rozpoznawany."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Te odciski palców (<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>) nie są już rozpoznawane."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 76a8dc110eac..1719648d10f0 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app divulgue, faça conexão e determine a posição relativa de dispositivos Wi-Fi por perto."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string>
@@ -1755,7 +1759,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Correção de cor"</string>
- <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo uma mão"</string>
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Não perturbe (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerenciada pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer agenda"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Ative a opção \"Selecionar a rede automaticamente\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Ative a opção \"Selecionar a rede automaticamente\" nas configurações para que o smartphone encontre uma rede que funcione com satélite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Voltar"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"O SOS via satélite já está disponível"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Você poderá enviar uma mensagem para serviços de emergência se não houver uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Incompatível com o SOS via satélite"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Este dispositivo não é compatível com o SOS via satélite"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"O SOS via satélite não foi configurado"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Verifique sua conexão de Internet e tente configurar de novo"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via satélite indisponível"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"O SOS via satélite não está disponível neste país ou região"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via satélite não configurado"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via satélite indisponível"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar se o SOS via satélite está disponível neste país ou região, ative as configurações de localização"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mensagem via satélite disponível"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"É possível trocar mensagens por satélite caso não haja uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Incompatível com mensagem via satélite"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Este dispositivo não é compatível com mensagem via satélite"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mensagem via satélite não configurada"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Verifique sua conexão de Internet e tente configurar de novo"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mensagem via satélite indisponível"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"A mensagem via satélite não está disponível neste país ou região"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mensagem via satélite não configurada"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mensagem via satélite indisponível"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar se a mensagem via satélite está disponível neste país ou região, ative as configurações de localização"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não é mais reconhecida."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não são mais reconhecidas."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 86e0fcdad8a1..b8fc6a96f783 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permita que a app determine a posição relativa entre os dispositivos de banda ultralarga próximos"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi‑Fi próximos"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que a app anuncie, estabeleça ligação e determine a posição relativa de dispositivos Wi‑Fi próximos"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações de serviços de pagamento com NFC preferenciais"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a app obtenha informações de serviços de pagamento com NFC preferenciais, como apoios registados e destino da rota."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlo Near Field Communication"</string>
@@ -1939,6 +1943,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Não incomodar (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerido por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
@@ -2431,6 +2436,30 @@
<string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string>
<string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retroceder"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"O Satélite SOS já está disponível"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Pode enviar mensagens aos serviços de emergência se não tiver uma rede móvel nem Wi-Fi. A app Mensagens Google tem de ser a sua app de mensagens predefinida."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"O Satélite SOS não é compatível"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"O Satélite SOS não é compatível com este dispositivo"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"O Satélite SOS não está configurado"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Certifique-se de que tem ligação à Internet e tente configurar novamente"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"O Satélite SOS não está disponível"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"O Satélite SOS não está disponível neste país ou região"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satélite SOS não configurado"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensagens por satélite, defina a app Mensagens Google como a sua app de mensagens predefinida"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"O Satélite SOS não está disponível"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar se o Satélite SOS está disponível neste país ou região, ative as definições de localização"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mensagens por satélite disponíveis"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Pode enviar mensagens por satélite se não tiver uma rede móvel nem Wi-Fi. A app Mensagens Google tem de ser a sua app de mensagens predefinida."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"As mensagens por satélite não são compatíveis"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"As mensagens por satélite não são compatíveis com este dispositivo"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mensagens por satélite não configuradas"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Certifique-se de que tem ligação à Internet e tente configurar novamente"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mensagens por satélite não disponíveis"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"As mensagens por satélite não estão disponíveis neste país ou região"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mensagens por satélite não configuradas"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensagens por satélite, defina a app Mensagens Google como a sua app de mensagens predefinida"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mensagens por satélite não disponíveis"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar se as mensagens por satélite estão disponíveis neste país ou região, ative as definições de localização"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configure o Desbloqueio por impressão digital novamente"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Já não é possível reconhecer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Já não é possível reconhecer <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 76a8dc110eac..1719648d10f0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app divulgue, faça conexão e determine a posição relativa de dispositivos Wi-Fi por perto."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string>
@@ -1755,7 +1759,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="7975133554160979214">"Correção de cor"</string>
- <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo uma mão"</string>
+ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Não perturbe (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerenciada pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Qualquer agenda"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Ative a opção \"Selecionar a rede automaticamente\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Ative a opção \"Selecionar a rede automaticamente\" nas configurações para que o smartphone encontre uma rede que funcione com satélite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Voltar"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"O SOS via satélite já está disponível"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Você poderá enviar uma mensagem para serviços de emergência se não houver uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Incompatível com o SOS via satélite"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Este dispositivo não é compatível com o SOS via satélite"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"O SOS via satélite não foi configurado"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Verifique sua conexão de Internet e tente configurar de novo"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via satélite indisponível"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"O SOS via satélite não está disponível neste país ou região"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via satélite não configurado"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via satélite indisponível"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar se o SOS via satélite está disponível neste país ou região, ative as configurações de localização"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mensagem via satélite disponível"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"É possível trocar mensagens por satélite caso não haja uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Incompatível com mensagem via satélite"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Este dispositivo não é compatível com mensagem via satélite"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mensagem via satélite não configurada"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Verifique sua conexão de Internet e tente configurar de novo"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mensagem via satélite indisponível"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"A mensagem via satélite não está disponível neste país ou região"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mensagem via satélite não configurada"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mensagem via satélite indisponível"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar se a mensagem via satélite está disponível neste país ou região, ative as configurações de localização"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não é mais reconhecida."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não são mais reconhecidas."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 384d1b3b22fd..7843bbe1e9f1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"să interacționeze cu dispozitive Wi‑Fi din apropiere"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite aplicației să se conecteze la dispozitive Wi-Fi din apropiere, să transmită anunțuri și să stabilească poziția relativă a acestora"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informații despre serviciul de plăți NFC preferat"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite aplicației să obțină informații despre serviciul de plăți NFC preferat, de exemplu, identificatorii de aplicație înregistrați și destinația traseului."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"controlare schimb de date prin Near Field Communication"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Eveniment"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Somn"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Nu deranja (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionat de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activată"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Dezactivată"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Orice calendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string>
@@ -2183,7 +2187,7 @@
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Conexiunea Bluetooth va rămâne activată în modul Avion"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Se încarcă"</string>
<string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # fișier}few{{file_name} + # fișiere}other{{file_name} + # de fișiere}}"</string>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Nu există persoane recomandate pentru permiterea accesului"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Nu există persoane recomandate pentru trimitere"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de aplicații"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Permisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Pornire"</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Trimite și primește mesaje fără o rețea mobilă sau Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Deschide Mesaje"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cum funcționează"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Activează opțiunea Selectează automat rețeaua"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Activează opțiunea Selectează automat rețeaua în Setări pentru ca telefonul să găsească o rețea compatibilă cu satelitul"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activează"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Înapoi"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Funcția SOS prin satelit este acum disponibilă"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Poți să trimiți mesaje serviciilor de urgență dacă nu este disponibilă o rețea mobilă sau Wi-Fi. Mesaje Google trebuie să fie aplicația ta pentru mesaje prestabilită."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Funcția SOS prin satelit nu este acceptată"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Funcția SOS prin satelit nu este acceptată pe acest dispozitiv"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Funcția SOS prin satelit nu este configurată"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Asigură-te că te-ai conectat la internet și încearcă din nou configurarea"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Funcția SOS prin satelit nu este disponibilă"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Funcția SOS prin satelit nu este disponibilă în această țară sau regiune"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Funcția SOS prin satelit nu este configurată"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Pentru a trimite mesaje prin satelit, setează Mesaje Google ca aplicație pentru mesaje prestabilită"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Funcția SOS prin satelit nu este disponibilă"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Pentru a verifica dacă funcția SOS prin satelit este disponibilă în această țară sau regiune, activează setările privind locația"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mesajele prin satelit sunt disponibile"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Dacă nu este disponibilă o rețea mobilă sau Wi-Fi, poți să trimiți mesaje prin satelit. Mesaje Google trebuie să fie aplicația ta pentru mesaje prestabilită."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Mesajele prin satelit nu sunt acceptate"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Mesajele prin satelit nu sunt acceptate pe acest dispozitiv"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mesajele prin satelit nu sunt configurate"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Asigură-te că te-ai conectat la internet și încearcă din nou configurarea"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mesajele prin satelit nu sunt disponibile"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Mesajele prin satelit nu sunt disponibile în această țară sau regiune"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mesajele prin satelit nu sunt configurate"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Pentru a trimite mesaje prin satelit, setează Mesaje Google ca aplicație pentru mesaje prestabilită"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mesajele prin satelit nu sunt disponibile"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Pentru a verifica dacă mesajele prin satelit sunt disponibile în această țară sau regiune, activează setările privind locația"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurează din nou Deblocarea cu amprenta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nu mai poate fi recunoscută."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> și <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nu mai pot fi recunoscute."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f7e3610a2022..8caa8b60c120 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Приложение сможет определять относительное позиционирование устройств с технологией сверхширокополосной связи поблизости"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Взаимодействие с устройствами Wi‑Fi поблизости"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Приложение сможет передавать данные на устройства Wi‑Fi рядом, подключаться к ним и определять их примерное местоположение."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Сведения о предпочтительном платежном сервисе NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Приложение сможет получать сведения о предпочтительном платежном сервисе NFC (например, зарегистрированные идентификаторы AID и конечный пункт маршрута)."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Управление NFC-модулем"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Выходные"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Мероприятие"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Время сна"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Не беспокоить (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Под управлением приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Включено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Отключено"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Любой календарь"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> приглушает некоторые звуки."</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Произошла внутренняя ошибка, и устройство может работать нестабильно, пока вы не выполните сброс настроек."</string>
@@ -2428,15 +2432,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Отправляйте и получайте сообщения без подключения к мобильной сети или Wi-Fi."</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Открыть Сообщения"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Узнать принцип работы"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Включите автоматический выбор сети"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Чтобы ваш телефон мог найти сеть, которая поддерживает спутниковую связь, включите в настройках параметр \"Выбирать сеть автоматически\"."</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Включить"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Доступен спутниковый SOS"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Вы можете отправлять сообщения экстренным службам, даже когда подключение по мобильной сети или Wi-Fi недоступно. Google Сообщения должны быть выбраны в качестве мессенджера по умолчанию."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Спутниковый SOS не поддерживается"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Функция недоступна на этом устройстве."</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Спутниковый SOS не настроен"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Проверьте подключение к интернету и повторите попытку."</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Спутниковый SOS недоступен"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Функция недоступна в этой стране или регионе."</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Спутниковый SOS не настроен"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Чтобы использовать эту функцию, необходимо выбрать Google Сообщения в качестве мессенджера по умолчанию."</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Спутниковый SOS недоступен"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Чтобы узнать, можно ли использовать спутниковый SOS в этой стране или регионе, включите настройки геолокации."</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Доступен спутниковый обмен сообщениями"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Вы можете обмениваться сообщениями по спутниковой связи, даже когда подключение к мобильной сети или Wi-Fi недоступно. Google Сообщения должны быть выбраны в качестве мессенджера по умолчанию."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Спутниковый обмен сообщениями не поддерживается"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Функция недоступна на этом устройстве."</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Спутниковый обмен сообщениями не настроен"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Проверьте подключение к интернету и повторите попытку."</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Спутниковый обмен сообщениями недоступен"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Функция недоступна в этой стране или регионе."</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Спутниковый обмен сообщениями не настроен"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Чтобы использовать эту функцию, необходимо выбрать Google Сообщения в качестве мессенджера по умолчанию."</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Спутниковый обмен сообщениями недоступен"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Чтобы узнать, можно ли обмениваться сообщениями по спутниковой связи в этой стране или регионе, включите настройки геолокации."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Настройте разблокировку по отпечатку пальца заново"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Отпечаток \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" больше нельзя распознать."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Отпечатки \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" и \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" больше нельзя распознать."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index ea404f1a6844..f150cd189f45 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"අවට ඇති අල්ට්‍රා-වයිඩ්බෑන්ඩ් උපාංග අතර සාපේක්ෂ පිහිටීම නිර්ණය කිරීමට යෙදුමට ඉඩ දීම"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"අවට Wi‑Fi උපාංග සමග අන්තර්ක්‍රියා කරන්න"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"වෙළඳ දැන්වීම් පළ කිරීමට, සම්බන්ධ වීමට සහ අවට ඇති Wi-Fi උපාංගවල සාපේක්ෂ පිහිටීම නිර්ණය කිරීමට යෙදුමට ඉඩ දෙයි"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"කැමති NFC ගෙවීම් සේවා තොරතුරු"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ලියාපදිංචි කළ ආධාර සහ ගමන් මාර්ග ගමනාන්ත වැනි කැමති nfc ගෙවීම් සේවා තොරතුරු ලබා ගැනීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ආසන්න ක්ෂේත්‍ර සන්නිවේදනය පාලනය කරන්න"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"සති අන්තය"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"සිදුවීම"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"නිදා ගනිමින්"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"බාධා නොකරන්න (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් කළමනාකරණය කරයි"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ක්‍රියාත්මකයි"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ක්‍රියාවිරහිතයි"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="END">%2$s</xliff:g> සිට <xliff:g id="START">%1$s</xliff:g> දක්වා"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ඕනෑම දින දර්ශනයක්"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> සමහර ශබ්ද නිහඬ කරමින්"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"ඔබේ උපාංගය සමගින් ගැටලුවක් ඇති අතර, ඔබේ කර්මාන්තශාලා දත්ත යළි සකසන තෙක් එය අස්ථායි විය හැකිය."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"ජංගම හෝ Wi-Fi ජාලයකින් තොරව පණිවිඩ යැවීම සහ ලැබීම"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages විවෘත කරන්න"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"එය ක්‍රියා කරන ආකාරය"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"ස්වයංක්‍රීයව ජාලය තෝරන්න\" ක්‍රියාත්මක කරන්න"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ඔබේ දුරකථනයට චන්ද්‍රිකාව සමග ක්‍රියා කරන ජාලයක් සොයා ගැනීමට හැකි වන පරිදි සැකසීම් තුළ \"ස්වයංක්‍රීයව ජාලය තෝරන්න\" ක්‍රියාත්මක කරන්න"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ක්‍රියාත්මක කරන්න"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ආපසු යන්න"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ඇඟිලි සලකුණු අගුලු හැරීම නැවත සකසන්න"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> තවදුරටත් හඳුනා ගත නොහැක."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> සහ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> තවදුරටත් හඳුනා ගත නොහැක."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index eeb308187cb6..e17e25f4734d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Povoľte aplikácii určovať relatívnu polohu medzi zariadeniami s ultraširokopásmovým pripojením v okolí"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcia so zariadeniami Wi-Fi v okolí"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Umožňuje aplikácii oznamovať a rozpoznávať relatívnu polohu zariadení Wi‑Fi v okolí a pripájať sa k nim"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferované informácie platenej služby NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Umožňuje aplikácii získavať preferované informácie platenej služby NFC, napríklad o registrovanej pomoci a trasách k cieľu."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ovládať technológiu NFC"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Udalosť"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánok"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Režim bez vyrušení (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Spravované aplikáciou <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Zapnuté"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuté"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Ľubovoľný kalendár"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypína niektoré zvuky"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Vo vašom zariadení došlo k internému problému. Môže byť nestabilné, kým neobnovíte jeho výrobné nastavenia."</string>
@@ -2428,15 +2432,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Odosielajte a prijímajte správy bez mobilnej siete či siete Wi‑Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvoriť Správy"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ako to funguje"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Zapnite Vyberať sieť automaticky"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Zapnite v Nastaveniach možnosť Vyberať sieť automaticky, aby telefón mohol nájsť sieť, ktorá spolupracuje so satelitom"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Zapnúť"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Prejsť späť"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Znova nastavte odomknutie odtlačkom prsta"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> sa už nedari rozpoznať."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> sa už nedari rozpoznať."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 514ad1656760..da9f1c189610 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikaciji dovoli, da določi relativno oddaljenost med napravami UWB v bližini."</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"komunikacija z napravami Wi‑Fi v bližini"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji dovoljuje objavljanje in določanje relativnega položaja naprav Wi‑Fi v bližini ter povezovanje z njimi."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Podatki o prednostni storitvi za plačevanje prek povezave NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Aplikaciji omogoča pridobivanje podatkov o prednostni storitvi za plačevanje prek povezave NFC, kot so registrirani pripomočki in cilj preusmeritve."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"nadzor nad komunikacijo s tehnologijo bližnjega polja"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Konec tedna"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Dogodek"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spanje"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ne moti (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Vklopljeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izklopljeno"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> do <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kateri koli koledar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izklaplja nekatere zvoke"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Vaša naprava ima notranjo napako in bo morda nestabilna, dokler je ne ponastavite na tovarniške nastavitve."</string>
@@ -2428,15 +2432,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Pošiljanje in prejemanje sporočil brez mobilnega omrežja ali omrežja Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Odpri Sporočila"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako deluje"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Vklopite »Samodejno izberi omrežje«"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"V nastavitvah vklopite možnost »Samodejno izberi omrežje«, da bo telefon lahko našel omrežje, ki deluje s satelitsko povezavo"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Vklopi"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazaj"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS prek satelita je zdaj na voljo"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Reševalnim službam lahko pošljete sporočilo, če ni povezave z mobilnim omrežjem ali omrežjem Wi-Fi. Google Sporočila morajo biti privzeta aplikacija za sporočanje."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS prek satelita ni podprt"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS prek satelita ni podprt v tej napravi"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS prek satelita ni nastavljen"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Zagotovite, da imate vzpostavljeno internetno povezavo, in znova poskusite nastaviti"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS prek satelita ni na voljo"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS prek satelita ni na voljo v tej državi ali regiji"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS prek satelita ni nastavljen"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Če želite pošiljati satelitska sporočila, nastavite Google Sporočila kot privzeto aplikacijo za sporočanje"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS prek satelita ni na voljo"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Če želite preveriti, ali je SOS prek satelita na voljo v tej državi ali regiji, vklopite nastavitve lokacije"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satelitska sporočila so na voljo"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Satelitska sporočila lahko pošljete, če ni povezave z mobilnim omrežjem ali omrežjem Wi-Fi. Google Sporočila morajo biti privzeta aplikacija za sporočanje."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satelitska sporočila niso podprta"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satelitska sporočila niso podprta v tej napravi"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satelitska sporočila niso nastavljena"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Zagotovite, da imate vzpostavljeno internetno povezavo, in znova poskusite nastaviti"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satelitska sporočila niso na voljo"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satelitska sporočila niso na voljo v tej državi ali regiji"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satelitska sporočila niso nastavljena"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Če želite pošiljati satelitska sporočila, nastavite Google Sporočila kot privzeto aplikacijo za sporočanje"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satelitska sporočila niso na voljo"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Če želite preveriti, ali so satelitska sporočila na voljo v tej državi ali regiji, vklopite nastavitve lokacije"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vnovična nastavitev odklepanja s prstnim odtisom"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Odtisa »<xliff:g id="FINGERPRINT">%s</xliff:g>« ni več mogoče prepoznati."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Odtisov »<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>« in »<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>« ni več mogoče prepoznati."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 8d535b9a9b8d..1b8d0a0095f3 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Lejo që aplikacioni të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"të ndërveprojë me pajisjet Wi-Fi në afërsi"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lejon që aplikacioni të reklamojë, të lidhet dhe të përcaktojë pozicionin përkatës të pajisjeve Wi-Fi në afërsi"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacionet për shërbimin e preferuar të pagesës me NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lejon aplikacionin të marrë informacione për shërbimin e preferuar të pagesës me NFC si p.sh. ndihmat e regjistruara dhe destinacionin e itinerarit."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrollo \"Komunikimin e fushës në afërsi\" NFC"</string>
@@ -1663,7 +1667,7 @@
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kufjet"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistemi"</string>
- <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja e \"bluetooth-it\""</string>
+ <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja e Bluetooth-it"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"Ekran wireless"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"Transmeto"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"Lidhu me pajisjen"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fundjava"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ngjarje"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Në gjumë"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Mos shqetëso (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Menaxhohet nga <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktivizuar"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Çaktivizuar"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Çdo kalendar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> po çaktivizon disa tinguj"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Ka një problem të brendshëm me pajisjen tënde. Ajo mund të jetë e paqëndrueshme derisa të rivendosësh të dhënat në gjendje fabrike."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Dërgo dhe merr mesazhe pa një rrjet celular ose Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Hap \"Mesazhet\""</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Si funksionon"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Aktivizo \"Zgjidh automatikisht rrjetin\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Aktivizo \"Zgjidh automatikisht rrjetin\" te \"Cilësimet\" që telefoni yt të mund të gjejë një rrjet që funksionon me satelitin"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktivizo"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Kthehu prapa"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguro përsëri \"Shkyçjen me gjurmën e gishtit\""</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nuk mund të njihet më."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dhe <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nuk mund të njihen më."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index af75a5c135e3..bdf557173391 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -613,6 +613,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозвољава апликацији да одређује релативну раздаљину између уређаја ултра-широког појаса у близини"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"интеракција са WiFi уређајима у близини"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозвољава апликацији да се оглашава, повезује и утврђује релативну позицију WiFi уређаја у близини"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информације о жељеној NFC услузи за плаћање"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозвољава апликацији да преузима информације о жељеној NFC услузи за плаћање, попут регистрованих идентификатора апликација и одредишта преусмеравања."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"контрола комуникације у ужем пољу (Near Field Communication)"</string>
@@ -1939,13 +1943,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Догађај"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спавање"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Не узнемиравај (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Управља: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Укључено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Искључено"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Било који календар"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> искључује неке звуке"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Дошло је до интерног проблема у вези са уређајем и можда ће бити нестабилан док не обавите ресетовање на фабричка подешавања."</string>
@@ -2427,15 +2431,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Шаљите и примајте поруке без мобилне или WiFi мреже"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отвори Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Принцип рада"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Укључите опцију Аутоматски изабери мрежу"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Укључите опцију Аутоматски изабери мрежу у Подешавањима да би телефон могао да пронађе мрежу која ради са сателитом"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Укључи"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Хитна помоћ преко сателита је сада доступна"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Можете да шаљете поруке хитним службама ако немате приступ мобилној ни WiFi мрежи. Google Messages мора да буде подразумевана апликација за размену порука."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Хитна помоћ преко сателита није подржана"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Хитна помоћ преко сателита није подржанa на овом уређају"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Хитна помоћ преко сателита није подешена"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Проверите да ли сте повезани на интернет и пробајте поново да подесите"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Хитна помоћ преко сателита није доступна"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Хитна помоћ преко сателита није доступна у овој земљи или региону"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Хитна помоћ преко сателита није подешена"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Да бисте слали поруке преко сателита, подесите Google Messages као подразумевану апликацију за размену порука"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Хитна помоћ преко сателита није доступна"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Да бисте проверили да ли је хитна помоћ преко сателита доступна у овој земљи или региону, укључите подешавања локације"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Размена порука преко сателита је доступна"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Можете да шаљете поруке преко сателита ако немате приступ мобилној ни WiFi мрежи. Google Messages мора да буде подразумевана апликација за размену порука."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Размена порука преко сателита није подржана"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Размена порука преко сателита није подржана на овом уређају"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Размена порука преко сателита није подешена"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Проверите да ли сте повезани на интернет и пробајте поново да подесите"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Размена порука преко сателита није доступна"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Размена порука преко сателита није доступна у овој земљи или региону"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Размена порука преко сателита није подешена"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Да бисте слали поруке преко сателита, подесите Google Messages као подразумевану апликацију за размену порука"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Размена порука преко сателита није доступна"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Да бисте проверили да ли је размена порука преко сателита доступна у овој земљи или региону, укључите подешавања локације"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поново подесите откључавање отиском прста"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> више не може да се препозна."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> више не могу да се препознају."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 0cff2e507710..8bef5ca8a842 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillåt att appen fastställer den relativa positionen mellan Ultra Wideband-enheter i närheten"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagera med wifi-enheter i närheten"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Tillåter appen att sända ut till, ansluta till och fastställa relativ position för wifi-enheter i närheten"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information kopplad till standardtjänsten för NFC-betalning"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillåter att appen hämtar information kopplad till standardtjänsten för NFC-betalning, till exempel registrerade hjälpmedel och ruttdestinationer."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrollera närfältskommunikationen"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"I helgen"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Händelse"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Stör ej (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Hanteras av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>–<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> till <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Alla kalendrar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> stänger av vissa ljud"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Ett internt problem har uppstått i enheten, och det kan hända att problemet kvarstår tills du återställer standardinställningarna."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Skicka och ta emot meddelanden utan ett mobil- eller wifi-nätverk"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Öppna Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Så fungerar det"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Aktivera Välj nätverk automatiskt"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Aktivera Välj nätverk automatiskt i inställningarna så att telefonen kan hitta ett nätverk som fungerar med satellit"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktivera"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Tillbaka"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS-larm via satellit är nu tillgängligt"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Du kan skicka meddelanden till räddningstjänsten om det inte finns någon mobil- eller wifi-anslutning. Google Messages måste vara din standardapp för meddelanden."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS-larm via satellit stöds inte"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS-larm via satellit stöds inte på den här enheten"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS-larm via satellit har inte konfigurerats"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Kontrollera att enheten är ansluten till internet och försök igen"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS-larm via satellit är inte tillgängligt"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS-larm via satellit är inte tillgängligt i det här landet eller den här regionen"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS-larm via satellit har inte konfigurerats"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Ställ in Google Messages som standardapp för meddelanden om du vill skicka meddelanden via satellit"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS-larm via satellit är inte tillgängligt"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Aktivera platsinställningar för att kontrollera om SOS-larm via satellit är tillgängligt i det här landet eller den här regionen"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellitmeddelanden är tillgängliga"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Du kan skicka meddelanden via satellit om det inte finns någon mobil- eller wifi-anslutning. Google Messages måste vara din standardapp för meddelanden."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellitmeddelanden stöds inte"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellitmeddelanden stöds inte på den här enheten"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellitmeddelanden har inte ställts in"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Kontrollera att enheten är ansluten till internet och försök igen"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellitmeddelanden är inte tillgängliga"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellitmeddelanden är inte tillgängliga i det här landet eller den här regionen"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellitmeddelanden har inte ställts in"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Ställ in Google Messages som standardapp för meddelanden om du vill skicka meddelanden via satellit"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellitmeddelanden är inte tillgängliga"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Aktivera platsinställningar för att kontrollera om satellitmeddelanden är tillgängliga i det här landet eller den här regionen"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurera fingeravtryckslås igen"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Det går inte längre att känna igen <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Det går inte längre att känna igen <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> och <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index bf5189261e8f..010415eaf313 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ruhusu programu ibainishe nafasi kati ya vifaa vyenye Bendi Pana Zaidi vilivyo karibu"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"tumia vifaa vya Wi‑Fi vilivyo karibu"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Huruhusu programu kutangaza, kuunganisha na kubaini mahali palipokadiriwa vilipo vifaa vya Wi-Fi vilivyo karibu"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maelezo ya Huduma Inayopendelewa ya Malipo ya NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Huruhusu programu kupata maelezo ya huduma inayopendelewa ya malipo ya nfc kama vile huduma zilizosajiliwa na njia."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kudhibiti Mawasiliano ya Vifaa Vilivyokaribu (NFC)"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wikendi"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tukio"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Kulala"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Usinisumbue (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Inadhibitiwa na <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Imewashwa"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Imezimwa"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> hadi <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Kalenda yoyote"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> inazima baadhi ya sauti"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Kuna hitilafu ya ndani ya kifaa chako, na huenda kisiwe thabiti mpaka urejeshe mipangilio ya kiwandani."</string>
@@ -2405,7 +2409,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Muundo wa kibodi umewekwa kuwa <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Gusa ili ubadilishe."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Mipangilio ya kibodi halisi imewekwa"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Gusa ili uangalie kibodi"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Faragha"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Sehemu ya Faragha"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Nakala"</string>
<string name="profile_label_work" msgid="3495359133038584618">"Kazini"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Wa 2 wa Kazini"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Tuma na upokee ujumbe bila kutumia mtandao wa simu wala Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Fungua Programu ya Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Utaratibu wake"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Washa kipengele cha \"Chagua mtandao kiotomatiki\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Washa kipengele cha \"Chagua mtandao kiotomatiki\" katika Mipangilio ili simu yako iweze kupata mtandao unaotumia setilaiti"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Washa"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Rudi nyuma"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Weka tena mipangilio ya Kufungua kwa Alama ya Kidole"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> haitambuliki tena."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> na <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> havitambuliki tena."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 8ff19a98f6d8..1915f2982611 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"அருகிலுள்ள அல்ட்ரா-வைடுபேண்ட் சாதனங்களுக்கிடையிலான தூரத்தைத் தீர்மானிக்க ஆப்ஸை அனுமதிக்கும்"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"அருகிலுள்ள வைஃபை சாதனங்களுடன் தொடர்பு கொள்ளுதல்"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"அருகிலுள்ள வைஃபை சாதனங்களைத் தெரியப்படுத்தவும் இணைக்கவும் இருப்பிடத்தைத் தீர்மானிக்கவும் இது ஆப்ஸை அனுமதிக்கும்"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்கள்"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"பதிவுசெய்யப்பட்ட கருவிகள், சேருமிடத்திற்கான வழி போன்ற விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்களைப் பெற ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"குறுகிய இடைவெளி தகவல்பரிமாற்றத்தைக் கட்டுப்படுத்துதல்"</string>
@@ -1937,14 +1941,14 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"வார இரவு"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"வார இறுதி"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"நிகழ்வு"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"உறக்கத்தில்"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"உறங்குதல்"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"தொந்தரவு செய்ய வேண்டாம் (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"நிர்வகிப்பது: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ஆன்"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ஆஃப்"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> முதல் <xliff:g id="END">%2$s</xliff:g> வரை"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ஏதேனும் கேலெண்டர்"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> சில ஒலிகளை முடக்குகிறது"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது, அதனை ஆரம்பநிலைக்கு மீட்டமைக்கும் வரை நிலையற்று இயங்கலாம்."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் மெசேஜ்களை அனுப்பலாம், பெறலாம்"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"இது செயல்படும் விதம்"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"நெட்வொர்க்கைத் தானாகத் தேர்ந்தெடு\" என்பதை இயக்குங்கள்"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"அமைப்புகளில் \"நெட்வொர்க்கைத் தானாகத் தேர்ந்தெடு\" என்பதை இயக்கினால் செயற்கைக்கோள் மூலம் இயங்கும் நெட்வொர்க்கை உங்கள் மொபைல் கண்டறிய முடியும்"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"இயக்கு"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"பின்செல்"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"கைரேகை அன்லாக் அம்சத்தை மீண்டும் அமையுங்கள்"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>ஐ இனி அடையாளம் காண முடியாது."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ஆகியவற்றை இனி அடையாளம் காண முடியாது."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 1d9e00c012bb..6ac81642d41f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"సమీపంలోని అల్ట్రా-వైడ్‌బ్యాండ్ పరికరాల మధ్య సాపేక్ష స్థానాన్ని నిర్ణయించడానికి యాప్‌ను అనుమతించండి"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"సమీపంలోని Wi-Fi పరికరాలతో ఇంటరాక్ట్ చేస్తుంది"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"అడ్వర్టయిజ్, కనెక్ట్ చేయడానికి, సమీపంలోని Wi-Fi పరికరాల సంబంధిత పొజిషన్‌ను నిర్ణయించడానికి యాప్‌ను అనుమతిస్తుంది"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారం"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారాన్ని, అంటే రిజిస్టర్ చేయబడిన సహాయక సాధనాలు, మార్గం, గమ్యస్థానం వంటి వాటిని పొందేందుకు యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"సమీప క్షేత్ర కమ్యూనికేషన్‌ను నియంత్రించడం"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"వారాంతం"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ఈవెంట్"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"స్లీప్ మోడ్"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"అంతరాయం కలిగించవద్దు (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ద్వారా మేనేజ్ చేయబడుతోంది"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ఆన్‌లో ఉంది"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ఆఫ్‌లో ఉంది"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> నుండి <xliff:g id="END">%2$s</xliff:g> వరకు"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ఏదైనా క్యాలెండర్"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"మొబైల్ లేదా Wi-Fi నెట్‌వర్క్ లేకుండా మెసేజ్‌లను పంపండి, స్వీకరించండి"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messagesను తెరవండి"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ఇది ఎలా పని చేస్తుంది"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"నెట్‌వర్క్‌ను ఆటోమేటిక్‌గా ఎంచుకోండి\" అనే ఆప్షన్‌ను ఆన్ చేయండి"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"సెట్టింగ్‌లలో \"నెట్‌వర్క్‌ను ఆటోమేటిక్‌గా ఎంచుకోండి\" అనే ఆప్షన్‌ను ఆన్ చేయండి, తద్వారా మీ ఫోన్ శాటిలైట్‌తో పనిచేసే నెట్‌వర్క్‌ను కనుగొనగలదు"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ఆన్ చేయండి"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"వెనుకకు వెళ్లండి"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్‌లో ఉంది..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్‌లో ఉంది..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"వేలిముద్ర అన్‌లాక్‌ను మళ్లీ సెటప్ చేయండి"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g>‌ను ఇకపై గుర్తించడం సాధ్యం కాదు."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>‌లను ఇకపై గుర్తించడం సాధ్యం కాదు."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 90a90ee9ab18..763eb6266198 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"อนุญาตให้แอประบุตำแหน่งสัมพันธ์ระหว่างอุปกรณ์ที่ใช้แถบความถี่กว้างยิ่งยวดซึ่งอยู่ใกล้เคียง"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"โต้ตอบกับอุปกรณ์ Wi-Fi ที่อยู่ใกล้เคียง"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"อนุญาตให้แอปแสดงข้อมูล เชื่อมต่อ และระบุตำแหน่งซึ่งสัมพันธ์กับอุปกรณ์ Wi-Fi ที่อยู่ใกล้เคียง"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ข้อมูลบริการชำระเงิน NFC ที่ต้องการ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"อนุญาตให้แอปรับข้อมูลบริการชำระเงิน NFC ที่ต้องการ เช่น รหัสแอป (AID) ที่ลงทะเบียนและปลายทางของเส้นทาง"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ควบคุม Near Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"สุดสัปดาห์"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"กิจกรรม"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"นอนหลับ"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ห้ามรบกวน (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"จัดการโดย <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"เปิด"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ปิด"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>ถึง<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"ปฏิทินทั้งหมด"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> กำลังปิดเสียงบางรายการ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง อุปกรณ์อาจทำงานไม่เสถียรจนกว่าคุณจะรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"รับและส่งข้อความโดยไม่ต้องใช้เครือข่ายมือถือหรือ Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"เปิด Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"วิธีการทำงาน"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"เปิด \"เลือกเครือข่ายโดยอัตโนมัติ\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"เปิด \"เลือกเครือข่ายโดยอัตโนมัติ\" ในการตั้งค่าเพื่อให้โทรศัพท์ค้นหาเครือข่ายที่ใช้งานร่วมกับดาวเทียมได้"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"เปิด"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ย้อนกลับ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS ดาวเทียมพร้อมใช้งานแล้ว"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"คุณส่งข้อความหาบริการช่วยเหลือฉุกเฉินได้ในกรณีที่ไม่มีเครือข่ายมือถือหรือ Wi-Fi โดยที่แอปรับส่งข้อความเริ่มต้นของคุณจะต้องเป็น Google Messages"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ไม่รองรับ SOS ดาวเทียม"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"อุปกรณ์นี้ไม่รองรับ SOS ดาวเทียม"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ไม่ได้ตั้งค่า SOS ดาวเทียม"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ตรวจสอบว่าคุณได้เชื่อมต่ออินเทอร์เน็ตแล้ว และลองตั้งค่าอีกครั้ง"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS ดาวเทียมไม่พร้อมใช้งาน"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS ดาวเทียมไม่พร้อมใช้งานในประเทศหรือภูมิภาคนี้"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ไม่ได้ตั้งค่า SOS ดาวเทียม"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"หากต้องการรับส่งข้อความผ่านดาวเทียม ให้ตั้ง Google Messages เป็นแอปรับส่งข้อความเริ่มต้น"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS ดาวเทียมไม่พร้อมใช้งาน"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"หากต้องการตรวจสอบว่า SOS ดาวเทียมพร้อมให้บริการในประเทศหรือภูมิภาคนี้หรือไม่ ให้เปิดการตั้งค่าตำแหน่ง"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"การรับส่งข้อความผ่านดาวเทียมพร้อมใช้งาน"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"คุณรับส่งข้อความผ่านดาวเทียมได้ในกรณีที่ไม่มีเครือข่ายมือถือหรือ Wi-Fi โดยที่แอปรับส่งข้อความเริ่มต้นของคุณจะต้องเป็น Google Messages"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ไม่รองรับการรับส่งข้อความผ่านดาวเทียม"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"อุปกรณ์นี้ไม่รองรับการรับส่งข้อความผ่านดาวเทียม"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ไม่ได้ตั้งค่าการรับส่งข้อความผ่านดาวเทียม"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ตรวจสอบว่าคุณได้เชื่อมต่ออินเทอร์เน็ตแล้ว และลองตั้งค่าอีกครั้ง"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"การรับส่งข้อความผ่านดาวเทียมไม่พร้อมใช้งาน"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"การรับส่งข้อความผ่านดาวเทียมไม่พร้อมใช้งานในประเทศหรือภูมิภาคนี้"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"ไม่ได้ตั้งค่าการรับส่งข้อความผ่านดาวเทียม"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"หากต้องการรับส่งข้อความผ่านดาวเทียม ให้ตั้ง Google Messages เป็นแอปรับส่งข้อความเริ่มต้น"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"การรับส่งข้อความผ่านดาวเทียมไม่พร้อมใช้งาน"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"หากต้องการตรวจสอบว่าการรับส่งข้อความผ่านดาวเทียมพร้อมให้บริการในประเทศหรือภูมิภาคนี้หรือไม่ ให้เปิดการตั้งค่าตำแหน่ง"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ตั้งค่าการปลดล็อกด้วยลายนิ้วมืออีกครั้ง"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"ระบบไม่จดจำ <xliff:g id="FINGERPRINT">%s</xliff:g> อีกต่อไป"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"ระบบไม่จดจำ <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> และ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> อีกต่อไป"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index f4df0e2ed7ae..b44faae4e84f 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Payagan ang app na tukuyin ang relatibong posisyon sa pagitan ng mga kalapit na Ultra-Wideband device"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"makipag-ugnayan sa mga kalapit na Wi‑Fi device"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Nagbibigay-daan sa app na i-advertise ang, kumonekta sa, at tukuyin ang nauugnay na posisyon ng mga kalapit na Wi‑Fi device"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Impormasyon sa Gustong NFC na Serbisyo sa Pagbabayad"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Pinapayagan ang app na makakuha ng impormasyon sa gustong nfc na serbisyo sa pagbabayad tulad ng mga nakarehistrong application ID at destinasyon ng ruta."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrolin ang Near Field Communication"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Pag-sleep"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Huwag Istorbohin (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Pinapamahalaan ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Naka-on"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Naka-off"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> patungong <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Anumang kalendaryo"</string>
<string name="muted_by" msgid="91464083490094950">"Minu-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g> ang ilang tunog"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"May internal na problema sa iyong device, at maaaring hindi ito maging stable hanggang sa i-reset mo ang factory data."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Magpadala at tumanggap ng mga mensahe nang walang mobile o Wi-Fi network"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buksan ang Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Paano ito gumagana"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"I-on ang \"Awtomatikong pumili ng network\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"I-on ang \"Awtomatikong piliin ang network\" sa Mga Setting para mahanap ng iyong telepono ang isang network na gumagana sa satellite"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"I-on"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Bumalik"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Available na ang SOS gamit ang Satellite"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Puwede kang magpadala ng mensahe sa mga serbisyong pang-emergency kung walang mobile o Wi-Fi network. Dapat Google Messages ang default mong app sa pagmemensahe."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Hindi sinusuportahan ang SOS gamit ang satellite"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Hindi sinusuportahan ang SOS gamit ang satellite sa device na ito"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Hindi naka-set up ang SOS gamit ang satellite"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Tiyaking nakakonekta ka sa internet at subukang mag-set up ulit"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Hindi available ang SOS gamit ang satellite"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Hindi available ang SOS gamit ang satellite sa bansa o rehiyong ito"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Hindi naka-set up ang SOS gamit ang satellite"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para magpadala ng mensahe sa pamamagitan ng satellite, itakda ang Google Messages bilang iyong default na app sa pagmemensahe"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Hindi available ang SOS gamit ang satellite"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para tingnan kung available ang SOS gamit ang satellite sa bansa o rehiyong ito, i-on ang mga setting ng lokasyon"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Available ang satellite messaging"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Puwede kang magpadala ng mensahe sa pamamagitan ng satellite kung walang mobile o Wi-Fi network. Dapat Google Messages ang default mong app sa pagmemensahe."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Hindi sinusuportahan ang satellite messaging"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Hindi sinusuportahan ang satellite messaging sa device na ito"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Hindi naka-set up ang satellite messaging"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Tiyaking nakakonekta ka sa internet at subukang mag-set up ulit"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Hindi available ang satellite messaging"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Hindi available ang satellite messaging sa bansa o rehiyong ito"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Hindi naka-set up ang satellite messaging"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para magpadala ng mensahe sa pamamagitan ng satellite, itakda ang Google Messages bilang iyong default na app sa pagmemensahe"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Hindi available ang satellite messaging"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para tingnan kung available ang satellite messaging sa bansa o rehiyong ito, i-on ang mga setting ng lokasyon"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"I-set up ulit ang Pag-unlock Gamit ang Fingerprint"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Hindi na makilala ang <xliff:g id="FINGERPRINT">%s</xliff:g>."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Hindi na makilala ang <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> at <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string>
@@ -2453,7 +2477,7 @@
<string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
<string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musika"</string>
- <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendaryo"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
<string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mga Mapa"</string>
<string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Mga Application"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 77c3ee24ba59..e54b8999a1dc 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Uygulamanın, yakındaki Ultra Geniş Bant cihazların birbirine göre konumunu belirlemesine izin verin"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yakındaki kablosuz cihazlarla etkileşim kur"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Uygulamanın reklam sunmasına, bağlanmasına ve yakındaki kablosuz cihazların göreli konumunu belirlemesine izin verir"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tercih Edilen NFC Ödeme Hizmeti Bilgileri"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Uygulamaya, kayıtlı yardımlar ve rota hedefi gibi tercih edilen NFC ödeme hizmeti bilgilerini alma izni verir."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Yakın Alan İletişimini denetle"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hafta sonu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Etkinlik"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyku"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Rahatsız Etmeyin (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarafından yönetiliyor"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Açık"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kapalı"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Tüm takvimler"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bazı sesleri kapatıyor"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Cihazınızla ilgili dahili bir sorun oluştu ve fabrika verilerine sıfırlama işlemi gerçekleştirilene kadar kararsız çalışabilir."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil veya kablosuz ağ kullanmadan mesaj gönderip alın"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajlar\'ı aç"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"İşleyiş şekli"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"Ağı otomatik seç\"i etkinleştirin"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Telefonunuzun uyduyla çalışan bir ağ bulabilmesi için Ayarlar\'da \"Ağı otomatik seç\"i etkinleştirin"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Etkinleştir"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Geri dön"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Parmak İzi Kilidi\'ni tekrar kurun"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> artık tanınamayacak."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ve <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> artık tanınamayacak."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 2f1a1d5987e2..344cb28afcb2 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -614,6 +614,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"З цим дозволом додаток може визначати відстань між розташованими поблизу пристроями з надширокосмуговим зв’язком"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"взаємодіяти з пристроями Wi‑Fi поблизу"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Додаток може виявляти пристрої Wi‑Fi поблизу, підключатися до них і визначати їх відносне розташування"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Використання інформації з платіжного NFC-сервісу"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозволяє додатку отримувати доступ до інформації потрібного платіжного NFC-сервісу (наприклад, пов\'язаних ідентифікаторів чи даних про маршрутизацію трансакцій)."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"контрол. Near Field Communication"</string>
@@ -1575,7 +1579,7 @@
<string name="sync_really_delete" msgid="5657871730315579051">"Видалити елементи"</string>
<string name="sync_undo_deletes" msgid="5786033331266418896">"Відмінити видалення"</string>
<string name="sync_do_nothing" msgid="4528734662446469646">"Наразі нічого не робити"</string>
- <string name="choose_account_label" msgid="5557833752759831548">"Вибрати обліковий запис"</string>
+ <string name="choose_account_label" msgid="5557833752759831548">"Вибрати облік. запис"</string>
<string name="add_account_label" msgid="4067610644298737417">"Додати обліковий запис"</string>
<string name="add_account_button_label" msgid="322390749416414097">"Додати облік. запис"</string>
<string name="number_picker_increment_button" msgid="7621013714795186298">"Збільшити"</string>
@@ -1940,13 +1944,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"На вихідних"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Подія"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Під час сну"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Не турбувати (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Керує додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Увімкнено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Вимкнено"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"З усіх календарів"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає деякі звуки"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string>
@@ -2428,15 +2432,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Надсилайте й отримуйте текстові повідомлення без мобільної мережі або Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Відкрийте Повідомлення"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як це працює"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Увімкніть опцію \"Вибирати мережу автоматично\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"У налаштуваннях увімкніть опцію \"Вибирати мережу автоматично\", щоб телефон міг знайти мережу, яка працює через супутник"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Увімкнути"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Налаштуйте розблокування відбитком пальця повторно"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Відбиток пальця <xliff:g id="FINGERPRINT">%s</xliff:g> більше не розпізнається."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Відбитки пальців <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> і <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> більше не розпізнаються."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 6c78283d9494..45e1a2347dbb 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ایپ کو قریبی الٹرا وائڈ بینڈ آلات کے مابین متعلقہ پوزیشن کا تعین کرنے کی اجازت دیں"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏قریبی Wi-Fi آلات کے ساتھ تعامل کریں"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏ایپ کو اشتہار دینے، منسلک کرنے اور قریبی Wi-Fi آلات کی متعلقہ پوزیشن کا تعین کرنے کی اجازت دیتا ہے"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏ترجیح شدہ NFC ادائیگی کی سروس کی معلومات"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏ایپ کو رجسٹرشدہ ایڈز اور روٹ ڈسٹنیشن جیسی ترجیح شدہ nfc ادائیگی سروس کی معلومات حاصل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"‏Near Field کمیونیکیشن کنٹرول کریں"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ویک اینڈ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ایونٹ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"سونا"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"ڈسٹرب نہ کریں (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے زیر انتظام ہے"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"آن ہے"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"آف ہے"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"، "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> تا <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"کوئی بھی کیلنڈر"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"آپ کے آلہ میں ایک داخلی مسئلہ ہے اور جب تک آپ فیکٹری ڈیٹا کو دوبارہ ترتیب نہیں دے دیتے ہیں، ہوسکتا ہے کہ یہ غیر مستحکم رہے۔"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏موبائل یا Wi-Fi نیٹ ورک کے بغیر پیغامات بھیجیں اور موصول کریں"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"پیغامات ایپ کو کھولیں"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"اس کے کام کرنے کا طریقہ"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"\"خودکار طور پر نیٹ ورک منتخب کریں\" کو آن کریں"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"ترتیبات میں \"خودکار طور پر نیٹ ورک منتخب کریں\" کو آن کریں تاکہ آپ کا فون سیٹلائٹ کے ساتھ کام کرنے والے نیٹ ورک کو تلاش کر سکے"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"آن کریں"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"واپس جائیں"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"‏سیٹلائٹ SOS اب دستیاب ہے"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"‏موبائل یا Wi-Fi نیٹ ورک نہ ہونے پر آپ ایمرجنسی سروسز کو پیغام بھیج سکتے ہیں۔ ‫Google پیغامات آپ کی ڈیفالٹ پیغام رسانی ایپ ہونی چاہیے۔"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"‏سیٹلائٹ SOS تعاون یافتہ نہیں ہے"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"‏سیٹلائٹ SOS اس آلے پر تعاون یافتہ نہیں ہے"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"‏سیٹلائٹ SOS سیٹ اپ نہیں ہے"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"یقینی بنائیں کہ آپ انٹرنیٹ سے منسلک ہیں اور دوبارہ سیٹ اپ کرنے کی کوشش کریں"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"‏سیٹلائٹ SOS دستیاب نہیں ہے"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"‏سیٹلائٹ SOS اس ملک یا علاقے میں دستیاب نہیں ہے"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"‏سیٹلائٹ SOS سیٹ اپ نہیں ہے"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"‏سیٹلائٹ کے ذریعے پیغام بھیجنے کے لیے، Google پیغامات کو اپنی ڈیفالٹ پیغام رسانی ایپ کے طور پر سیٹ کریں"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"‏سیٹلائٹ SOS دستیاب نہیں ہے"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"‏یہ چیک کرنے کے لیے کہ آیا اس ملک یا علاقے میں سیٹلائٹ SOS دستیاب ہے، مقام کی ترتیبات کو آن کریں"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"سیٹلائٹ پیغام رسانی دستیاب ہے"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"‏موبائل یا Wi-Fi نیٹ ورک نہ ہونے پر آپ سیٹلائٹ کے ذریعے پیغام بھیج سکتے ہیں۔ ‫Google پیغامات آپ کی ڈیفالٹ پیغام رسانی ایپ ہونی چاہیے۔"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"سیٹلائٹ پیغام رسانی تعاون یافتہ نہیں ہے"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"سیٹلائٹ پیغام رسانی اس آلے پر تعاون یافتہ نہیں ہے"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"سیٹلائٹ پیغام رسانی سیٹ اپ نہیں ہے"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"یقینی بنائیں کہ آپ انٹرنیٹ سے منسلک ہیں اور دوبارہ سیٹ اپ کرنے کی کوشش کریں"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"سیٹلائٹ پیغام رسانی دستیاب نہیں ہے"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"سیٹلائٹ پیغام رسانی اس ملک یا علاقے میں دستیاب نہیں ہے"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"سیٹلائٹ پیغام رسانی سیٹ اپ نہیں ہے"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"‏سیٹلائٹ کے ذریعے پیغام بھیجنے کے لیے، Google پیغامات کو اپنی ڈیفالٹ پیغام رسانی ایپ کے طور پر سیٹ کریں"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"سیٹلائٹ پیغام رسانی دستیاب نہیں ہے"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"یہ چیک کرنے کے لیے کہ آیا اس ملک یا علاقے میں سیٹلائٹ پیغام رسانی دستیاب ہے، مقام کی ترتیبات کو آن کریں"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"فنگر پرنٹ اَن لاک کو دوبارہ سیٹ اپ کریں"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> مزید پہچانا نہیں جا سکتا۔"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> اور <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> کو مزید پہچانا نہیں جا سکتا۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index ac70b93d2248..f123bea572b1 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ilovaga yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlashga ruxsat beradi"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Yaqin-atrofdagi Wi-Fi qurilmalar bilan ishlash"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ilovaga yaqin-atrofdagi Wi-Fi qurilmalarga reklama yuborish, ulanish va ularning taxminiy joylashuvini aniqlash imkonini beradi."</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Asosiy NFC toʻlov xizmati haqidagi axborot"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Bu ilovaga asosiy NFC toʻlov xizmati haqidagi axborotni olish imkonini beradi (masalan, qayd qilingan AID identifikatorlari va marshrutning yakuniy manzili)."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFC modulini boshqarish"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Dam olish kunlari"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tadbir"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyquda"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Bezovta qilinmasin (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> tomonidan boshqariladi"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Yoniq"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Oʻchiq"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Har qanday taqvim"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ayrim tovushlarni ovozsiz qilgan"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. U zavod sozlamalari tiklanmaguncha barqaror ishlamasligi mumkin."</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil yoki Wi-Fi tarmoq blan aloqa yoʻqligida xabar yuboring va qabul qiling"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Xabarlar ilovasini ochish"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ishlash tartibi"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"“Tarmoqni avtomatik tanlash” sozlamasini yoqing"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Telefoningiz sputnik bilan ishlaydigan tarmoqni topishi uchun Sozlamalar orqali “Tarmoqni avtomatik tanlash” sozlamasini yoqing"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Yoqish"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Orqaga"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Sputnik SOS xizmati mavjud"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Agar mobil yoki Wi-Fi tarmoq boʻlmasa, favqulodda xizmatlarga xabar yuborishingiz mumkin. Google Xabarlar asosiy xabar almashinuv ilovasi boʻlishi zarur."</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Sputnik SOS ishlamaydi"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Bu qurilmada sputnik SOS ishlamaydi"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Sputnik SOS sozlanmagan"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Internetga ulanganingizni tekshiring va qayta sozlang"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Sputnik SOS mavjud emas"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Sputnik SOS bu mamlakat yoki hududda mavjud emas"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Sputnik SOS sozlanmagan"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Sunʼiy yoʻldosh orqali xabarlashish uchun Google Xabarlar ilovasini asosiy xabar almashinuv ilovasi sifatida sozlang"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Sputnik SOS mavjud emas"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Bu mamlakat yoki hududda sputnik SOS mavjudligini tekshirish uchun joylashuv sozlamalarini yoqing"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Sunʼiy yoʻldosh orqali xabarlashuv mavjud"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Agar mobil yoki Wi-Fi tarmoq boʻlmasa, sunʼiy yoʻldosh orqali xabar yuborishingiz mumkin. Google Xabarlar asosiy xabar almashinuv ilovasi boʻlishi zarur."</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Sunʼiy yoʻldosh orqali xabarlashuv ishlamaydi"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Bu qurilmada sunʼiy yoʻldosh orqali xabarlashuv ishlamaydi"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Sunʼiy yoʻldosh orqali xabarlashuv sozlanmagan"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Internetga ulanganingizni tekshiring va qayta sozlang"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Sunʼiy yoʻldosh orqali xabarlashuv mavjud emas"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Sunʼiy yoʻldosh orqali xabarlashuv ushbu mamlakat yoki hududda ishlamaydi"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Sunʼiy yoʻldosh orqali xabarlashuv sozlanmagan"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Sunʼiy yoʻldosh orqali xabarlashish uchun Google Xabarlar ilovasini asosiy xabar almashinuv ilovasi sifatida sozlang"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Sunʼiy yoʻldosh orqali xabarlashuv mavjud emas"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Bu mamlakat yoki hududda sunʼiy yoʻldosh orqali xabarlashuv mavjudligini tekshirish uchun joylashuv sozlamalarini yoqing"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmoq izi bilan ochish funksiyasini qayta sozlang"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> endi tanilmaydi."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> va <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> endi tanilmaydi."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index d4551b178568..ef50a9badc4c 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Cho phép ứng dụng xác định khoảng cách tương đối giữa các thiết bị ở gần dùng Băng tần siêu rộng"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"tương tác với các thiết bị Wi‑Fi lân cận"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Cho phép ứng dụng này thông báo, kết nối và xác định vị trí tương đối của các thiết bị Wi‑Fi lân cận"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần (NFC) được ưu tiên"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Cho phép ứng dụng nhận thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần mà bạn ưu tiên, chẳng hạn như các hình thức hỗ trợ đã đăng ký và điểm đến trong hành trình."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kiểm soát Liên lạc trường gần"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cuối tuần"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sự kiện"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ngủ"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Không làm phiền (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Do <xliff:g id="APP_NAME">%1$s</xliff:g> quản lý"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Bật"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Tắt"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> đến <xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Bất kỳ lịch nào"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> đang tắt một số âm thanh"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Đã xảy ra sự cố nội bộ với thiết bị của bạn và thiết bị có thể sẽ không ổn định cho tới khi bạn thiết lập lại dữ liệu ban đầu."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Gửi và nhận tin nhắn mà không cần mạng di động hoặc Wi-Fi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mở ứng dụng Tin nhắn"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cách hoạt động"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Bật tính năng \"Tự động chọn mạng\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Bật tính năng \"Tự động chọn mạng\" trong phần Cài đặt để điện thoại có thể tìm thấy mạng hoạt động với vệ tinh"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Bật"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Quay lại"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Thiết lập lại tính năng Mở khoá bằng vân tay"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Không nhận dạng được <xliff:g id="FINGERPRINT">%s</xliff:g> nữa."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Không nhận dạng được <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> và <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nữa."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 22f80bd2d511..e2f66fc93c08 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允许应用确定附近超宽带设备之间的相对位置"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"与附近的 WLAN 设备互动"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允许该应用向附近的 WLAN 设备进行广播、连接到这些设备以及确定这些设备的相对位置"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首选 NFC 付款服务信息"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允许应用获取首选 NFC 付款服务信息,例如注册的应用标识符和路线目的地。"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"控制近距离通信"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"周末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活动"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"勿扰 (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由<xliff:g id="APP_NAME">%1$s</xliff:g>管理"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"已启用"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已停用"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>到<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"所有日历"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正在将某些音效设为静音"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"您的设备内部出现了问题。如果不将设备恢复出厂设置,设备运行可能会不稳定。"</string>
@@ -2426,15 +2430,35 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"即使没有移动网络或 WLAN 网络,也能收发消息"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"打开“信息”应用"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"运作方式"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
- <skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
- <skip />
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"开启“自动选择网络”"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"在“设置”中开启“自动选择网络”,以便手机找到可与卫星配合使用的网络"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"开启"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"返回"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string>
+ <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"现在支持卫星紧急呼救功能"</string>
+ <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"即使没有移动网络或 WLAN 网络,您仍可以向应急服务发送消息。您必须将 Google 信息设为默认的即时通讯应用。"</string>
+ <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"不支持卫星紧急呼救功能"</string>
+ <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"此设备不支持卫星紧急呼救功能"</string>
+ <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"未设置卫星紧急呼救功能"</string>
+ <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"请确保您已联网,然后尝试重新设置"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"不支持卫星紧急呼救功能"</string>
+ <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"此国家/地区不支持卫星紧急呼救功能"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"未设置卫星紧急呼救功能"</string>
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"如需通过卫星发送消息,请将 Google 信息设为默认即时通讯应用"</string>
+ <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"不支持卫星紧急呼救功能"</string>
+ <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"若要查看此国家/地区是否支持卫星紧急呼救功能,请开启位置信息设置"</string>
+ <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"支持卫星消息功能"</string>
+ <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"即使没有移动网络或 WLAN 网络,您仍可以通过卫星发送消息。您必须将 Google 信息设为默认的即时通讯应用。"</string>
+ <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"不支持卫星消息功能"</string>
+ <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"此设备不支持卫星消息功能"</string>
+ <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"未设置卫星消息功能"</string>
+ <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"请确保您已联网,然后尝试重新设置"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"不支持卫星消息功能"</string>
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"此国家/地区不支持卫星消息功能"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"未设置卫星消息功能"</string>
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"如需通过卫星发送消息,请将 Google 信息设为默认即时通讯应用"</string>
+ <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"不支持卫星消息功能"</string>
+ <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"若要查看此国家/地区是否支持卫星消息功能,请开启位置信息设置"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新设置指纹解锁功能"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"系统无法再识别<xliff:g id="FINGERPRINT">%s</xliff:g>。"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"系统无法再识别<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>和<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 3f4c1758b1fb..a2d32805df76 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置之間的相對位置"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"與附近的 Wi‑Fi 裝置互動"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式向附近的 Wi-Fi 裝置顯示此裝置、連接這些裝置並判斷其相對位置"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"由用戶允許授權的 NFC 付款服務資訊"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得由用戶允許授權的 NFC 付款服務資訊 (如已註冊的付款輔助功能和最終付款對象)。"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活動"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"請勿騷擾 (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由<xliff:g id="APP_NAME">%1$s</xliff:g>管理"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"已開啟"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已關閉"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>至<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正將某些音效設為靜音"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"你裝置的系統發生問題,回復原廠設定後即可解決該問題。"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"在沒有流動網絡或 Wi-Fi 網絡的情況下收發短訊"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"開啟「自動選取網絡」"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"前往設定開啟「自動選取網絡」,讓手機可以尋找可使用衛星的網絡"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"開啟"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"返回"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定「指紋解鎖」功能"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"無法再辨識<xliff:g id="FINGERPRINT">%s</xliff:g>。"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"無法再辨識<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>和<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 90cede80e250..d1f8b5a289ed 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置間的相對位置"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"與鄰近的 Wi-Fi 裝置互動"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式顯示鄰近的 Wi-Fi 裝置的資料、與其連線並判斷相對位置"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首選 NFC 付費服務資訊"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得首選 NFC 付費服務資訊,例如已註冊的輔助工具和路線目的地。"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string>
@@ -640,12 +644,12 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"允許應用程式修改你的相片收藏。"</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"讀取你的媒體收藏的位置資訊"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"允許應用程式讀取你的媒體收藏的位置資訊。"</string>
- <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物特徵辨識功能"</string>
- <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物特徵辨識或螢幕鎖定功能"</string>
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物辨識功能"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物辨識或螢幕鎖定功能"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"驗證你的身分"</string>
- <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用生物特徵辨識功能驗證身分"</string>
- <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"請使用生物特徵辨識或螢幕鎖定功能驗證身分,才能繼續操作"</string>
- <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物特徵辨識硬體"</string>
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用生物辨識功能驗證身分"</string>
+ <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"請使用生物辨識或螢幕鎖定功能驗證身分,才能繼續操作"</string>
+ <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>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活動"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"零打擾 (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由「<xliff:g id="APP_NAME">%1$s</xliff:g>」管理"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"已啟用"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已停用"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">"、 "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> - <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g>到<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"任何日曆"</string>
<string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」正在關閉部分音效"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"你的裝置發生內部問題,必須將裝置恢復原廠設定才能解除不穩定狀態。"</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"即使沒有行動或 Wi-Fi 網路,還是可以收發訊息"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"開啟「自動選取網路」"</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"請前往「設定」開啟「自動選取網路」,讓手機可以找到支援衛星的網路"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"開啟"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"返回"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定指紋解鎖"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"系統無法再辨識「<xliff:g id="FINGERPRINT">%s</xliff:g>」。"</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"系統無法再辨識「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5448fcd98d2b..d1b7e510168e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -612,6 +612,10 @@
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Vumela i-app inqume indawo ehambelanayo phakathi kwamadivayisi e-Ultra-Wideband aseduze"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"xhumana namadivayisi we-Wi‑Fi aseduze"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ivumela i-app ikhangise, ixhume, futhi inqume isimo esihambisanayo samadivayisi we-Wi-Fi aseduze"</string>
+ <!-- no translation found for permlab_ranging (2854543350668593390) -->
+ <skip />
+ <!-- no translation found for permdesc_ranging (6703905535621521710) -->
+ <skip />
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ulwazi Lwesevisi Yenkokhelo Ye-NFC Okhethwayo"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ivuemela uhlelo lokusebenza ukuthola ulwazi lesevisi yenkokhelo ye-nfc njengezinsiza zokubhalisa nezindawo zomzila."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"lawula Uxhumano Lwenkambu Eseduze"</string>
@@ -1938,13 +1942,13 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Ngempelasonto"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Umcimbi"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ulele"</string>
+ <string name="zen_mode_implicit_name" msgid="177586786232302019">"Ungaphazamisi (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Iphethwe yi-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Kuvuliwe"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kuvaliwe"</string>
<string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
<string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>, <xliff:g id="END">%2$s</xliff:g>"</string>
- <!-- no translation found for zen_mode_trigger_summary_range_words (7228261413029290750) -->
- <skip />
+ <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"U-<xliff:g id="START">%1$s</xliff:g> ukuya ku-<xliff:g id="END">%2$s</xliff:g>"</string>
<string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"Noma iyiphi ikhalenda"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ithulisa eminye imisindo"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Kukhona inkinga yangaphakathi ngedivayisi yakho, futhi ingase ibe engazinzile kuze kube yilapho usetha kabusha yonke idatha."</string>
@@ -2426,15 +2430,59 @@
<string name="satellite_notification_manual_summary" msgid="901206289846283445">"Thumela futhi wamukele imilayezo ngaphandle kwenethiwekhi yeselula noma yeWiFi"</string>
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Vula Imilayezo"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Indlela esebenza ngayo"</string>
- <!-- no translation found for satellite_manual_selection_state_popup_title (8545991934926661974) -->
+ <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Vula okuthi \"Khetha inethiwekhi ngokuzenzekela\""</string>
+ <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Vula okuthi \"Khetha inethiwekhi ngokuzenzekela\" kumasethingi ukuze ifoni yakho ithole inethiwekhi esebenza nesathelayithi"</string>
+ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Vula"</string>
+ <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Iya emuva"</string>
+ <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
+ <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_message (1928101658551382450) -->
+ <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_ok (2459664752624985095) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) -->
<skip />
- <!-- no translation found for satellite_manual_selection_state_popup_cancel (973605633339469252) -->
+ <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) -->
+ <skip />
+ <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) -->
+ <skip />
+ <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) -->
+ <skip />
+ <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) -->
+ <skip />
+ <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) -->
<skip />
- <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setha Ukuvula ngesigxivizo somunwe futhi"</string>
<string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"I-<xliff:g id="FINGERPRINT">%s</xliff:g> angeke isaziwa."</string>
<string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"I-<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> kanye ne-<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> angeke isaziwa."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 092d2a72580a..e6dedce8feaf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4418,6 +4418,10 @@
<declare-styleable name="InputMethod_Subtype">
<!-- The name of the subtype. -->
<attr name="label" />
+ <!-- The layout label of the subtype.
+ {@link android.view.inputmethod.InputMethodSubtype#getLayoutDisplayName} returns the
+ value specified in this attribute. -->
+ <attr name="layoutLabel" format="reference" />
<!-- The icon of the subtype. -->
<attr name="icon" />
<!-- The locale of the subtype. This string should be a locale (for example en_US and fr_FR)
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 4d73f228ad0c..41dec3776b5c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2273,6 +2273,22 @@
<attr name="enableOnBackInvokedCallback" format="boolean"/>
<attr name="intentMatchingFlags"/>
+
+ <!-- Specifies the set of drawable resources that can be used in place
+ of an existing declared icon or banner for activities that appear
+ in the app launcher. The resource referenced must be an array of
+ drawable resources and can contain at most 500 items.
+ {@link android.content.pm.PackageManager#changeLauncherIconConfig}
+ @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
+ <attr name="alternateLauncherIcons" format="reference" />
+
+ <!-- Specifies the set of string resources that can be used in place
+ of an existing declared label for activities that appear
+ in the app launcher. The resource referenced must be an array of
+ string resources and can contain at most 500 items.
+ {@link android.content.pm.PackageManager#changeLauncherIconConfig}
+ @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
+ <attr name="alternateLauncherLabels" format="reference" />
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ac9bb93a6ffa..969ee2e16deb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1849,6 +1849,10 @@
<item>-1</item>
</integer-array>
+ <!-- Specifies the delay in milliseconds after the last user input before turning off the
+ keyboard backlight.
+ -->
+ <integer name="config_keyboardBacklightTimeoutMs">30000</integer>
<!-- An array describing the screen's backlight values corresponding to the brightness
values in the config_screenBrightnessNits array.
@@ -3087,6 +3091,12 @@
<!-- Whether UI for multi user should be shown -->
<bool name="config_enableMultiUserUI">false</bool>
+ <!-- Indicates the boot strategy in Headless System User Mode (HSUM)
+ This config has no effect if the device is not in HSUM.
+ 0 (Default) : boot to the previous foreground user if there is one, otherwise the first switchable user.
+ 1 : boot to the first switchable full user for initial boot (unprovisioned device), else to the headless system user, i.e. user 0. -->
+ <integer name="config_hsumBootStrategy">0</integer>
+
<!-- Whether to boot system with the headless system user, i.e. user 0. If set to true,
system will be booted with the headless system user, or user 0. It has no effect if device
is not in Headless System User Mode (HSUM). -->
@@ -4590,6 +4600,11 @@
exists on the device, the accessibility shortcut will be disabled by default. -->
<string name="config_defaultAccessibilityService" translatable="false"></string>
+ <!-- The component name, flattened to a string, for the default select to speak service to be
+ enabled by the accessibility keyboard shortcut. If the service with the specified component
+ name is not preinstalled then this shortcut will do nothing. -->
+ <string name="config_defaultSelectToSpeakService" translatable="false"></string>
+
<!-- URI for default Accessibility notification sound when to enable accessibility shortcut. -->
<string name="config_defaultAccessibilityNotificationSound" translatable="false"></string>
@@ -6994,6 +7009,15 @@
<string-array name="config_healthConnectMigrationKnownSigners">
</string-array>
+ <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner
+ permission for backing up HealthConnect's data and settings. The digest should be computed over the
+ DER encoding of the trusted certificate using the SHA-256 digest algorithm. -->
+ <string-array name="config_backupHealthConnectDataAndSettingsKnownSigners"/>
+ <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner
+ permission for restoring HealthConnect's data and settings. The digest should be computed over the
+ DER encoding of the trusted certificate using the SHA-256 digest algorithm. -->
+ <string-array name="config_restoreHealthConnectDataAndSettingsKnownSigners"/>
+
<!-- Package name of Health Connect data migrator application. -->
<string name="config_healthConnectMigratorPackageName"></string>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 779422a70cad..31e9913dd988 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -472,10 +472,13 @@
<integer name="config_mt_sms_polling_throttle_millis">300000</integer>
<java-symbol type="integer" name="config_mt_sms_polling_throttle_millis" />
-
<!-- The receiver class of the intent that hidden menu sends to start satellite non-emergency mode -->
<string name="config_satellite_carrier_roaming_non_emergency_session_class" translatable="false"></string>
<java-symbol type="string" name="config_satellite_carrier_roaming_non_emergency_session_class" />
+ <!-- Whether to show the system notification to users whenever there is a change
+ in the satellite availability state at the current location. -->
+ <bool name="config_satellite_should_notify_availability">false</bool>
+ <java-symbol type="bool" name="config_satellite_should_notify_availability" />
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 0c28ea406aa2..b6436d0b30a5 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -125,6 +125,12 @@
<public name="supplementalDescription"/>
<!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
<public name="intentMatchingFlags"/>
+ <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) -->
+ <public name="layoutLabel"/>
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
+ <public name="alternateLauncherIcons"/>
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) -->
+ <public name="alternateLauncherLabels"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/res/res/values/stoppable_fgs_system_apps.xml b/core/res/res/values/stoppable_fgs_system_apps.xml
new file mode 100644
index 000000000000..06843f4b4f76
--- /dev/null
+++ b/core/res/res/values/stoppable_fgs_system_apps.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <!-- A list of system apps whose FGS can be stopped in the task manager. -->
+ <string-array translatable="false" name="stoppable_fgs_system_apps">
+ <item>com.android.virtualization.terminal</item>
+ </string-array>
+ <!-- stoppable_fgs_system_apps which is supposed to be overridden by vendor -->
+ <string-array translatable="false" name="vendor_stoppable_fgs_system_apps">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d3ef07ca8122..c13fdb17dfe3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1766,6 +1766,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=140]-->
<string name="permdesc_nearby_wifi_devices">Allows the app to advertise, connect, and determine the relative position of nearby Wi\u2011Fi devices</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
+ <string name="permlab_ranging">determine relative position between nearby devices</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
+ <string name="permdesc_ranging">Allow the app to determine relative position between nearby devices</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_preferredPaymentInfo">Preferred NFC Payment Service Information</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -6522,6 +6527,54 @@ ul.</string>
<string name="satellite_manual_selection_state_popup_cancel">Go back</string>
<!-- Initial/System provided label shown for an app which gets unarchived. [CHAR LIMIT=64]. -->
<string name="unarchival_session_app_label">Pending...</string>
+ <!-- Notification title when satellite service is available. -->
+ <string name="satellite_sos_available_notification_title">Satellite SOS is now available</string>
+ <!-- Notification summary when satellite service is available. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_available_notification_summary">You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app.</string>
+ <!-- Notification title when satellite service is not supported by device. -->
+ <string name="satellite_sos_not_supported_notification_title">Satellite SOS isn\'t supported</string>
+ <!-- Notification summary when satellite service is not supported by device. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_not_supported_notification_summary">Satellite SOS isn\'t supported on this device</string>
+ <!-- Notification title when satellite service is not provisioned. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_not_provisioned_notification_title">Satellite SOS isn\'t set up</string>
+ <!-- Notification summary when satellite service is not provisioned. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_not_provisioned_notification_summary">Make sure you\'re connected to the internet and try setup again</string>
+ <!-- Notification title when satellite service is not allowed at current location. -->
+ <string name="satellite_sos_not_in_allowed_region_notification_title">Satellite SOS isn\'t available</string>
+ <!-- Notification summary when satellite service is not allowed at current location. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_not_in_allowed_region_notification_summary">Satellite SOS isn\'t available in this country or region</string>
+ <!-- Notification title when default messaging app does not support satellite. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_unsupported_default_sms_app_notification_title">Satellite SOS not set up</string>
+ <!-- Notification summary when default messaging app does not support satellite. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_unsupported_default_sms_app_notification_summary">To message by satellite, set Google Messages as your default messaging app</string>
+ <!-- Notification title when location settings is disabled. -->
+ <string name="satellite_sos_location_disabled_notification_title">Satellite SOS isn\'t available</string>
+ <!-- Notification summary when location settings is disabled. [CHAR LIMIT=NONE] -->
+ <string name="satellite_sos_location_disabled_notification_summary">To check if satellite SOS is available in this country or region, turn on location settings</string>
+ <!-- Notification title when satellite service is available. -->
+ <string name="satellite_messaging_available_notification_title">Satellite messaging available</string>
+ <!-- Notification summary when satellite service is available. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_available_notification_summary">You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app.</string>
+ <!-- Notification title when satellite service is not supported by device. -->
+ <string name="satellite_messaging_not_supported_notification_title">Satellite messaging not supported</string>
+ <!-- Notification summary when satellite service is not supported by device. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_not_supported_notification_summary">Satellite messaging isn\'t supported on this device</string>
+ <!-- Notification title when satellite service is not provisioned. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_not_provisioned_notification_title">Satellite messaging not set up</string>
+ <!-- Notification summary when satellite service is not provisioned. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_not_provisioned_notification_summary">Make sure you\'re connected to the internet and try setup again</string>
+ <!-- Notification title when satellite service is not allowed at current location. -->
+ <string name="satellite_messaging_not_in_allowed_region_notification_title">Satellite messaging not available</string>
+ <!-- Notification summary when satellite service is not allowed at current location. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_not_in_allowed_region_notification_summary">Satellite messaging isn\'t available in this country or region</string>
+ <!-- Notification title when default messaging app does not support satellite. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_title">Satellite messaging not set up</string>
+ <!-- Notification summary when default messaging app does not support satellite. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_unsupported_default_sms_app_notification_summary">To message by satellite, set Google Messages as your default messaging app</string>
+ <!-- Notification title when location settings is disabled. -->
+ <string name="satellite_messaging_location_disabled_notification_title">Satellite messaging not available</string>
+ <!-- Notification summary when location settings is disabled. [CHAR LIMIT=NONE] -->
+ <string name="satellite_messaging_location_disabled_notification_summary">To check if satellite messaging is available in this country or region, turn on location settings</string>
<!-- Fingerprint dangling notification title -->
<string name="fingerprint_dangling_notification_title">Set up Fingerprint Unlock again</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 515ebd54b5eb..9dd302784c2c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -312,6 +312,7 @@
<java-symbol type="bool" name="config_disableLockscreenByDefault" />
<java-symbol type="bool" name="config_enableBurnInProtection" />
<java-symbol type="bool" name="config_hotswapCapable" />
+ <java-symbol type="integer" name="config_hsumBootStrategy" />
<java-symbol type="bool" name="config_mms_content_disposition_support" />
<java-symbol type="bool" name="config_networkSamplingWakesDevice" />
<java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
@@ -1305,6 +1306,8 @@
<java-symbol type="array" name="vendor_policy_exempt_apps" />
<java-symbol type="array" name="cloneable_apps" />
<java-symbol type="array" name="config_securityStatePackages" />
+ <java-symbol type="array" name="stoppable_fgs_system_apps" />
+ <java-symbol type="array" name="vendor_stoppable_fgs_system_apps" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -2095,6 +2098,7 @@
<java-symbol type="integer" name="config_autoBrightnessDarkeningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessInitialLightSensorRate"/>
<java-symbol type="integer" name="config_autoBrightnessLightSensorRate"/>
+ <java-symbol type="integer" name="config_keyboardBacklightTimeoutMs" />
<java-symbol type="integer" name="config_carDockKeepsScreenOn" />
<java-symbol type="integer" name="config_criticalBatteryWarningLevel" />
<java-symbol type="integer" name="config_datause_notification_type" />
@@ -3715,6 +3719,7 @@
<java-symbol type="string" name="color_correction_feature_name" />
<java-symbol type="string" name="reduce_bright_colors_feature_name" />
<java-symbol type="string" name="config_defaultAccessibilityService" />
+ <java-symbol type="string" name="config_defaultSelectToSpeakService" />
<java-symbol type="string" name="config_defaultAccessibilityNotificationSound" />
<java-symbol type="string" name="accessibility_shortcut_spoken_feedback" />
<java-symbol type="array" name="config_trustedAccessibilityServices" />
@@ -5551,6 +5556,30 @@
<java-symbol type="string" name="satellite_manual_selection_state_popup_cancel" />
<java-symbol type="drawable" name="ic_satellite_alt_24px" />
<java-symbol type="drawable" name="ic_android_satellite_24px" />
+ <java-symbol type="string" name="satellite_sos_available_notification_title" />
+ <java-symbol type="string" name="satellite_sos_available_notification_summary" />
+ <java-symbol type="string" name="satellite_sos_not_in_allowed_region_notification_title" />
+ <java-symbol type="string" name="satellite_sos_not_in_allowed_region_notification_summary" />
+ <java-symbol type="string" name="satellite_sos_not_supported_notification_title" />
+ <java-symbol type="string" name="satellite_sos_not_supported_notification_summary" />
+ <java-symbol type="string" name="satellite_sos_not_provisioned_notification_title" />
+ <java-symbol type="string" name="satellite_sos_not_provisioned_notification_summary" />
+ <java-symbol type="string" name="satellite_sos_unsupported_default_sms_app_notification_title" />
+ <java-symbol type="string" name="satellite_sos_unsupported_default_sms_app_notification_summary" />
+ <java-symbol type="string" name="satellite_sos_location_disabled_notification_title" />
+ <java-symbol type="string" name="satellite_sos_location_disabled_notification_summary" />
+ <java-symbol type="string" name="satellite_messaging_available_notification_title" />
+ <java-symbol type="string" name="satellite_messaging_available_notification_summary" />
+ <java-symbol type="string" name="satellite_messaging_not_in_allowed_region_notification_title" />
+ <java-symbol type="string" name="satellite_messaging_not_in_allowed_region_notification_summary" />
+ <java-symbol type="string" name="satellite_messaging_not_supported_notification_title" />
+ <java-symbol type="string" name="satellite_messaging_not_supported_notification_summary" />
+ <java-symbol type="string" name="satellite_messaging_not_provisioned_notification_title" />
+ <java-symbol type="string" name="satellite_messaging_not_provisioned_notification_summary" />
+ <java-symbol type="string" name="satellite_messaging_unsupported_default_sms_app_notification_title" />
+ <java-symbol type="string" name="satellite_messaging_unsupported_default_sms_app_notification_summary" />
+ <java-symbol type="string" name="satellite_messaging_location_disabled_notification_title" />
+ <java-symbol type="string" name="satellite_messaging_location_disabled_notification_summary" />
<!-- DisplayManager configs. -->
<java-symbol type="bool" name="config_evenDimmerEnabled" />
@@ -5622,7 +5651,6 @@
<java-symbol type="drawable" name="ic_zen_mode_type_schedule_calendar" />
<java-symbol type="drawable" name="ic_zen_mode_type_schedule_time" />
<java-symbol type="drawable" name="ic_zen_mode_type_theater" />
- <java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
<java-symbol type="drawable" name="ic_zen_mode_type_special_dnd" />
<!-- System notification for background user sound -->
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index a382d798fb9b..f39508d6de15 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -151,6 +151,7 @@ android_test {
":HelloWorldUsingSdk1And2",
":HelloWorldUsingSdkMalformedNegativeVersion",
":CtsStaticSharedLibConsumerApp1",
+ ":CtsStaticSharedLibConsumerApp3",
],
}
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 3f7c83a82787..5d8ff87eca24 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -41,6 +41,8 @@
value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/>
<option name="push-file" key="CtsStaticSharedLibConsumerApp1.apk"
value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp1.apk"/>
+ <option name="push-file" key="CtsStaticSharedLibConsumerApp3.apk"
+ value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp3.apk"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index c2d8f9129e2c..a2598f69e031 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,7 +16,11 @@
package android.app;
+import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
+import static android.app.PropertyInvalidatedCache.MODULE_BLUETOOTH;
+import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM;
+import static android.app.PropertyInvalidatedCache.MODULE_TEST;
import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDEX;
import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED;
@@ -27,6 +31,9 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.annotation.SuppressLint;
+import android.app.PropertyInvalidatedCache.Args;
+import android.os.Binder;
import com.android.internal.os.ApplicationSharedMemory;
import android.platform.test.annotations.IgnoreUnderRavenwood;
@@ -53,11 +60,12 @@ import org.junit.Test;
*/
@SmallTest
public class PropertyInvalidatedCacheTests {
+ @Rule
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
// Configuration for creating caches
- private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST;
+ private static final String MODULE = MODULE_TEST;
private static final String API = "testApi";
// This class is a proxy for binder calls. It contains a counter that increments
@@ -245,6 +253,12 @@ public class PropertyInvalidatedCacheTests {
mQuery = query;
}
+ // Create a cache from the args. The name of the cache is the api.
+ TestCache(Args args, TestQuery query) {
+ super(args, args.mApi(), query);
+ mQuery = query;
+ }
+
public int getRecomputeCount() {
return mQuery.getRecomputeCount();
}
@@ -374,14 +388,11 @@ public class PropertyInvalidatedCacheTests {
@Test
public void testPropertyNames() {
String n1;
- n1 = PropertyInvalidatedCache.createPropertyName(
- PropertyInvalidatedCache.MODULE_SYSTEM, "getPackageInfo");
+ n1 = PropertyInvalidatedCache.createPropertyName(MODULE_SYSTEM, "getPackageInfo");
assertEquals(n1, "cache_key.system_server.get_package_info");
- n1 = PropertyInvalidatedCache.createPropertyName(
- PropertyInvalidatedCache.MODULE_SYSTEM, "get_package_info");
+ n1 = PropertyInvalidatedCache.createPropertyName(MODULE_SYSTEM, "get_package_info");
assertEquals(n1, "cache_key.system_server.get_package_info");
- n1 = PropertyInvalidatedCache.createPropertyName(
- PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState");
+ n1 = PropertyInvalidatedCache.createPropertyName(MODULE_BLUETOOTH, "getState");
assertEquals(n1, "cache_key.bluetooth.get_state");
}
@@ -391,7 +402,7 @@ public class PropertyInvalidatedCacheTests {
reason = "SystemProperties doesn't have permission check")
public void testPermissionFailure() {
// Create a cache that will write a system nonce.
- TestCache sysCache = new TestCache(PropertyInvalidatedCache.MODULE_SYSTEM, "mode1");
+ TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
try {
// Invalidate the cache, which writes the system property. There must be a permission
// failure.
@@ -407,7 +418,7 @@ public class PropertyInvalidatedCacheTests {
@Test
public void testTestMode() {
// Create a cache that will write a system nonce.
- TestCache sysCache = new TestCache(PropertyInvalidatedCache.MODULE_SYSTEM, "mode1");
+ TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
sysCache.testPropertyName();
// Invalidate the cache. This must succeed because the property has been marked for
@@ -416,7 +427,7 @@ public class PropertyInvalidatedCacheTests {
// Create a cache that uses MODULE_TEST. Invalidation succeeds whether or not the
// property is tagged as being tested.
- TestCache testCache = new TestCache(PropertyInvalidatedCache.MODULE_TEST, "mode2");
+ TestCache testCache = new TestCache(MODULE_TEST, "mode2");
testCache.invalidateCache();
testCache.testPropertyName();
testCache.invalidateCache();
@@ -432,7 +443,7 @@ public class PropertyInvalidatedCacheTests {
// The expected exception.
}
// Configuring a property for testing must fail if test mode is false.
- TestCache cache2 = new TestCache(PropertyInvalidatedCache.MODULE_SYSTEM, "mode3");
+ TestCache cache2 = new TestCache(MODULE_SYSTEM, "mode3");
try {
cache2.testPropertyName();
fail("expected an IllegalStateException");
@@ -444,6 +455,35 @@ public class PropertyInvalidatedCacheTests {
PropertyInvalidatedCache.setTestMode(true);
}
+ // Test the Args-style constructor.
+ @Test
+ public void testArgsConstructor() {
+ // Create a cache with a maximum of four entries and non-isolated UIDs.
+ TestCache cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).isolateUids(false).api("init1"),
+ new TestQuery());
+
+ cache.invalidateCache();
+ for (int i = 1; i <= 4; i++) {
+ assertEquals("foo" + i, cache.query(i));
+ assertEquals(i, cache.getRecomputeCount());
+ }
+ // Everything is in the cache. The recompute count must not increase.
+ for (int i = 1; i <= 4; i++) {
+ assertEquals("foo" + i, cache.query(i));
+ assertEquals(4, cache.getRecomputeCount());
+ }
+ // Overflow the max entries. The recompute count increases by one.
+ assertEquals("foo5", cache.query(5));
+ assertEquals(5, cache.getRecomputeCount());
+ // The oldest entry (1) has been evicted. Iterating through the first four entries will
+ // sequentially evict them all because the loop is proceeding oldest to newest.
+ for (int i = 1; i <= 4; i++) {
+ assertEquals("foo" + i, cache.query(i));
+ assertEquals(5+i, cache.getRecomputeCount());
+ }
+ }
+
// Verify the behavior of shared memory nonce storage. This does not directly test the cache
// storing nonces in shared memory.
@RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
@@ -495,4 +535,112 @@ public class PropertyInvalidatedCacheTests {
shmem.close();
}
+
+ // Verify that an invalid module causes an exception.
+ private void testInvalidModule(String module) {
+ try {
+ @SuppressLint("UnusedVariable")
+ Args arg = new Args(module);
+ fail("expected an invalid module exception: module=" + module);
+ } catch (IllegalArgumentException e) {
+ // Expected exception.
+ }
+ }
+
+ // Test various instantiation errors. The good path is tested in other methods.
+ @Test
+ public void testArgumentErrors() {
+ // Verify that an illegal module throws an exception.
+ testInvalidModule(MODULE_SYSTEM.substring(0, MODULE_SYSTEM.length() - 1));
+ testInvalidModule(MODULE_SYSTEM + "x");
+ testInvalidModule("mymodule");
+
+ // Verify that a negative max entries throws.
+ Args arg = new Args(MODULE_SYSTEM);
+ try {
+ arg.maxEntries(0);
+ fail("expected an invalid maxEntries exception");
+ } catch (IllegalArgumentException e) {
+ // Expected exception.
+ }
+
+ // Verify that creating a cache with an invalid property string throws.
+ try {
+ final String badKey = "cache_key.volume_list";
+ @SuppressLint("UnusedVariable")
+ var cache = new PropertyInvalidatedCache<Integer, Void>(4, badKey);
+ fail("expected bad property exception: prop=" + badKey);
+ } catch (IllegalArgumentException e) {
+ // Expected exception.
+ }
+ }
+
+ // Verify that a cache created with isolatedUids(true) separates out the results.
+ @RequiresFlagsEnabled(FLAG_PIC_ISOLATE_CACHE_BY_UID)
+ @Test
+ public void testIsolatedUids() {
+ TestCache cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).isolateUids(true).api("testIsolatedUids").testMode(true),
+ new TestQuery());
+ cache.invalidateCache();
+ final int uid1 = 1;
+ final int uid2 = 2;
+
+ long token = Binder.setCallingWorkSourceUid(uid1);
+ try {
+ // Populate the cache for user 1
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo6", cache.query(6));
+ assertEquals(2, cache.getRecomputeCount());
+
+ // Populate the cache for user 2. User 1 values are not reused.
+ Binder.setCallingWorkSourceUid(uid2);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+
+ // Verify that the cache for user 1 is still populated.
+ Binder.setCallingWorkSourceUid(uid1);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+
+ } finally {
+ Binder.restoreCallingWorkSource(token);
+ }
+
+ // Repeat the test with a non-isolated cache.
+ cache = new TestCache(new Args(MODULE_TEST)
+ .maxEntries(4).isolateUids(false).api("testIsolatedUids2").testMode(true),
+ new TestQuery());
+ cache.invalidateCache();
+ token = Binder.setCallingWorkSourceUid(uid1);
+ try {
+ // Populate the cache for user 1
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo6", cache.query(6));
+ assertEquals(2, cache.getRecomputeCount());
+
+ // Populate the cache for user 2. User 1 values are reused.
+ Binder.setCallingWorkSourceUid(uid2);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+
+ // Verify that the cache for user 1 is still populated.
+ Binder.setCallingWorkSourceUid(uid1);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+
+ } finally {
+ Binder.restoreCallingWorkSource(token);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 24f6ceaf786c..8d045f87063b 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -26,6 +26,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -59,6 +60,7 @@ import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -67,7 +69,11 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Looper;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -129,6 +135,9 @@ public class ActivityThreadTest {
@Rule(order = 1)
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private ActivityWindowInfoListener mActivityWindowInfoListener;
private WindowTokenClientController mOriginalWindowTokenClientController;
private Configuration mOriginalAppConfig;
@@ -912,6 +921,32 @@ public class ActivityThreadTest {
}
/**
+ * Verifies that {@link ActivityThread#handleApplicationInfoChanged} does send updates to the
+ * system context, when given the system application info.
+ */
+ @RequiresFlagsEnabled(android.content.res.Flags.FLAG_SYSTEM_CONTEXT_HANDLE_APP_INFO_CHANGED)
+ @Test
+ public void testHandleApplicationInfoChanged_systemContext() {
+ Looper.prepare();
+ final var systemThread = ActivityThread.createSystemActivityThreadForTesting();
+
+ final Context systemContext = systemThread.getSystemContext();
+ final var appInfo = systemContext.getApplicationInfo();
+ // sourceDir must not be null, and contain at least a '/', for handleApplicationInfoChanged.
+ appInfo.sourceDir = "/";
+
+ // Create a copy of the application info.
+ final var newAppInfo = new ApplicationInfo(appInfo);
+ newAppInfo.sourceDir = "/";
+ assertWithMessage("New application info is a separate instance")
+ .that(systemContext.getApplicationInfo()).isNotSameInstanceAs(newAppInfo);
+
+ systemThread.handleApplicationInfoChanged(newAppInfo);
+ assertWithMessage("Application info was updated successfully")
+ .that(systemContext.getApplicationInfo()).isSameInstanceAs(newAppInfo);
+ }
+
+ /**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int, ActivityWindowInfo)} to try to push activity configuration to the
* activity for the given sequence number.
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 31a4f16553a0..911b7ce22741 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -120,7 +120,8 @@ public class ClientTransactionListenerControllerTest {
doReturn(newDisplayInfo).when(mIDisplayManager).getDisplayInfo(123);
mDisplayManager.registerDisplayListener(mListener, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null /* packageName */);
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ null /* packageName */);
mController.onDisplayChanged(123);
mHandler.runWithScissors(() -> { }, 0);
diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java
index d169ce3c07d0..7bc4abd935b6 100644
--- a/core/tests/coretests/src/android/content/IntentTest.java
+++ b/core/tests/coretests/src/android/content/IntentTest.java
@@ -37,6 +37,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Build/Install/Run:
* atest FrameworksCoreTests:IntentTest
@@ -57,7 +61,12 @@ public class IntentTest {
public void testReadFromParcelWithExtraIntentKeys() {
Intent intent = new Intent("TEST_ACTION");
intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION));
+ // Not an intent, don't count.
intent.putExtra(TEST_EXTRA_NAME + "2", 1);
+ ArrayList<Intent> intents = new ArrayList<>();
+ intents.add(new Intent(TEST_ACTION));
+ intent.putParcelableArrayListExtra(TEST_EXTRA_NAME + "3", intents);
+ intent.setClipData(ClipData.newIntent("label", new Intent(TEST_ACTION)));
intent.collectExtraIntentKeys();
final Parcel parcel = Parcel.obtain();
@@ -68,7 +77,7 @@ public class IntentTest {
assertEquals(intent.getAction(), target.getAction());
assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys());
- assertThat(intent.getExtraIntentKeys()).hasSize(1);
+ assertThat(intent.getExtraIntentKeys()).hasSize(3);
}
@Test
@@ -87,13 +96,37 @@ public class IntentTest {
@RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT)
public void testCollectExtraIntentKeys() {
Intent intent = new Intent(TEST_ACTION);
- Intent extraIntent = new Intent(TEST_ACTION, TEST_URI);
- intent.putExtra(TEST_EXTRA_NAME, extraIntent);
+
+ Intent[] intents = new Intent[10];
+ for (int i = 0; i < intents.length; i++) {
+ intents[i] = new Intent("action" + i);
+ }
+ Intent[] intents2 = new Intent[2]; // intents[6-7]
+ System.arraycopy(intents, 6, intents2, 0, intents2.length);
+ ArrayList<Intent> intents3 = new ArrayList<>(2);
+ intents3.addAll(Arrays.asList(intents).subList(8, 10)); // intents[8-9]
+ intent.putExtra("key1", intents[0]);
+ intent.putExtra("array-key", intents2);
+ intent.setClipData(ClipData.newIntent("label2", intents[1]));
+ intent.putExtra("intkey", 1);
+ intents[0].putExtra("key3", intents[2]);
+ intents[0].setClipData(ClipData.newIntent("label4", intents[3]));
+ intents[0].putParcelableArrayListExtra("array-list-key", intents3);
+ intents[1].putExtra("key3", intents[4]);
+ intents[1].setClipData(ClipData.newIntent("label4", intents[5]));
+ intents[5].putExtra("intkey", 2);
intent.collectExtraIntentKeys();
- assertThat(intent.getExtraIntentKeys()).hasSize(1);
- assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME);
+ // collect all actions of nested intents.
+ final List<String> actions = new ArrayList<>();
+ intent.forEachNestedCreatorToken(intent1 -> {
+ actions.add(intent1.getAction());
+ });
+ assertThat(actions).hasSize(10);
+ for (int i = 0; i < intents.length; i++) {
+ assertThat(actions).contains("action" + i);
+ }
}
}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
index d4618d744644..0db49a72c51d 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -72,6 +72,12 @@ public class ApkLiteParseUtilsTest {
private static final String TEST_APP_USING_SDK_MALFORMED_VERSION =
"HelloWorldUsingSdkMalformedNegativeVersion.apk";
private static final String TEST_APP_USING_STATIC_LIB = "CtsStaticSharedLibConsumerApp1.apk";
+ private static final String TEST_APP_USING_STATIC_LIB_TWO_CERTS =
+ "CtsStaticSharedLibConsumerApp3.apk";
+ private static final String STATIC_LIB_CERT_1 =
+ "70fbd440503ec0bf41f3f21fcc83ffd39880133c27deb0945ed677c6f31d72fb";
+ private static final String STATIC_LIB_CERT_2 =
+ "e49582ff3a0aa4c5589fc5feaac6b7d6e757199dd0c6742df7bf37c2ffef95f5";
private static final String TEST_SDK1 = "HelloWorldSdk1.apk";
private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1";
private static final String TEST_SDK1_NAME = "com.test.sdk1";
@@ -86,7 +92,7 @@ public class ApkLiteParseUtilsTest {
@Before
public void setUp() throws IOException {
- mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest");
+ mTmpDir = mTemporaryFolder.newFolder("ApkLiteParseUtilsTest");
}
@After
@@ -108,9 +114,8 @@ public class ApkLiteParseUtilsTest {
assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly(
TEST_SDK1_VERSION, TEST_SDK2_VERSION
);
- for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) {
- assertThat(certDigests).asList().containsExactly("");
- }
+ String[][] expectedCerts = {{""}, {""}};
+ assertThat(baseApk.getUsesSdkLibrariesCertDigests()).isEqualTo(expectedCerts);
}
@SuppressLint("CheckResult")
@@ -126,18 +131,13 @@ public class ApkLiteParseUtilsTest {
ApkLite baseApk = result.getResult();
String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
- assertThat(liteCerts).isNotNull();
- for (String[] certDigests: liteCerts) {
- assertThat(certDigests).asList().containsExactly(certDigest);
- }
+ String[][] expectedCerts = {{certDigest}, {certDigest}};
+ assertThat(liteCerts).isEqualTo(expectedCerts);
// Same for package parser
AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
- assertThat(pkgCerts).isNotNull();
- for (int i = 0; i < liteCerts.length; i++) {
- assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
- }
+ assertThat(liteCerts).isEqualTo(pkgCerts);
}
@@ -160,9 +160,7 @@ public class ApkLiteParseUtilsTest {
String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
- for (int i = 0; i < liteCerts.length; i++) {
- assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
- }
+ assertThat(liteCerts).isEqualTo(pkgCerts);
}
@SuppressLint("CheckResult")
@@ -184,9 +182,27 @@ public class ApkLiteParseUtilsTest {
String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests();
String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests();
- for (int i = 0; i < liteCerts.length; i++) {
- assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
- }
+ assertThat(liteCerts).isEqualTo(pkgCerts);
+ }
+
+ @Test
+ public void testParseApkLite_getUsesStaticLibrary_twoCerts()
+ throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_STATIC_LIB_TWO_CERTS);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+ ApkLite baseApk = result.getResult();
+
+ // There are two certs.
+ String[][] expectedCerts = {{STATIC_LIB_CERT_1, STATIC_LIB_CERT_2}};
+ String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests();
+ assertThat(liteCerts).isEqualTo(expectedCerts);
+
+ // And they are same as package parser.
+ AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+ String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests();
+ assertThat(liteCerts).isEqualTo(pkgCerts);
}
@SuppressLint("CheckResult")
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 5a0dacb38865..9552c887443b 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -55,9 +55,10 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class DisplayManagerGlobalTest {
- private static final long ALL_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ private static final long ALL_DISPLAY_EVENTS =
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
@Mock
private IDisplayManager mDisplayManager;
@@ -127,19 +128,22 @@ public class DisplayManagerGlobalTest {
int displayId = 1;
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED, null);
+ ALL_DISPLAY_EVENTS
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
Mockito.verifyZeroInteractions(mListener);
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null);
+ ALL_DISPLAY_EVENTS
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED, null);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mListener);
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, null);
+ ALL_DISPLAY_EVENTS
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
Mockito.verifyZeroInteractions(mListener);
@@ -162,22 +166,25 @@ public class DisplayManagerGlobalTest {
public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS, null);
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
+ null);
InOrder inOrder = Mockito.inOrder(mDisplayManager);
inOrder.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED));
mDisplayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks();
inOrder.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(),
- eq(ALL_DISPLAY_EVENTS | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(ALL_DISPLAY_EVENTS
+ | DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED));
mDisplayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks();
inOrder.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED));
mDisplayManagerGlobal.unregisterDisplayListener(mListener);
inOrder.verify(mDisplayManager)
@@ -196,10 +203,12 @@ public class DisplayManagerGlobalTest {
// One listener listens on add/remove, and the other one listens on change.
mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, null /* packageName */);
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
+ null /* packageName */);
mDisplayManagerGlobal.registerDisplayListener(mListener2, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, null /* packageName */);
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ null /* packageName */);
mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
waitForHandler();
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
new file mode 100644
index 000000000000..a6de611cc077
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display
+
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
+import android.view.Display
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class DisplayTopologyTest {
+ private var topology = DisplayTopology()
+
+ @Test
+ fun addOneDisplay() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ topology.addDisplay(displayId, width, height)
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+
+ val display = topology.root!!
+ assertThat(display.displayId).isEqualTo(displayId)
+ assertThat(display.width).isEqualTo(width)
+ assertThat(display.height).isEqualTo(height)
+ assertThat(display.children).isEmpty()
+ }
+
+ @Test
+ fun addTwoDisplays() {
+ val displayId1 = 1
+ val width1 = 800f
+ val height1 = 600f
+
+ val displayId2 = 2
+ val width2 = 1000f
+ val height2 = 1500f
+
+ topology.addDisplay(displayId1, width1, height1)
+ topology.addDisplay(displayId2, width2, height2)
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
+
+ val display1 = topology.root!!
+ assertThat(display1.displayId).isEqualTo(displayId1)
+ assertThat(display1.width).isEqualTo(width1)
+ assertThat(display1.height).isEqualTo(height1)
+ assertThat(display1.children).hasSize(1)
+
+ val display2 = display1.children[0]
+ assertThat(display2.displayId).isEqualTo(displayId2)
+ assertThat(display2.width).isEqualTo(width2)
+ assertThat(display2.height).isEqualTo(height2)
+ assertThat(display2.children).isEmpty()
+ assertThat(display2.position).isEqualTo(POSITION_TOP)
+ assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2)
+ }
+
+ @Test
+ fun addManyDisplays() {
+ val displayId1 = 1
+ val width1 = 800f
+ val height1 = 600f
+
+ val displayId2 = 2
+ val width2 = 1000f
+ val height2 = 1500f
+
+ topology.addDisplay(displayId1, width1, height1)
+ topology.addDisplay(displayId2, width2, height2)
+
+ val noOfDisplays = 30
+ for (i in 3..noOfDisplays) {
+ topology.addDisplay(/* displayId= */ i, width1, height1)
+ }
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
+
+ val display1 = topology.root!!
+ assertThat(display1.displayId).isEqualTo(displayId1)
+ assertThat(display1.width).isEqualTo(width1)
+ assertThat(display1.height).isEqualTo(height1)
+ assertThat(display1.children).hasSize(1)
+
+ val display2 = display1.children[0]
+ assertThat(display2.displayId).isEqualTo(displayId2)
+ assertThat(display2.width).isEqualTo(width2)
+ assertThat(display2.height).isEqualTo(height2)
+ assertThat(display2.children).hasSize(1)
+ assertThat(display2.position).isEqualTo(POSITION_TOP)
+ assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2)
+
+ var display = display2
+ for (i in 3..noOfDisplays) {
+ display = display.children[0]
+ assertThat(display.displayId).isEqualTo(i)
+ assertThat(display.width).isEqualTo(width1)
+ assertThat(display.height).isEqualTo(height1)
+ // The last display should have no children
+ assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0)
+ assertThat(display.position).isEqualTo(POSITION_RIGHT)
+ assertThat(display.offset).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun removeDisplays() {
+ val displayId1 = 1
+ val width1 = 800f
+ val height1 = 600f
+
+ val displayId2 = 2
+ val width2 = 1000f
+ val height2 = 1500f
+
+ topology.addDisplay(displayId1, width1, height1)
+ topology.addDisplay(displayId2, width2, height2)
+
+ val noOfDisplays = 30
+ for (i in 3..noOfDisplays) {
+ topology.addDisplay(/* displayId= */ i, width1, height1)
+ }
+
+ var removedDisplays = arrayOf(20)
+ topology.removeDisplay(20)
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
+
+ var display1 = topology.root!!
+ assertThat(display1.displayId).isEqualTo(displayId1)
+ assertThat(display1.width).isEqualTo(width1)
+ assertThat(display1.height).isEqualTo(height1)
+ assertThat(display1.children).hasSize(1)
+
+ var display2 = display1.children[0]
+ assertThat(display2.displayId).isEqualTo(displayId2)
+ assertThat(display2.width).isEqualTo(width2)
+ assertThat(display2.height).isEqualTo(height2)
+ assertThat(display2.children).hasSize(1)
+ assertThat(display2.position).isEqualTo(POSITION_TOP)
+ assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2)
+
+ var display = display2
+ for (i in 3..noOfDisplays) {
+ if (i in removedDisplays) {
+ continue
+ }
+ display = display.children[0]
+ assertThat(display.displayId).isEqualTo(i)
+ assertThat(display.width).isEqualTo(width1)
+ assertThat(display.height).isEqualTo(height1)
+ // The last display should have no children
+ assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0)
+ assertThat(display.position).isEqualTo(POSITION_RIGHT)
+ assertThat(display.offset).isEqualTo(0)
+ }
+
+ topology.removeDisplay(22)
+ removedDisplays += 22
+ topology.removeDisplay(23)
+ removedDisplays += 23
+ topology.removeDisplay(25)
+ removedDisplays += 25
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
+
+ display1 = topology.root!!
+ assertThat(display1.displayId).isEqualTo(displayId1)
+ assertThat(display1.width).isEqualTo(width1)
+ assertThat(display1.height).isEqualTo(height1)
+ assertThat(display1.children).hasSize(1)
+
+ display2 = display1.children[0]
+ assertThat(display2.displayId).isEqualTo(displayId2)
+ assertThat(display2.width).isEqualTo(width2)
+ assertThat(display2.height).isEqualTo(height2)
+ assertThat(display2.children).hasSize(1)
+ assertThat(display2.position).isEqualTo(POSITION_TOP)
+ assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2)
+
+ display = display2
+ for (i in 3..noOfDisplays) {
+ if (i in removedDisplays) {
+ continue
+ }
+ display = display.children[0]
+ assertThat(display.displayId).isEqualTo(i)
+ assertThat(display.width).isEqualTo(width1)
+ assertThat(display.height).isEqualTo(height1)
+ // The last display should have no children
+ assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0)
+ assertThat(display.position).isEqualTo(POSITION_RIGHT)
+ assertThat(display.offset).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun removeAllDisplays() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ topology.addDisplay(displayId, width, height)
+ topology.removeDisplay(displayId)
+
+ assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
+ assertThat(topology.root).isNull()
+ }
+
+ @Test
+ fun removeDisplayThatDoesNotExist() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ topology.addDisplay(displayId, width, height)
+ topology.removeDisplay(3)
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+
+ val display = topology.root!!
+ assertThat(display.displayId).isEqualTo(displayId)
+ assertThat(display.width).isEqualTo(width)
+ assertThat(display.height).isEqualTo(height)
+ assertThat(display.children).isEmpty()
+ }
+
+ @Test
+ fun removePrimaryDisplay() {
+ val displayId1 = 1
+ val displayId2 = 2
+ val width = 800f
+ val height = 600f
+
+ topology = DisplayTopology(/* root= */ null, displayId2)
+ topology.addDisplay(displayId1, width, height)
+ topology.addDisplay(displayId2, width, height)
+ topology.removeDisplay(displayId2)
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
+ val display = topology.root!!
+ assertThat(display.displayId).isEqualTo(displayId1)
+ assertThat(display.width).isEqualTo(width)
+ assertThat(display.height).isEqualTo(height)
+ assertThat(display.children).isEmpty()
+ }
+
+ @Test
+ fun normalization_noOverlaps_leavesTopologyUnchanged() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.addChild(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+ display1.addChild(display3)
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.addChild(display4)
+
+ topology = DisplayTopology(display1, primaryDisplayId)
+ topology.normalize()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.root!!
+ assertThat(actualDisplay1.displayId).isEqualTo(1)
+ assertThat(actualDisplay1.width).isEqualTo(200f)
+ assertThat(actualDisplay1.height).isEqualTo(600f)
+ assertThat(actualDisplay1.children).hasSize(2)
+
+ val actualDisplay2 = actualDisplay1.children[0]
+ assertThat(actualDisplay2.displayId).isEqualTo(2)
+ assertThat(actualDisplay2.width).isEqualTo(600f)
+ assertThat(actualDisplay2.height).isEqualTo(200f)
+ assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.offset).isEqualTo(0f)
+ assertThat(actualDisplay2.children).hasSize(1)
+
+ val actualDisplay3 = actualDisplay1.children[1]
+ assertThat(actualDisplay3.displayId).isEqualTo(3)
+ assertThat(actualDisplay3.width).isEqualTo(600f)
+ assertThat(actualDisplay3.height).isEqualTo(200f)
+ assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.offset).isEqualTo(400f)
+ assertThat(actualDisplay3.children).isEmpty()
+
+ val actualDisplay4 = actualDisplay2.children[0]
+ assertThat(actualDisplay4.displayId).isEqualTo(4)
+ assertThat(actualDisplay4.width).isEqualTo(200f)
+ assertThat(actualDisplay4.height).isEqualTo(600f)
+ assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.offset).isEqualTo(0f)
+ assertThat(actualDisplay4.children).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveDisplayWithoutReparenting() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.addChild(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+ display1.addChild(display3)
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.addChild(display4)
+
+ topology = DisplayTopology(display1, primaryDisplayId)
+ // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent.
+ topology.normalize()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.root!!
+ assertThat(actualDisplay1.displayId).isEqualTo(1)
+ assertThat(actualDisplay1.width).isEqualTo(200f)
+ assertThat(actualDisplay1.height).isEqualTo(600f)
+ assertThat(actualDisplay1.children).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.children[0]
+ assertThat(actualDisplay2.displayId).isEqualTo(2)
+ assertThat(actualDisplay2.width).isEqualTo(200f)
+ assertThat(actualDisplay2.height).isEqualTo(600f)
+ assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.offset).isEqualTo(0f)
+ assertThat(actualDisplay2.children).hasSize(2)
+
+ val actualDisplay3 = actualDisplay2.children[1]
+ assertThat(actualDisplay3.displayId).isEqualTo(3)
+ assertThat(actualDisplay3.width).isEqualTo(600f)
+ assertThat(actualDisplay3.height).isEqualTo(200f)
+ assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.offset).isEqualTo(10f)
+ assertThat(actualDisplay3.children).isEmpty()
+
+ val actualDisplay4 = actualDisplay2.children[0]
+ assertThat(actualDisplay4.displayId).isEqualTo(4)
+ assertThat(actualDisplay4.width).isEqualTo(200f)
+ assertThat(actualDisplay4.height).isEqualTo(600f)
+ assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.offset).isEqualTo(210f)
+ assertThat(actualDisplay4.children).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 50f, /* position= */ 0, /* offset= */ 0f)
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.addChild(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+ display1.addChild(display3)
+
+ topology = DisplayTopology(display1, primaryDisplayId)
+ // Display 3 gets moved and its left side is still on the same line as the right side
+ // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2
+ // becomes its new parent.
+ topology.normalize()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.root!!
+ assertThat(actualDisplay1.displayId).isEqualTo(1)
+ assertThat(actualDisplay1.width).isEqualTo(200f)
+ assertThat(actualDisplay1.height).isEqualTo(50f)
+ assertThat(actualDisplay1.children).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.children[0]
+ assertThat(actualDisplay2.displayId).isEqualTo(2)
+ assertThat(actualDisplay2.width).isEqualTo(600f)
+ assertThat(actualDisplay2.height).isEqualTo(200f)
+ assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.offset).isEqualTo(0f)
+ assertThat(actualDisplay2.children).hasSize(1)
+
+ val actualDisplay3 = actualDisplay2.children[0]
+ assertThat(actualDisplay3.displayId).isEqualTo(3)
+ assertThat(actualDisplay3.width).isEqualTo(600f)
+ assertThat(actualDisplay3.height).isEqualTo(200f)
+ assertThat(actualDisplay3.position).isEqualTo(POSITION_BOTTOM)
+ assertThat(actualDisplay3.offset).isEqualTo(0f)
+ assertThat(actualDisplay3.children).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveAndReparentDisplay() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.addChild(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+ display1.addChild(display3)
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.addChild(display4)
+
+ topology = DisplayTopology(display1, primaryDisplayId)
+ topology.normalize()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.root!!
+ assertThat(actualDisplay1.displayId).isEqualTo(1)
+ assertThat(actualDisplay1.width).isEqualTo(200f)
+ assertThat(actualDisplay1.height).isEqualTo(600f)
+ assertThat(actualDisplay1.children).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.children[0]
+ assertThat(actualDisplay2.displayId).isEqualTo(2)
+ assertThat(actualDisplay2.width).isEqualTo(200f)
+ assertThat(actualDisplay2.height).isEqualTo(600f)
+ assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.offset).isEqualTo(0f)
+ assertThat(actualDisplay2.children).hasSize(1)
+
+ val actualDisplay3 = actualDisplay2.children[0]
+ assertThat(actualDisplay3.displayId).isEqualTo(3)
+ assertThat(actualDisplay3.width).isEqualTo(600f)
+ assertThat(actualDisplay3.height).isEqualTo(200f)
+ assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.offset).isEqualTo(400f)
+ assertThat(actualDisplay3.children).hasSize(1)
+
+ val actualDisplay4 = actualDisplay3.children[0]
+ assertThat(actualDisplay4.displayId).isEqualTo(4)
+ assertThat(actualDisplay4.width).isEqualTo(200f)
+ assertThat(actualDisplay4.height).isEqualTo(600f)
+ assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.offset).isEqualTo(-400f)
+ assertThat(actualDisplay4.children).isEmpty()
+ }
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 3b27fc06352e..e4e965f1cadb 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -21,6 +21,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
@@ -61,9 +63,13 @@ public class PowerManagerTest {
private UiDevice mUiDevice;
private Executor mExec = Executors.newSingleThreadExecutor();
@Mock
- private PowerManager.OnThermalStatusChangedListener mListener1;
+ private PowerManager.OnThermalStatusChangedListener mStatusListener1;
@Mock
- private PowerManager.OnThermalStatusChangedListener mListener2;
+ private PowerManager.OnThermalStatusChangedListener mStatusListener2;
+ @Mock
+ private PowerManager.OnThermalHeadroomChangedListener mHeadroomListener1;
+ @Mock
+ private PowerManager.OnThermalHeadroomChangedListener mHeadroomListener2;
private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
private native Parcel nativeObtainPowerSaveStateParcel(boolean batterySaverEnabled,
boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode,
@@ -245,53 +251,90 @@ public class PowerManagerTest {
// Initial override status is THERMAL_STATUS_NONE
int status = PowerManager.THERMAL_STATUS_NONE;
// Add listener1
- mPm.addThermalStatusListener(mExec, mListener1);
- verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ mPm.addThermalStatusListener(mExec, mStatusListener1);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
- reset(mListener1);
+ reset(mStatusListener1);
status = PowerManager.THERMAL_STATUS_SEVERE;
mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ Integer.toString(status));
- verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
- reset(mListener1);
+ reset(mStatusListener1);
// Add listener1 again
try {
- mPm.addThermalStatusListener(mListener1);
+ mPm.addThermalStatusListener(mStatusListener1);
fail("Expected exception not thrown");
} catch (IllegalArgumentException expectedException) {
}
// Add listener2 on main thread.
- mPm.addThermalStatusListener(mListener2);
- verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ mPm.addThermalStatusListener(mStatusListener2);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
- reset(mListener2);
+ reset(mStatusListener2);
status = PowerManager.THERMAL_STATUS_MODERATE;
mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ Integer.toString(status));
- verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
- verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
- reset(mListener1);
- reset(mListener2);
+ reset(mStatusListener1);
+ reset(mStatusListener2);
// Remove listener1
- mPm.removeThermalStatusListener(mListener1);
+ mPm.removeThermalStatusListener(mStatusListener1);
// Remove listener1 again
try {
- mPm.removeThermalStatusListener(mListener1);
+ mPm.removeThermalStatusListener(mStatusListener1);
fail("Expected exception not thrown");
} catch (IllegalArgumentException expectedException) {
}
status = PowerManager.THERMAL_STATUS_LIGHT;
mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ Integer.toString(status));
- verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(0)).onThermalStatusChanged(status);
- verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onThermalStatusChanged(status);
}
+ /**
+ * Confirm that we can add/remove thermal headroom listener.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK)
+ public void testThermalHeadroomCallback() throws Exception {
+ float headroom = mPm.getThermalHeadroom(0);
+ // If the device doesn't support thermal headroom, return early
+ if (Float.isNaN(headroom)) {
+ return;
+ }
+ // Add listener1
+ mPm.addThermalHeadroomListener(mExec, mHeadroomListener1);
+ verify(mHeadroomListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onThermalHeadroomChanged(anyInt(), anyInt(), anyInt(), any());
+ reset(mHeadroomListener1);
+ // Add listener1 again
+ try {
+ mPm.addThermalHeadroomListener(mHeadroomListener1);
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException expectedException) {
+ }
+ // Add listener2 on main thread.
+ mPm.addThermalHeadroomListener(mHeadroomListener2);
+ verify(mHeadroomListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onThermalHeadroomChanged(anyInt(), anyInt(), anyInt(), any());
+ reset(mHeadroomListener2);
+ // Remove listener1
+ mPm.removeThermalHeadroomListener(mHeadroomListener1);
+ // Remove listener1 again
+ try {
+ mPm.removeThermalHeadroomListener(mHeadroomListener1);
+ fail("Expected exception not thrown");
+ } catch (IllegalArgumentException expectedException) {
+ }
+ }
+
@Test
public void testGetThermalHeadroom() throws Exception {
float headroom = mPm.getThermalHeadroom(0);
diff --git a/core/tests/coretests/src/android/view/DisplayInfoTest.java b/core/tests/coretests/src/android/view/DisplayInfoTest.java
index 4c5b7e508e34..8932cf1ba552 100644
--- a/core/tests/coretests/src/android/view/DisplayInfoTest.java
+++ b/core/tests/coretests/src/android/view/DisplayInfoTest.java
@@ -78,6 +78,23 @@ public class DisplayInfoTest {
}
@Test
+ public void testRefreshRateOverride_keepsDisplyInfosEqualWhenOverrideIsSame() {
+ Display.Mode mode = new Display.Mode(
+ /*modeId=*/1, /*width=*/1000, /*height=*/1000, /*refreshRate=*/120);
+ DisplayInfo displayInfo1 = new DisplayInfo();
+ setSupportedMode(displayInfo1, mode);
+ displayInfo1.renderFrameRate = 60;
+ displayInfo1.refreshRateOverride = 30;
+
+ DisplayInfo displayInfo2 = new DisplayInfo();
+ setSupportedMode(displayInfo2, mode);
+ displayInfo2.renderFrameRate = 30;
+ displayInfo2.refreshRateOverride = 30;
+
+ assertTrue(displayInfo1.equals(displayInfo2));
+ }
+
+ @Test
public void testRefreshRateOverride_makeDisplayInfosDifferent() {
Display.Mode mode = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, /*refreshRate=*/120);
diff --git a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
index ac6c19e79fcb..66cf9c7ec832 100644
--- a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
+++ b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java
@@ -17,6 +17,7 @@
package android.view;
import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
+import static android.view.flags.Flags.FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION;
import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
import static android.view.HapticFeedbackConstants.SCROLL_TICK;
@@ -74,12 +75,13 @@ public final class HapticScrollFeedbackProviderTest {
mView = new TestView(InstrumentationRegistry.getContext());
mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
- /* disabledIfViewPlaysScrollHaptics= */ true);
+ /* isFromView= */ false);
mSetFlagsRule.disableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
}
@Test
public void testRotaryEncoder_noFeedbackWhenViewBasedFeedbackIsEnabled() {
+ mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
.thenReturn(true);
setHapticScrollTickInterval(5);
@@ -97,7 +99,24 @@ public final class HapticScrollFeedbackProviderTest {
}
@Test
+ public void testRotaryEncoder_dynamicViewRotaryFeedback_enabledEvenWhenViewFeedbackIsEnabled() {
+ mSetFlagsRule.enableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
+ when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
+ .thenReturn(true);
+ setHapticScrollTickInterval(5);
+ mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
+ /* isFromView= */ false);
+
+ mProvider.onScrollProgress(
+ INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL,
+ /* deltaInPixels= */ 10);
+
+ assertFeedbackCount(mView, SCROLL_TICK, 1);
+ }
+
+ @Test
public void testRotaryEncoder_inputDeviceCustomized_noFeedbackWhenViewBasedFeedbackIsEnabled() {
+ mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
@@ -119,7 +138,7 @@ public final class HapticScrollFeedbackProviderTest {
@Test
public void testRotaryEncoder_feedbackWhenDisregardingViewBasedScrollHaptics() {
mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
- /* disabledIfViewPlaysScrollHaptics= */ false);
+ /* isFromView= */ true);
when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
.thenReturn(true);
setHapticScrollTickInterval(5);
@@ -144,7 +163,7 @@ public final class HapticScrollFeedbackProviderTest {
List<HapticFeedbackRequest> requests = new ArrayList<>();
mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
- /* disabledIfViewPlaysScrollHaptics= */ false);
+ /* isFromView= */ true);
when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
.thenReturn(true);
setHapticScrollTickInterval(5);
@@ -917,19 +936,20 @@ public final class HapticScrollFeedbackProviderTest {
@Test
public void testNonRotaryInputFeedbackNotBlockedByRotaryUnavailability() {
+ mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION);
when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled())
.thenReturn(true);
setHapticScrollFeedbackEnabled(true);
setHapticScrollTickInterval(5);
mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig,
- /* disabledIfViewPlaysScrollHaptics= */ true);
+ /* isFromView= */ false);
// Expect one feedback here. Touch input should provide feedback since scroll feedback has
// been enabled via `setHapticScrollFeedbackEnabled(true)`.
mProvider.onScrollProgress(
INPUT_DEVICE_1, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.AXIS_Y,
/* deltaInPixels= */ 10);
- // Because `isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()` is false and
+ // Because `isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()` is true and
// `disabledIfViewPlaysScrollHaptics` is true, the scroll progress from rotary encoders will
// produce no feedback.
mProvider.onScrollProgress(
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 00ffda867d6a..a47a3e0e6c2a 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -161,7 +161,7 @@ public class ImeBackAnimationControllerTest {
mBackAnimationController.onBackInvoked();
// verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
// getInputMethodManager is called from ImeBackAnimationController)
- verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ verify(mViewRootInsetsControllerHost, times(2)).getInputMethodManager();
// verify that ImeBackAnimationController does not take control over IME insets
verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
@@ -180,7 +180,7 @@ public class ImeBackAnimationControllerTest {
mBackAnimationController.onBackInvoked();
// verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
// getInputMethodManager is called from ImeBackAnimationController)
- verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ verify(mViewRootInsetsControllerHost, times(2)).getInputMethodManager();
// verify that ImeBackAnimationController does not take control over IME insets
verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
@@ -300,7 +300,7 @@ public class ImeBackAnimationControllerTest {
mBackAnimationController.onBackInvoked();
// verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
// getInputMethodManager is called from ImeBackAnimationController)
- verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ verify(mViewRootInsetsControllerHost, times(2)).getInputMethodManager();
});
}
diff --git a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
index 9a5c1c5112e6..b1a56373c9d6 100644
--- a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
+++ b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java
@@ -30,8 +30,12 @@ import static org.mockito.Mockito.when;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.annotation.Nullable;
import android.content.Context;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.flags.Flags;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -39,6 +43,7 @@ import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -48,6 +53,8 @@ import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
@Presubmit
public final class RotaryScrollHapticsTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final int TEST_ROTARY_DEVICE_ID = 1;
private static final int TEST_RANDOM_DEVICE_ID = 2;
@@ -167,6 +174,26 @@ public final class RotaryScrollHapticsTest {
}
@Test
+ @EnableFlags(Flags.FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION)
+ public void testChildViewImplementationUsesScrollFeedbackProvider_doesNoScrollFeedback() {
+ mView.configureGenericMotion(/* result= */ false, /* scroll= */ true);
+ mView.mUsesCustomScrollFeedbackProvider = true;
+
+ // Send multiple generic motion events, to catch bugs where the behavior is WAI only for the
+ // first dispatch, but buggy for future calls.
+ mView.dispatchGenericMotionEvent(createRotaryEvent(20));
+ mView.dispatchGenericMotionEvent(createRotaryEvent(10));
+ mView.dispatchGenericMotionEvent(createRotaryEvent(30));
+
+ // Verify that the base View class's ScrollFeedbackProvider produces no scroll progress
+ // or limit events, because there's a custom ScrollFeedbackProvider used by the child
+ // View class implementation, which should hint the base View class to disable its own
+ // ScrollFeedbackProvider usage.
+ verifyNoScrollProgress();
+ verifyNoScrollLimit();
+ }
+
+ @Test
public void testScrollProgress_genericMotionEventCallbackReturningTrue_doesScrollProgress() {
mView.configureGenericMotion(/* result= */ true, /* scroll= */ true);
@@ -208,6 +235,9 @@ public final class RotaryScrollHapticsTest {
private static final class TestGenericMotionEventControllingView extends View {
private boolean mGenericMotionResult;
private boolean mScrollOnGenericMotion;
+ private boolean mUsesCustomScrollFeedbackProvider = false;
+
+ @Nullable private ScrollFeedbackProvider mCustomScrollFeedbackProvider;
TestGenericMotionEventControllingView(Context context) {
super(context);
@@ -222,6 +252,19 @@ public final class RotaryScrollHapticsTest {
public boolean onGenericMotionEvent(MotionEvent event) {
if (mScrollOnGenericMotion) {
scrollTo(100, 200); // scroll values random (not relevant for tests).
+ if (mUsesCustomScrollFeedbackProvider) {
+ // Mimic how a real child class of View would instantiate and use the
+ // ScrollFeedbackProvider API.
+ if (mCustomScrollFeedbackProvider == null) {
+ mCustomScrollFeedbackProvider = ScrollFeedbackProvider.createProvider(this);
+ }
+ float axisScrollValue = event.getAxisValue(AXIS_SCROLL);
+ mCustomScrollFeedbackProvider.onScrollProgress(
+ event.getDeviceId(),
+ event.getSource(),
+ MotionEvent.AXIS_SCROLL,
+ (int) (axisScrollValue * TEST_SCALED_VERTICAL_SCROLL_FACTOR));
+ }
}
return mGenericMotionResult;
}
diff --git a/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java b/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
index 262bd5cd6c01..0f17f9cdddc8 100644
--- a/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
+++ b/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
@@ -16,7 +16,11 @@
package android.view;
+import static android.view.RoundScrollbarRenderer.BLUECHIP_ENABLED_SYSPROP;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -30,11 +34,8 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.flags.Flags;
import androidx.test.core.app.ApplicationProvider;
@@ -42,7 +43,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -66,9 +66,6 @@ public class RoundScrollbarRendererTest {
private static final float DEFAULT_ALPHA = 0.5f;
private static final Rect BOUNDS = new Rect(0, 0, 200, 200);
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock private Canvas mCanvas;
@Captor private ArgumentCaptor<Paint> mPaintCaptor;
private RoundScrollbarRenderer mScrollbar;
@@ -88,8 +85,8 @@ public class RoundScrollbarRendererTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_USE_REFACTORED_ROUND_SCROLLBAR)
public void testScrollbarDrawn_legacy() {
+ assumeFalse(usingRefactoredScrollbar());
mScrollbar.drawRoundScrollbars(mCanvas, DEFAULT_ALPHA, BOUNDS, /* drawToLeft= */ false);
// The arc will be drawn twice, i.e. once for track and once for thumb
@@ -105,8 +102,8 @@ public class RoundScrollbarRendererTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_USE_REFACTORED_ROUND_SCROLLBAR)
public void testScrollbarDrawn() {
+ assumeTrue(usingRefactoredScrollbar());
mScrollbar.drawRoundScrollbars(mCanvas, DEFAULT_ALPHA, BOUNDS, /* drawToLeft= */ false);
// The arc will be drawn thrice, i.e. twice for track and once for thumb
@@ -143,4 +140,9 @@ public class RoundScrollbarRendererTest {
return super.computeVerticalScrollExtent();
}
}
+
+ private static boolean usingRefactoredScrollbar() {
+ return Flags.useRefactoredRoundScrollbar()
+ && SystemProperties.getBoolean(BLUECHIP_ENABLED_SYSPROP, false);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
index 8bebc62e93f2..1a9af6b55eed 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
@@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAG
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.google.common.truth.Truth.assertThat;
@@ -123,6 +124,14 @@ public class ShortcutUtilsTest {
}
@Test
+ public void getShortcutTargets_keyGestureShortcutNoService_emptyResult() {
+ assertThat(
+ ShortcutUtils.getShortcutTargetsFromSettings(
+ mContext, KEY_GESTURE, mDefaultUserId)
+ ).isEmpty();
+ }
+
+ @Test
public void getShortcutTargets_softwareShortcut1Service_return1Service() {
setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 120a4de54427..3239598eccdc 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -301,18 +301,14 @@ public class KernelSingleUidTimeReaderTest {
{1_000_000, 2_000_000, 3_000_000},
{4_000_000, 5_000_000}});
- LongArrayMultiStateCounter.LongArrayContainer array =
- new LongArrayMultiStateCounter.LongArrayContainer(5);
+
long[] out = new long[5];
- success = mInjector.addDelta(TEST_UID, counter, 2000, array);
+ success = mInjector.addDelta(TEST_UID, counter, 2000, out);
assertThat(success).isTrue();
-
- array.getValues(out);
assertThat(out).isEqualTo(new long[]{1, 2, 3, 4, 5});
- counter.getCounts(array, 0);
- array.getValues(out);
+ counter.getCounts(out, 0);
assertThat(out).isEqualTo(new long[]{1, 2, 3, 4, 5});
counter.setState(1, 3000);
@@ -322,18 +318,14 @@ public class KernelSingleUidTimeReaderTest {
{11_000_000, 22_000_000, 33_000_000},
{44_000_000, 55_000_000}});
- success = mInjector.addDelta(TEST_UID, counter, 4000, array);
+ success = mInjector.addDelta(TEST_UID, counter, 4000, out);
assertThat(success).isTrue();
-
- array.getValues(out);
assertThat(out).isEqualTo(new long[]{10, 20, 30, 40, 50});
- counter.getCounts(array, 0);
- array.getValues(out);
+ counter.getCounts(out, 0);
assertThat(out).isEqualTo(new long[]{1 + 5, 2 + 10, 3 + 15, 4 + 20, 5 + 25});
- counter.getCounts(array, 1);
- array.getValues(out);
+ counter.getCounts(out, 1);
assertThat(out).isEqualTo(new long[]{5, 10, 15, 20, 25});
}
@@ -385,7 +377,7 @@ public class KernelSingleUidTimeReaderTest {
@Override
public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs,
- LongArrayMultiStateCounter.LongArrayContainer deltaOut) {
+ long[] deltaOut) {
return addDeltaForTest(uid, counter, timestampMs, mCpuTimeInStatePerClusterNs,
deltaOut);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index b86dc5807b22..7e5d0a4c2e42 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -24,14 +24,11 @@ import android.os.BadParcelableException;
import android.os.Parcel;
import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
public class LongArrayMultiStateCounterTest {
@Rule
@@ -41,11 +38,11 @@ public class LongArrayMultiStateCounterTest {
public void setStateAndUpdateValue() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
- updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
+ counter.updateValues(new long[]{0, 0, 0, 0}, 1000);
counter.setState(0, 1000);
counter.setState(1, 2000);
counter.setState(0, 4000);
- updateValue(counter, new long[]{100, 200, 300, 400}, 9000);
+ counter.updateValues(new long[]{100, 200, 300, 400}, 9000);
assertCounts(counter, 0, new long[]{75, 150, 225, 300});
assertCounts(counter, 1, new long[]{25, 50, 75, 100});
@@ -55,15 +52,28 @@ public class LongArrayMultiStateCounterTest {
}
@Test
+ public void increment() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+
+ counter.updateValues(new long[]{0, 0, 0, 0}, 1000);
+ counter.setState(0, 1000);
+ counter.incrementValues(new long[]{1, 2, 3, 4}, 2000);
+ counter.incrementValues(new long[]{100, 200, 300, 400}, 3000);
+
+ assertCounts(counter, 0, new long[]{101, 202, 303, 404});
+ assertCounts(counter, 1, new long[]{0, 0, 0, 0});
+ }
+
+ @Test
public void copyStatesFrom() {
LongArrayMultiStateCounter source = new LongArrayMultiStateCounter(2, 1);
- updateValue(source, new long[]{0}, 1000);
+ source.updateValues(new long[]{0}, 1000);
source.setState(0, 1000);
source.setState(1, 2000);
LongArrayMultiStateCounter target = new LongArrayMultiStateCounter(2, 1);
target.copyStatesFrom(source);
- updateValue(target, new long[]{1000}, 5000);
+ target.updateValues(new long[]{1000}, 5000);
assertCounts(target, 0, new long[]{250});
assertCounts(target, 1, new long[]{750});
@@ -83,25 +93,25 @@ public class LongArrayMultiStateCounterTest {
public void setEnabled() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
counter.setState(0, 1000);
- updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
- updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
+ counter.updateValues(new long[]{0, 0, 0, 0}, 1000);
+ counter.updateValues(new long[]{100, 200, 300, 400}, 2000);
assertCounts(counter, 0, new long[]{100, 200, 300, 400});
counter.setEnabled(false, 3000);
// Partially included, because the counter is disabled after the previous update
- updateValue(counter, new long[]{200, 300, 400, 500}, 4000);
+ counter.updateValues(new long[]{200, 300, 400, 500}, 4000);
// Count only 50%, because the counter was disabled for 50% of the time
assertCounts(counter, 0, new long[]{150, 250, 350, 450});
// Not counted because the counter is disabled
- updateValue(counter, new long[]{250, 350, 450, 550}, 5000);
+ counter.updateValues(new long[]{250, 350, 450, 550}, 5000);
counter.setEnabled(true, 6000);
- updateValue(counter, new long[]{300, 400, 500, 600}, 7000);
+ counter.updateValues(new long[]{300, 400, 500, 600}, 7000);
// Again, take 50% of the delta
assertCounts(counter, 0, new long[]{175, 275, 375, 475});
@@ -111,8 +121,8 @@ public class LongArrayMultiStateCounterTest {
public void reset() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
counter.setState(0, 1000);
- updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
- updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
+ counter.updateValues(new long[]{0, 0, 0, 0}, 1000);
+ counter.updateValues(new long[]{100, 200, 300, 400}, 2000);
assertCounts(counter, 0, new long[]{100, 200, 300, 400});
@@ -120,8 +130,8 @@ public class LongArrayMultiStateCounterTest {
assertCounts(counter, 0, new long[]{0, 0, 0, 0});
- updateValue(counter, new long[]{200, 300, 400, 500}, 3000);
- updateValue(counter, new long[]{300, 400, 500, 600}, 4000);
+ counter.updateValues(new long[]{200, 300, 400, 500}, 3000);
+ counter.updateValues(new long[]{300, 400, 500, 600}, 4000);
assertCounts(counter, 0, new long[]{100, 100, 100, 100});
}
@@ -129,11 +139,11 @@ public class LongArrayMultiStateCounterTest {
@Test
public void parceling() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
- updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
+ counter.updateValues(new long[]{0, 0, 0, 0}, 1000);
counter.setState(0, 1000);
- updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
+ counter.updateValues(new long[]{100, 200, 300, 400}, 2000);
counter.setState(1, 2000);
- updateValue(counter, new long[]{101, 202, 304, 408}, 3000);
+ counter.updateValues(new long[]{101, 202, 304, 408}, 3000);
assertCounts(counter, 0, new long[]{100, 200, 300, 400});
assertCounts(counter, 1, new long[]{1, 2, 4, 8});
@@ -158,27 +168,17 @@ public class LongArrayMultiStateCounterTest {
// State, last update timestamp and current counts are undefined at this point.
newCounter.setState(0, 100);
- updateValue(newCounter, new long[]{300, 400, 500, 600}, 100);
+ newCounter.updateValues(new long[]{300, 400, 500, 600}, 100);
// A new base state and counters are established; we can continue accumulating deltas
- updateValue(newCounter, new long[]{316, 432, 564, 728}, 200);
+ newCounter.updateValues(new long[]{316, 432, 564, 728}, 200);
assertCounts(newCounter, 0, new long[]{116, 232, 364, 528});
}
- private void updateValue(LongArrayMultiStateCounter counter, long[] values, int timestamp) {
- LongArrayMultiStateCounter.LongArrayContainer container =
- new LongArrayMultiStateCounter.LongArrayContainer(values.length);
- container.setValues(values);
- counter.updateValues(container, timestamp);
- }
-
private void assertCounts(LongArrayMultiStateCounter counter, int state, long[] expected) {
- LongArrayMultiStateCounter.LongArrayContainer container =
- new LongArrayMultiStateCounter.LongArrayContainer(expected.length);
long[] counts = new long[expected.length];
- counter.getCounts(container, state);
- container.getValues(counts);
+ counter.getCounts(counts, state);
assertThat(counts).isEqualTo(expected);
}
@@ -230,33 +230,4 @@ public class LongArrayMultiStateCounterTest {
parcel.writeInt(endPos - startPos);
parcel.setDataPosition(endPos);
}
-
- @Test
- public void combineValues() {
- long[] values = new long[] {0, 1, 2, 3, 42};
- LongArrayMultiStateCounter.LongArrayContainer container =
- new LongArrayMultiStateCounter.LongArrayContainer(values.length);
- container.setValues(values);
-
- long[] out = new long[3];
- int[] indexes = {2, 1, 1, 0, 0};
- boolean nonZero = container.combineValues(out, indexes);
- assertThat(nonZero).isTrue();
- assertThat(out).isEqualTo(new long[]{45, 3, 0});
-
- // All zeros
- container.setValues(new long[]{0, 0, 0, 0, 0});
- nonZero = container.combineValues(out, indexes);
- assertThat(nonZero).isFalse();
- assertThat(out).isEqualTo(new long[]{0, 0, 0});
-
- // Index out of range
- IndexOutOfBoundsException e1 = assertThrows(
- IndexOutOfBoundsException.class,
- () -> container.combineValues(out, new int[]{0, 1, -1, 0, 0}));
- assertThat(e1.getMessage()).isEqualTo("Index -1 is out of bounds: [0, 2]");
- IndexOutOfBoundsException e2 = assertThrows(IndexOutOfBoundsException.class,
- () -> container.combineValues(out, new int[]{0, 1, 4, 0, 0}));
- assertThat(e2.getMessage()).isEqualTo("Index 4 is out of bounds: [0, 2]");
- }
}
diff --git a/core/tests/vibrator/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java
index 6210a00a5940..09bfadbf56a4 100644
--- a/core/tests/vibrator/src/android/os/VibratorTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorTest.java
@@ -110,8 +110,9 @@ public class VibratorTest {
@Test
public void onVibratorStateChanged_noVibrator_registersNoListenerToVibratorManager() {
+ int[] vibratorIds = new int[0];
VibratorManager mockVibratorManager = mock(VibratorManager.class);
- when(mockVibratorManager.getVibratorIds()).thenReturn(new int[0]);
+ when(mockVibratorManager.getVibratorIds()).thenReturn(vibratorIds);
Vibrator.OnVibratorStateChangedListener mockListener =
mock(Vibrator.OnVibratorStateChangedListener.class);
@@ -119,7 +120,7 @@ public class VibratorTest {
new SystemVibrator.MultiVibratorStateListener(
mTestLooper.getNewExecutor(), mockListener);
- multiVibratorListener.register(mockVibratorManager);
+ multiVibratorListener.register(mockVibratorManager, vibratorIds);
// Never tries to register a listener to an individual vibrator.
assertFalse(multiVibratorListener.hasRegisteredListeners());
@@ -128,8 +129,9 @@ public class VibratorTest {
@Test
public void onVibratorStateChanged_singleVibrator_forwardsAllCallbacks() {
+ int[] vibratorIds = new int[] { 1 };
VibratorManager mockVibratorManager = mock(VibratorManager.class);
- when(mockVibratorManager.getVibratorIds()).thenReturn(new int[] { 1 });
+ when(mockVibratorManager.getVibratorIds()).thenReturn(vibratorIds);
when(mockVibratorManager.getVibrator(anyInt())).thenReturn(NullVibrator.getInstance());
Vibrator.OnVibratorStateChangedListener mockListener =
@@ -138,7 +140,7 @@ public class VibratorTest {
new SystemVibrator.MultiVibratorStateListener(
mTestLooper.getNewExecutor(), mockListener);
- multiVibratorListener.register(mockVibratorManager);
+ multiVibratorListener.register(mockVibratorManager, vibratorIds);
assertTrue(multiVibratorListener.hasRegisteredListeners());
multiVibratorListener.onVibrating(/* vibratorIdx= */ 0, /* vibrating= */ false);
@@ -156,8 +158,9 @@ public class VibratorTest {
@Test
public void onVibratorStateChanged_multipleVibrators_triggersOnlyWhenAllVibratorsInitialized() {
+ int[] vibratorIds = new int[] { 1, 2 };
VibratorManager mockVibratorManager = mock(VibratorManager.class);
- when(mockVibratorManager.getVibratorIds()).thenReturn(new int[] { 1, 2 });
+ when(mockVibratorManager.getVibratorIds()).thenReturn(vibratorIds);
when(mockVibratorManager.getVibrator(anyInt())).thenReturn(NullVibrator.getInstance());
Vibrator.OnVibratorStateChangedListener mockListener =
@@ -166,7 +169,7 @@ public class VibratorTest {
new SystemVibrator.MultiVibratorStateListener(
mTestLooper.getNewExecutor(), mockListener);
- multiVibratorListener.register(mockVibratorManager);
+ multiVibratorListener.register(mockVibratorManager, vibratorIds);
assertTrue(multiVibratorListener.hasRegisteredListeners());
multiVibratorListener.onVibrating(/* vibratorIdx= */ 0, /* vibrating= */ false);
@@ -181,8 +184,9 @@ public class VibratorTest {
@Test
public void onVibratorStateChanged_multipleVibrators_stateChangeIsDeduped() {
+ int[] vibratorIds = new int[] { 1, 2 };
VibratorManager mockVibratorManager = mock(VibratorManager.class);
- when(mockVibratorManager.getVibratorIds()).thenReturn(new int[] { 1, 2 });
+ when(mockVibratorManager.getVibratorIds()).thenReturn(vibratorIds);
when(mockVibratorManager.getVibrator(anyInt())).thenReturn(NullVibrator.getInstance());
Vibrator.OnVibratorStateChangedListener mockListener =
@@ -191,7 +195,7 @@ public class VibratorTest {
new SystemVibrator.MultiVibratorStateListener(
mTestLooper.getNewExecutor(), mockListener);
- multiVibratorListener.register(mockVibratorManager);
+ multiVibratorListener.register(mockVibratorManager, vibratorIds);
assertTrue(multiVibratorListener.hasRegisteredListeners());
multiVibratorListener.onVibrating(/* vibratorIdx= */ 0, /* vibrating= */ false); // none
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 7b96699f7f71..baaec86828eb 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -213,6 +213,8 @@
<assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="gpu_service" />
<assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="keystore" />
+ <assign-permission name="android.permission.DYNAMIC_INSTRUMENTATION" uid="uprobestats" />
+
<split-permission name="android.permission.ACCESS_FINE_LOCATION">
<new-permission name="android.permission.ACCESS_COARSE_LOCATION" />
</split-permission>
@@ -271,6 +273,16 @@
targetSdk="33">
<new-permission name="android.permission.BODY_SENSORS_BACKGROUND" />
</split-permission>
+ <split-permission name="android.permission.BODY_SENSORS"
+ featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"
+ targetSdk="36">
+ <new-permission name="android.permission.health.READ_HEART_RATE" />
+ </split-permission>
+ <split-permission name="android.permission.BODY_SENSORS_BACKGROUND"
+ featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"
+ targetSdk="36">
+ <new-permission name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" />
+ </split-permission>
<split-permission name="android.permission.READ_EXTERNAL_STORAGE"
targetSdk="33">
<new-permission name="android.permission.READ_MEDIA_AUDIO" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 56e55df3f27c..541ca602a386 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -402,6 +402,9 @@ applications that come with the platform
<permission name="android.permission.SHOW_CUSTOMIZED_RESOLVER"/>
<!-- Permission required for access VIBRATOR_STATE. -->
<permission name="android.permission.ACCESS_VIBRATOR_STATE"/>
+ <!-- Permission required for vendor vibration effects and sessions. -->
+ <permission name="android.permission.VIBRATE_VENDOR_EFFECTS"/>
+ <permission name="android.permission.START_VIBRATION_SESSIONS"/>
<!-- Permission required for UsageStatsTest CTS test. -->
<permission name="android.permission.MANAGE_NOTIFICATIONS"/>
<!-- Permission required for CompanionDeviceManager CTS test. -->
@@ -591,6 +594,9 @@ applications that come with the platform
<!-- Permission required for CTS test - AdvancedProtectionManagerTest -->
<permission name="android.permission.SET_ADVANCED_PROTECTION_MODE" />
<permission name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" />
+ <!-- Permissions required for CTS test - SettingsPreferenceServiceClientTest -->
+ <permission name="android.permission.READ_SYSTEM_PREFERENCES" />
+ <permission name="android.permission.WRITE_SYSTEM_PREFERENCES" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index f8d3bffbe00b..2b0802b54c14 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -171,7 +171,7 @@ key 143 WAKEUP
# key 149 "KEY_PROG2"
key 150 EXPLORER
# key 151 "KEY_MSDOS"
-key 152 POWER
+key 152 LOCK
# key 153 "KEY_DIRECTION"
# key 154 "KEY_CYCLEWINDOWS"
key 155 ENVELOPE
@@ -200,20 +200,20 @@ key 177 PAGE_UP
key 178 PAGE_DOWN
key 179 NUMPAD_LEFT_PAREN
key 180 NUMPAD_RIGHT_PAREN
-# key 181 "KEY_NEW"
+key 181 NEW
# key 182 "KEY_REDO"
-# key 183 F13
-# key 184 F14
-# key 185 F15
-# key 186 F16
-# key 187 F17
-# key 188 F18
-# key 189 F19
-# key 190 F20
-# key 191 F21
-# key 192 F22
-# key 193 F23
-# key 194 F24
+key 183 F13
+key 184 F14
+key 185 F15
+key 186 F16
+key 187 F17
+key 188 F18
+key 189 F19
+key 190 F20
+key 191 F21
+key 192 F22
+key 193 F23
+key 194 F24
# key 195 (undefined)
# key 196 (undefined)
# key 197 (undefined)
@@ -225,11 +225,11 @@ key 201 MEDIA_PAUSE
# key 203 "KEY_PROG4"
key 204 NOTIFICATION
# key 205 "KEY_SUSPEND"
-# key 206 "KEY_CLOSE"
+key 206 CLOSE
key 207 MEDIA_PLAY
key 208 MEDIA_FAST_FORWARD
# key 209 "KEY_BASSBOOST"
-# key 210 "KEY_PRINT"
+key 210 PRINT
# key 211 "KEY_HP"
key 212 CAMERA
key 213 MUSIC
@@ -328,7 +328,7 @@ key 368 LANGUAGE_SWITCH
# key 369 "KEY_TITLE"
key 370 CAPTIONS
# key 371 "KEY_ANGLE"
-# key 372 "KEY_ZOOM"
+key 372 FULLSCREEN
# key 373 "KEY_MODE"
# key 374 "KEY_KEYBOARD"
# key 375 "KEY_SCREEN"
@@ -425,12 +425,15 @@ key 582 VOICE_ASSIST
# Linux KEY_ASSISTANT
key 583 ASSIST
key 585 EMOJI_PICKER
+key 586 DICTATE
key 656 MACRO_1
key 657 MACRO_2
key 658 MACRO_3
key 659 MACRO_4
# Keys defined by HID usages
+key usage 0x010082 LOCK FALLBACK_USAGE_MAPPING
+key usage 0x01009B DO_NOT_DISTURB FALLBACK_USAGE_MAPPING
key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING
key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING
key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING
@@ -438,12 +441,17 @@ key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING
key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING
key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING
key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c00D8 DICTATE FALLBACK_USAGE_MAPPING
key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING
key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING
key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING
key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING
key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0c0201 NEW FALLBACK_USAGE_MAPPING
+key usage 0x0c0203 CLOSE FALLBACK_USAGE_MAPPING
+key usage 0x0c0208 PRINT FALLBACK_USAGE_MAPPING
key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING
+key usage 0x0c0232 FULLSCREEN FALLBACK_USAGE_MAPPING
key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING
key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING
key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING
diff --git a/data/sounds/Android.bp b/data/sounds/Android.bp
new file mode 100644
index 000000000000..65d4872cdc16
--- /dev/null
+++ b/data/sounds/Android.bp
@@ -0,0 +1,304 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+phony {
+ name: "frameworks_sounds",
+ required: [
+ "frameworks_alarm_sounds",
+ "frameworks_notifications_sounds",
+ "frameworks_ringtones_sounds",
+ "frameworks_ui_sounds",
+ "frameworks_ui_48k_sounds",
+ ],
+}
+
+prebuilt_media {
+ name: "frameworks_alarm_sounds",
+ srcs: [
+ "Alarm_Beep_01.ogg",
+ "Alarm_Beep_02.ogg",
+ "Alarm_Beep_03.ogg",
+ "Alarm_Buzzer.ogg",
+ "Alarm_Classic.ogg",
+ "Alarm_Rooster_02.ogg",
+ "alarms/ogg/Argon.ogg",
+ "alarms/ogg/Barium.ogg",
+ "alarms/ogg/Carbon.ogg",
+ "alarms/ogg/Helium.ogg",
+ "alarms/ogg/Krypton.ogg",
+ "alarms/ogg/Neon.ogg",
+ "alarms/ogg/Neptunium.ogg",
+ "alarms/ogg/Osmium.ogg",
+ "alarms/ogg/Oxygen.ogg",
+ "alarms/ogg/Platinum.ogg",
+ "alarms/ogg/Promethium.ogg",
+ "alarms/ogg/Scandium.ogg",
+ ],
+ relative_install_path: "audio/alarms",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_notifications_sounds",
+ srcs: [
+ "notifications/ogg/Adara.ogg",
+ "notifications/Aldebaran.ogg",
+ "notifications/Altair.ogg",
+ "notifications/ogg/Alya.ogg",
+ "notifications/Antares.ogg",
+ "notifications/ogg/Antimony.ogg",
+ "notifications/ogg/Arcturus.ogg",
+ "notifications/ogg/Argon.ogg",
+ "notifications/Beat_Box_Android.ogg",
+ "notifications/ogg/Bellatrix.ogg",
+ "notifications/ogg/Beryllium.ogg",
+ "notifications/Betelgeuse.ogg",
+ "newwavelabs/CaffeineSnake.ogg",
+ "notifications/Canopus.ogg",
+ "notifications/ogg/Capella.ogg",
+ "notifications/Castor.ogg",
+ "notifications/ogg/CetiAlpha.ogg",
+ "notifications/ogg/Cobalt.ogg",
+ "notifications/Cricket.ogg",
+ "newwavelabs/DearDeer.ogg",
+ "notifications/Deneb.ogg",
+ "notifications/Doink.ogg",
+ "newwavelabs/DontPanic.ogg",
+ "notifications/Drip.ogg",
+ "notifications/Electra.ogg",
+ "F1_MissedCall.ogg",
+ "F1_New_MMS.ogg",
+ "F1_New_SMS.ogg",
+ "notifications/ogg/Fluorine.ogg",
+ "notifications/Fomalhaut.ogg",
+ "notifications/ogg/Gallium.ogg",
+ "notifications/Heaven.ogg",
+ "notifications/ogg/Helium.ogg",
+ "newwavelabs/Highwire.ogg",
+ "notifications/ogg/Hojus.ogg",
+ "notifications/ogg/Iridium.ogg",
+ "notifications/ogg/Krypton.ogg",
+ "newwavelabs/KzurbSonar.ogg",
+ "notifications/ogg/Lalande.ogg",
+ "notifications/Merope.ogg",
+ "notifications/ogg/Mira.ogg",
+ "newwavelabs/OnTheHunt.ogg",
+ "notifications/ogg/Palladium.ogg",
+ "notifications/Plastic_Pipe.ogg",
+ "notifications/ogg/Polaris.ogg",
+ "notifications/ogg/Pollux.ogg",
+ "notifications/ogg/Procyon.ogg",
+ "notifications/ogg/Proxima.ogg",
+ "notifications/ogg/Radon.ogg",
+ "notifications/ogg/Rubidium.ogg",
+ "notifications/ogg/Selenium.ogg",
+ "notifications/ogg/Shaula.ogg",
+ "notifications/Sirrah.ogg",
+ "notifications/SpaceSeed.ogg",
+ "notifications/ogg/Spica.ogg",
+ "notifications/ogg/Strontium.ogg",
+ "notifications/ogg/Syrma.ogg",
+ "notifications/TaDa.ogg",
+ "notifications/ogg/Talitha.ogg",
+ "notifications/ogg/Tejat.ogg",
+ "notifications/ogg/Thallium.ogg",
+ "notifications/Tinkerbell.ogg",
+ "notifications/ogg/Upsilon.ogg",
+ "notifications/ogg/Vega.ogg",
+ "newwavelabs/Voila.ogg",
+ "notifications/ogg/Xenon.ogg",
+ "notifications/ogg/Zirconium.ogg",
+ "notifications/arcturus.ogg",
+ "notifications/moonbeam.ogg",
+ "notifications/pixiedust.ogg",
+ "notifications/pizzicato.ogg",
+ "notifications/regulus.ogg",
+ "notifications/sirius.ogg",
+ "notifications/tweeters.ogg",
+ "notifications/vega.ogg",
+ ],
+ relative_install_path: "audio/notifications",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_ringtones_sounds",
+ srcs: [
+ "ringtones/ANDROMEDA.ogg",
+ "ringtones/ogg/Andromeda.ogg",
+ "ringtones/ogg/Aquila.ogg",
+ "ringtones/ogg/ArgoNavis.ogg",
+ "ringtones/ogg/Atria.ogg",
+ "ringtones/BOOTES.ogg",
+ "newwavelabs/Backroad.ogg",
+ "newwavelabs/BeatPlucker.ogg",
+ "newwavelabs/BentleyDubs.ogg",
+ "newwavelabs/Big_Easy.ogg",
+ "newwavelabs/BirdLoop.ogg",
+ "newwavelabs/Bollywood.ogg",
+ "newwavelabs/BussaMove.ogg",
+ "ringtones/CANISMAJOR.ogg",
+ "ringtones/CASSIOPEIA.ogg",
+ "newwavelabs/Cairo.ogg",
+ "newwavelabs/Calypso_Steel.ogg",
+ "ringtones/ogg/CanisMajor.ogg",
+ "newwavelabs/CaribbeanIce.ogg",
+ "ringtones/ogg/Carina.ogg",
+ "ringtones/ogg/Centaurus.ogg",
+ "newwavelabs/Champagne_Edition.ogg",
+ "newwavelabs/Club_Cubano.ogg",
+ "newwavelabs/CrayonRock.ogg",
+ "newwavelabs/CrazyDream.ogg",
+ "newwavelabs/CurveBall.ogg",
+ "ringtones/ogg/Cygnus.ogg",
+ "newwavelabs/DancinFool.ogg",
+ "newwavelabs/Ding.ogg",
+ "newwavelabs/DonMessWivIt.ogg",
+ "ringtones/ogg/Draco.ogg",
+ "newwavelabs/DreamTheme.ogg",
+ "newwavelabs/Eastern_Sky.ogg",
+ "newwavelabs/Enter_the_Nexus.ogg",
+ "ringtones/Eridani.ogg",
+ "newwavelabs/EtherShake.ogg",
+ "ringtones/FreeFlight.ogg",
+ "newwavelabs/FriendlyGhost.ogg",
+ "newwavelabs/Funk_Yall.ogg",
+ "newwavelabs/GameOverGuitar.ogg",
+ "newwavelabs/Gimme_Mo_Town.ogg",
+ "ringtones/ogg/Girtab.ogg",
+ "newwavelabs/Glacial_Groove.ogg",
+ "newwavelabs/Growl.ogg",
+ "newwavelabs/HalfwayHome.ogg",
+ "ringtones/ogg/Hydra.ogg",
+ "newwavelabs/InsertCoin.ogg",
+ "ringtones/ogg/Kuma.ogg",
+ "newwavelabs/LoopyLounge.ogg",
+ "newwavelabs/LoveFlute.ogg",
+ "ringtones/Lyra.ogg",
+ "ringtones/ogg/Machina.ogg",
+ "newwavelabs/MidEvilJaunt.ogg",
+ "newwavelabs/MildlyAlarming.ogg",
+ "newwavelabs/Nairobi.ogg",
+ "newwavelabs/Nassau.ogg",
+ "newwavelabs/NewPlayer.ogg",
+ "newwavelabs/No_Limits.ogg",
+ "newwavelabs/Noises1.ogg",
+ "newwavelabs/Noises2.ogg",
+ "newwavelabs/Noises3.ogg",
+ "newwavelabs/OrganDub.ogg",
+ "ringtones/ogg/Orion.ogg",
+ "ringtones/PERSEUS.ogg",
+ "newwavelabs/Paradise_Island.ogg",
+ "ringtones/ogg/Pegasus.ogg",
+ "ringtones/ogg/Perseus.ogg",
+ "newwavelabs/Playa.ogg",
+ "ringtones/ogg/Pyxis.ogg",
+ "ringtones/ogg/Rasalas.ogg",
+ "newwavelabs/Revelation.ogg",
+ "ringtones/ogg/Rigel.ogg",
+ "Ring_Classic_02.ogg",
+ "Ring_Digital_02.ogg",
+ "Ring_Synth_02.ogg",
+ "Ring_Synth_04.ogg",
+ "newwavelabs/Road_Trip.ogg",
+ "newwavelabs/RomancingTheTone.ogg",
+ "newwavelabs/Safari.ogg",
+ "newwavelabs/Savannah.ogg",
+ "ringtones/ogg/Scarabaeus.ogg",
+ "ringtones/ogg/Sceptrum.ogg",
+ "newwavelabs/Seville.ogg",
+ "newwavelabs/Shes_All_That.ogg",
+ "newwavelabs/SilkyWay.ogg",
+ "newwavelabs/SitarVsSitar.ogg",
+ "ringtones/ogg/Solarium.ogg",
+ "newwavelabs/SpringyJalopy.ogg",
+ "newwavelabs/Steppin_Out.ogg",
+ "newwavelabs/Terminated.ogg",
+ "ringtones/Testudo.ogg",
+ "ringtones/ogg/Themos.ogg",
+ "newwavelabs/Third_Eye.ogg",
+ "newwavelabs/Thunderfoot.ogg",
+ "newwavelabs/TwirlAway.ogg",
+ "ringtones/URSAMINOR.ogg",
+ "ringtones/ogg/UrsaMinor.ogg",
+ "newwavelabs/VeryAlarmed.ogg",
+ "ringtones/Vespa.ogg",
+ "newwavelabs/World.ogg",
+ "ringtones/ogg/Zeta.ogg",
+ "ringtones/hydra.ogg",
+ ],
+ relative_install_path: "audio/ringtones",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_ui_48k_sounds",
+ srcs: [
+ "effects/ogg/Effect_Tick_48k.ogg",
+ "effects/ogg/KeypressDelete_120_48k.ogg",
+ "effects/ogg/KeypressReturn_120_48k.ogg",
+ "effects/ogg/KeypressSpacebar_120_48k.ogg",
+ "effects/ogg/KeypressStandard_120_48k.ogg",
+ "effects/ogg/KeypressInvalid_120_48k.ogg",
+ "effects/ogg/Trusted_48k.ogg",
+ "effects/ogg/VideoRecord_48k.ogg",
+ "effects/ogg/VideoStop_48k.ogg",
+ "effects/ogg/camera_click_48k.ogg",
+ ],
+ dsts: [
+ "Effect_Tick.ogg",
+ "KeypressDelete.ogg",
+ "KeypressReturn.ogg",
+ "KeypressSpacebar.ogg",
+ "KeypressStandard.ogg",
+ "KeypressInvalid.ogg",
+ "Trusted.ogg",
+ "VideoRecord.ogg",
+ "VideoStop.ogg",
+ "camera_click.ogg",
+ ],
+ relative_install_path: "audio/ui",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_ui_sounds",
+ srcs: [
+ "effects/ogg/Dock.ogg",
+ "effects/ogg/Lock.ogg",
+ "effects/ogg/LowBattery.ogg",
+ "effects/ogg/Undock.ogg",
+ "effects/ogg/Unlock.ogg",
+ "effects/ogg/WirelessChargingStarted.ogg",
+ "effects/ogg/camera_focus.ogg",
+ "effects/ogg/ChargingStarted.ogg",
+ "effects/ogg/InCallNotification.ogg",
+ "effects/ogg/NFCFailure.ogg",
+ "effects/ogg/NFCInitiated.ogg",
+ "effects/ogg/NFCSuccess.ogg",
+ "effects/ogg/NFCTransferComplete.ogg",
+ "effects/ogg/NFCTransferInitiated.ogg",
+ ],
+ relative_install_path: "audio/ui",
+ product_specific: true,
+ no_full_install: true,
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
index cae5d8e6846d..35b2375fbc46 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
@@ -96,7 +96,7 @@ public final class EfficientParcelableChecker extends BugChecker
}
if (WRITE_PARCELABLE.matches(tree, state)) {
return buildDescription(tree)
- .setMessage("Recommended to use 'item.writeToParcel()' to improve "
+ .setMessage("Recommended to use 'writeTypedObject()' to improve "
+ "efficiency; saves overhead of Parcelable class name")
.build();
}
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 4c47de0ca754..d55a71e21931 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1761,7 +1761,7 @@ public abstract class ColorSpace {
if (Flags.displayBt2020Colorspace()) {
sNamedColorSpaceMap.put(Named.DISPLAY_BT2020.ordinal(), new ColorSpace.Rgb(
- "BT 2020",
+ "Display BT. 2020",
BT2020_PRIMARIES,
ILLUMINANT_D65,
null,
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 93d94c9cd7eb..b4899f975f43 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -19,6 +19,8 @@ package android.graphics;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -63,6 +65,7 @@ public class ImageFormat {
RAW_DEPTH10,
PRIVATE,
HEIC,
+ HEIC_ULTRAHDR,
JPEG_R
})
public @interface Format {
@@ -832,6 +835,16 @@ public class ImageFormat {
public static final int HEIC = 0x48454946;
/**
+ * High Efficiency Image File Format (HEIF) with embedded HDR gain map
+ *
+ * <p>This format defines the HEIC brand of High Efficiency Image File
+ * Format as described in ISO/IEC 23008-12:2024 with HDR gain map according
+ * to ISO/CD 21496‐1.</p>
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_HEIF_GAINMAP)
+ public static final int HEIC_ULTRAHDR = 0x1006;
+
+ /**
* Use this function to retrieve the number of bits per pixel of an
* ImageFormat.
*
@@ -926,6 +939,11 @@ public class ImageFormat {
if (android.media.codec.Flags.p210FormatSupport() && format == YCBCR_P210) {
return true;
}
+ if (Flags.cameraHeifGainmap()){
+ if (format == HEIC_ULTRAHDR) {
+ return true;
+ }
+ }
return false;
}
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b7a1c13c75c7..56bb0f0d12d5 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -19,7 +19,7 @@ package android.graphics;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import static com.android.text.flags.Flags.FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API;
-
+import static com.android.text.flags.Flags.FLAG_VERTICAL_TEXT_LAYOUT;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
@@ -35,6 +35,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
+import android.graphics.text.TextRunShaper;
import android.os.Build;
import android.os.LocaleList;
import android.text.GraphicsOperations;
@@ -269,7 +270,24 @@ public class Paint {
public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
/** @hide bit mask for the flag forcing freetype's autohinter on for text */
public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
- /** @hide bit mask for the flag enabling vertical rendering for text */
+
+ /**
+ * A flat that controls text to be written in vertical orientation
+ *
+ * <p>
+ * This flag is used for telling the underlying text layout engine that the text is for vertical
+ * direction. By enabling this flag, text measurement, drawing and shaping APIs works for
+ * vertical text layout. For example, {@link Canvas#drawText(String, float, float, Paint)} draws
+ * text from top to bottom. {@link Paint#measureText(String)} returns vertical advances instead
+ * of horizontal advances. {@link TextRunShaper} shapes text vertically and report glyph IDs for
+ * vertical layout.
+ *
+ * <p>
+ * Do not set this flag for making {@link android.text.Layout}. The {@link android.text.Layout}
+ * class and its subclasses are designed for horizontal text only and does not work for vertical
+ * text.
+ */
+ @FlaggedApi(FLAG_VERTICAL_TEXT_LAYOUT)
public static final int VERTICAL_TEXT_FLAG = 0x1000;
/**
@@ -2101,7 +2119,7 @@ public class Paint {
* @see FontVariationAxis
*/
public boolean setFontVariationSettings(String fontVariationSettings) {
- final boolean useFontVariationStore = Flags.typefaceRedesign()
+ final boolean useFontVariationStore = Flags.typefaceRedesignReadonly()
&& CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT);
if (useFontVariationStore) {
FontVariationAxis[] axes =
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index ed17fdefcb53..43216ba6e087 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -133,7 +133,7 @@ public final class PositionedGlyphs {
@NonNull
public Font getFont(@IntRange(from = 0) int index) {
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
- if (Flags.typefaceRedesign()) {
+ if (Flags.typefaceRedesignReadonly()) {
return mFonts.get(nGetFontId(mLayoutPtr, index));
}
return mFonts.get(index);
@@ -252,7 +252,7 @@ public final class PositionedGlyphs {
mXOffset = xOffset;
mYOffset = yOffset;
- if (Flags.typefaceRedesign()) {
+ if (Flags.typefaceRedesignReadonly()) {
int fontCount = nGetFontCount(layoutPtr);
mFonts = new ArrayList<>(fontCount);
for (int i = 0; i < fontCount; ++i) {
diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java
index 197aaba4bcb5..e6091c1da8a5 100644
--- a/keystore/java/android/security/keystore/KeyStoreManager.java
+++ b/keystore/java/android/security/keystore/KeyStoreManager.java
@@ -49,7 +49,7 @@ import java.util.List;
*/
@FlaggedApi(android.security.Flags.FLAG_KEYSTORE_GRANT_API)
@SystemService(Context.KEYSTORE_SERVICE)
-public class KeyStoreManager {
+public final class KeyStoreManager {
private static final String TAG = "KeyStoreManager";
private static final Object sInstanceLock = new Object();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 438532725686..76eb207a31c9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -16,8 +16,10 @@
package androidx.window.extensions.area;
+import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
@@ -103,6 +105,30 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
@GuardedBy("mLock")
private int mLastReportedRearDisplayPresentationStatus;
+ @VisibleForTesting
+ static int getRdmV1Identifier(List<DeviceState> currentSupportedDeviceStates) {
+ for (int i = 0; i < currentSupportedDeviceStates.size(); i++) {
+ DeviceState state = currentSupportedDeviceStates.get(i);
+ if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)
+ && !state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) {
+ return state.getIdentifier();
+ }
+ }
+ return INVALID_DEVICE_STATE_IDENTIFIER;
+ }
+
+ @VisibleForTesting
+ static int getRdmV2Identifier(List<DeviceState> currentSupportedDeviceStates) {
+ for (int i = 0; i < currentSupportedDeviceStates.size(); i++) {
+ DeviceState state = currentSupportedDeviceStates.get(i);
+ if (state.hasProperties(PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) {
+ return state.getIdentifier();
+ }
+ }
+ return INVALID_DEVICE_STATE_IDENTIFIER;
+ }
+
public WindowAreaComponentImpl(@NonNull Context context) {
mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
mDisplayManager = context.getSystemService(DisplayManager.class);
@@ -111,12 +137,10 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedDeviceStates();
if (Flags.deviceStatePropertyMigration()) {
- for (int i = 0; i < mCurrentSupportedDeviceStates.size(); i++) {
- DeviceState state = mCurrentSupportedDeviceStates.get(i);
- if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) {
- mRearDisplayState = state.getIdentifier();
- break;
- }
+ if (Flags.deviceStateRdmV2()) {
+ mRearDisplayState = getRdmV2Identifier(mCurrentSupportedDeviceStates);
+ } else {
+ mRearDisplayState = getRdmV1Identifier(mCurrentSupportedDeviceStates);
}
} else {
mFoldedDeviceStates = context.getResources().getIntArray(
@@ -569,7 +593,8 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
private boolean isDeviceFolded() {
if (Flags.deviceStatePropertyApi()) {
return mCurrentDeviceState.hasProperty(
- PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+ && !mCurrentDeviceState.hasProperty(PROPERTY_EMULATED_ONLY);
} else {
return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState.getIdentifier());
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java
index ccb4ebe9199e..d677fef5c22c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java
@@ -16,8 +16,13 @@
package androidx.window.extensions.area;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
+
import static org.junit.Assert.assertEquals;
+import android.hardware.devicestate.DeviceState;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.Surface;
@@ -29,11 +34,34 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WindowAreaComponentImplTests {
+ private static final DeviceState REAR_DISPLAY_STATE_V1 = new DeviceState(
+ new DeviceState.Configuration.Builder(1, "STATE_0")
+ .setSystemProperties(
+ Set.of(PROPERTY_FEATURE_REAR_DISPLAY))
+ .build());
+ private static final DeviceState REAR_DISPLAY_STATE_V2 = new DeviceState(
+ new DeviceState.Configuration.Builder(2, "STATE_0")
+ .setSystemProperties(
+ Set.of(PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT))
+ .build());
+ // The PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT state must be present together with the
+ // PROPERTY_FEATURE_REAR_DISPLAY state in order to be a valid state.
+ private static final DeviceState INVALID_REAR_DISPLAY_STATE = new DeviceState(
+ new DeviceState.Configuration.Builder(2, "STATE_0")
+ .setSystemProperties(
+ Set.of(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT))
+ .build());
+
private final DisplayMetrics mTestDisplayMetrics = new DisplayMetrics();
@Before
@@ -93,4 +121,37 @@ public class WindowAreaComponentImplTests {
Surface.ROTATION_270, Surface.ROTATION_0, mTestDisplayMetrics);
assertEquals(expectedMetrics, mTestDisplayMetrics);
}
+
+ @Test
+ public void testRdmV1Identifier() {
+ final List<DeviceState> supportedStates = new ArrayList<>();
+ supportedStates.add(REAR_DISPLAY_STATE_V2);
+ assertEquals(INVALID_DEVICE_STATE_IDENTIFIER,
+ WindowAreaComponentImpl.getRdmV1Identifier(supportedStates));
+
+ supportedStates.add(REAR_DISPLAY_STATE_V1);
+ assertEquals(REAR_DISPLAY_STATE_V1.getIdentifier(),
+ WindowAreaComponentImpl.getRdmV1Identifier(supportedStates));
+ }
+
+ @Test
+ public void testRdmV2Identifier_whenStateIsImproperlyConfigured() {
+ final List<DeviceState> supportedStates = new ArrayList<>();
+ supportedStates.add(INVALID_REAR_DISPLAY_STATE);
+ assertEquals(INVALID_DEVICE_STATE_IDENTIFIER,
+ WindowAreaComponentImpl.getRdmV2Identifier(supportedStates));
+ }
+
+ @Test
+ public void testRdmV2Identifier_whenStateIsProperlyConfigured() {
+ final List<DeviceState> supportedStates = new ArrayList<>();
+
+ supportedStates.add(REAR_DISPLAY_STATE_V1);
+ assertEquals(INVALID_DEVICE_STATE_IDENTIFIER,
+ WindowAreaComponentImpl.getRdmV2Identifier(supportedStates));
+
+ supportedStates.add(REAR_DISPLAY_STATE_V2);
+ assertEquals(REAR_DISPLAY_STATE_V2.getIdentifier(),
+ WindowAreaComponentImpl.getRdmV2Identifier(supportedStates));
+ }
}
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index b2ac640a468d..636e3cfd571d 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -32,7 +32,6 @@
android:name=".desktopmode.DesktopWallpaperActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
- android:showForAllUsers="true"
android:theme="@style/DesktopWallpaperTheme" />
<activity
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 0b515f590f98..5f42bb161204 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -475,6 +475,6 @@ class BubbleStackViewTest {
override fun hideCurrentInputMethod() {}
- override fun updateBubbleBarLocation(location: BubbleBarLocation) {}
+ override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
}
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index 08d647de4a51..6ac36a3319c9 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -47,6 +47,7 @@ import com.android.wm.shell.shared.handles.RegionSamplingHelper
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import java.util.Collections
import java.util.concurrent.Executor
@@ -147,6 +148,7 @@ class BubbleBarExpandedViewTest {
@After
fun tearDown() {
testableRegionSamplingHelper?.stopAndDestroy()
+ getInstrumentation().waitForIdleSync()
}
@Test
@@ -218,8 +220,8 @@ class BubbleBarExpandedViewTest {
@Test
fun testEventLogging_dismissBubbleViaAppMenu() {
getInstrumentation().runOnMainSync { bubbleExpandedView.handleView.performClick() }
- val dismissMenuItem =
- bubbleExpandedView.findViewWithTag<View>(BubbleBarMenuView.DISMISS_ACTION_TAG)
+ val dismissMenuItem = bubbleExpandedView.menuView()
+ .actionViewWithText(context.getString(R.string.bubble_dismiss_text))
assertThat(dismissMenuItem).isNotNull()
getInstrumentation().runOnMainSync { dismissMenuItem.performClick() }
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
@@ -228,6 +230,42 @@ class BubbleBarExpandedViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @Test
+ fun testEventLogging_openAppSettings() {
+ getInstrumentation().runOnMainSync { bubbleExpandedView.handleView.performClick() }
+ val appMenuItem = bubbleExpandedView.menuView()
+ .actionViewWithText(context.getString(R.string.bubbles_app_settings, bubble.appName))
+ getInstrumentation().runOnMainSync { appMenuItem.performClick() }
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_APP_MENU_GO_TO_SETTINGS.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ @Test
+ fun testEventLogging_unBubbleConversation() {
+ getInstrumentation().runOnMainSync { bubbleExpandedView.handleView.performClick() }
+ val menuItem = bubbleExpandedView.menuView()
+ .actionViewWithText(context.getString(R.string.bubbles_dont_bubble_conversation))
+ getInstrumentation().runOnMainSync { menuItem.performClick() }
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_APP_MENU_OPT_OUT.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView {
+ return findViewByPredicate { it is BubbleBarMenuView }
+ }
+
+ private fun BubbleBarMenuView.actionViewWithText(text: CharSequence): View {
+ val views = ArrayList<View>()
+ findViewsWithText(views, text, View.FIND_VIEWS_WITH_TEXT)
+ assertWithMessage("Expecting a single action with text '$text'").that(views).hasSize(1)
+ // findViewsWithText returns the TextView, but the click listener is on the parent container
+ return views.first().parent as View
+ }
+
private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
val taskViewTaskController = mock<TaskViewTaskController>()
@@ -337,7 +375,7 @@ class BubbleBarExpandedViewTest {
override fun hideCurrentInputMethod() {
}
- override fun updateBubbleBarLocation(location: BubbleBarLocation) {
+ override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {
}
}
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
index 2fbf089d99d6..0044593ad228 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -19,12 +19,15 @@ package com.android.wm.shell.bubbles.bar
import android.app.ActivityManager
import android.content.Context
import android.content.pm.LauncherApps
+import android.graphics.PointF
import android.os.Handler
import android.os.UserManager
import android.view.IWindowManager
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
+import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -48,6 +51,7 @@ import com.android.wm.shell.bubbles.BubbleTaskViewFactory
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.FakeBubbleFactory
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
+import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.bubbles.properties.BubbleProperties
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.common.DisplayController
@@ -57,6 +61,7 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.TransactionPool
+import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -66,8 +71,10 @@ import com.android.wm.shell.taskview.TaskViewTaskController
import com.android.wm.shell.taskview.TaskViewTransitions
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import java.util.Collections
import org.junit.Before
+import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -79,18 +86,28 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class BubbleBarLayerViewTest {
+ companion object {
+ @JvmField @ClassRule
+ val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+ }
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var bubbleBarLayerView: BubbleBarLayerView
private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var bubbleController: BubbleController
+
+ private lateinit var bubblePositioner: BubblePositioner
+
private lateinit var bubble: Bubble
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
ProtoLog.init()
+ PhysicsAnimatorTestUtils.prepareForTest()
uiEventLoggerFake = UiEventLoggerFake()
val bubbleLogger = BubbleLogger(uiEventLoggerFake)
@@ -100,7 +117,7 @@ class BubbleBarLayerViewTest {
val windowManager = context.getSystemService(WindowManager::class.java)
- val bubblePositioner = BubblePositioner(context, windowManager)
+ bubblePositioner = BubblePositioner(context, windowManager)
bubblePositioner.setShowingInBubbleBar(true)
val bubbleData =
@@ -113,7 +130,7 @@ class BubbleBarLayerViewTest {
bgExecutor,
)
- val bubbleController =
+ bubbleController =
createBubbleController(
bubbleData,
windowManager,
@@ -151,6 +168,12 @@ class BubbleBarLayerViewTest {
bubble = FakeBubbleFactory.createChatBubbleWithViewInfo(context, viewInfo = viewInfo)
}
+ @After
+ fun tearDown() {
+ PhysicsAnimatorTestUtils.tearDown()
+ getInstrumentation().waitForIdleSync()
+ }
+
private fun createBubbleController(
bubbleData: BubbleData,
windowManager: WindowManager?,
@@ -224,6 +247,70 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @Test
+ fun testEventLogging_dragExpandedViewLeft() {
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ // Drag from right to left
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, rightEdge())
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, leftEdge())
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, leftEdge())
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ @Test
+ fun testEventLogging_dragExpandedViewRight() {
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ // Drag from left to right
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, leftEdge())
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, rightEdge())
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, rightEdge())
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ private fun leftEdge(): PointF {
+ val screenSize = bubblePositioner.availableRect
+ return PointF(screenSize.left.toFloat(), screenSize.height() / 2f)
+ }
+
+ private fun rightEdge(): PointF {
+ val screenSize = bubblePositioner.availableRect
+ return PointF(screenSize.right.toFloat(), screenSize.height() / 2f)
+ }
+
+ private fun waitForExpandedViewAnimation() {
+ // wait for idle to allow the animation to start
+ getInstrumentation().waitForIdleSync()
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(200) }
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+ AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
+ }
+
private inner class FakeBubbleTaskViewFactory(private val mainExecutor: ShellExecutor) :
BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
@@ -264,7 +351,7 @@ class BubbleBarLayerViewTest {
override fun hideCurrentInputMethod() {}
- override fun updateBubbleBarLocation(location: BubbleBarLocation) {}
+ override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {}
}
}
@@ -290,4 +377,9 @@ class BubbleBarLayerViewTest {
}
}
}
+
+ private fun View.dispatchTouchEvent(eventTime: Long, action: Int, point: PointF) {
+ val event = MotionEvent.obtain(0L, eventTime, action, point.x, point.y, 0)
+ getInstrumentation().runOnMainSync { dispatchTouchEvent(event) }
+ }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index ecb2b25a02f1..d4cbe6e10971 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -74,6 +74,7 @@ class BubbleExpandedViewPinControllerTest {
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ ProtoLog.init()
container = FrameLayout(context)
val windowManager = context.getSystemService(WindowManager::class.java)
positioner = BubblePositioner(context, windowManager)
@@ -85,7 +86,7 @@ class BubbleExpandedViewPinControllerTest {
isSmallTablet = false,
isLandscape = true,
isRtl = false,
- insets = Insets.of(10, 20, 30, 40)
+ insets = Insets.of(10, 20, 30, 40),
)
positioner.update(deviceConfig)
positioner.bubbleBarTopOnScreen =
@@ -407,12 +408,26 @@ class BubbleExpandedViewPinControllerTest {
assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
+ /** Send drag start event when on left */
+ @Test
+ fun start_onLeft_sendStartEventOnLeft() {
+ getInstrumentation().runOnMainSync { controller.onDragStart(initialLocationOnLeft = true) }
+ assertThat(testListener.locationStart).containsExactly(LEFT)
+ }
+
+ /** Send drag start event when on right */
+ @Test
+ fun start_onRight_sendStartEventOnRight() {
+ getInstrumentation().runOnMainSync { controller.onDragStart(initialLocationOnLeft = false) }
+ assertThat(testListener.locationStart).containsExactly(RIGHT)
+ }
+
private fun getExpectedDropTargetBoundsOnLeft(): Rect =
Rect().also {
positioner.getBubbleBarExpandedViewBounds(
true /* onLeft */,
false /* isOverflowExpanded */,
- it
+ it,
)
}
@@ -421,7 +436,7 @@ class BubbleExpandedViewPinControllerTest {
positioner.getBubbleBarExpandedViewBounds(
false /* onLeft */,
false /* isOverflowExpanded */,
- it
+ it,
)
}
@@ -446,8 +461,14 @@ class BubbleExpandedViewPinControllerTest {
}
internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
+ val locationStart = mutableListOf<BubbleBarLocation>()
val locationChanges = mutableListOf<BubbleBarLocation>()
val locationReleases = mutableListOf<BubbleBarLocation>()
+
+ override fun onStart(location: BubbleBarLocation) {
+ locationStart.add(location)
+ }
+
override fun onChange(location: BubbleBarLocation) {
locationChanges.add(location)
}
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_change_aspect_ratio.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_change_aspect_ratio.xml
new file mode 100644
index 000000000000..4442e9df7688
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_change_aspect_ratio.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/compat_controls_text"
+ android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
index 501bedd50f55..c2755ef6ccb6 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
@@ -19,6 +19,7 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
+ android:clipChildren="false"
android:id="@+id/bubble_expanded_view">
<com.android.wm.shell.bubbles.bar.BubbleBarHandleView
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
index f1ecde49ce78..7aca921dccc7 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
@@ -14,20 +14,18 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.wm.shell.bubbles.bar.BubbleBarMenuView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
+<com.android.wm.shell.bubbles.bar.BubbleBarMenuView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
+ android:clipToPadding="false"
android:minWidth="@dimen/bubble_bar_manage_menu_min_width"
android:orientation="vertical"
- android:elevation="@dimen/bubble_manage_menu_elevation"
- android:paddingTop="@dimen/bubble_bar_manage_menu_padding_top"
- android:paddingHorizontal="@dimen/bubble_bar_manage_menu_padding"
- android:paddingBottom="@dimen/bubble_bar_manage_menu_padding"
- android:clipToPadding="false">
+ android:visibility="invisible"
+ tools:visibility="visible">
<LinearLayout
android:id="@+id/bubble_bar_manage_menu_bubble_section"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 5609663c01a0..a18a2510f0f7 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -157,10 +157,18 @@
android:drawableStart="@drawable/desktop_mode_ic_handle_menu_manage_windows"
android:drawableTint="?androidprv:attr/materialColorOnSurface"
style="@style/DesktopModeHandleMenuActionButton" />
+
+ <Button
+ android:id="@+id/change_aspect_ratio_button"
+ android:contentDescription="@string/change_aspect_ratio_text"
+ android:text="@string/change_aspect_ratio_text"
+ android:drawableStart="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
+ android:drawableTint="?androidprv:attr/materialColorOnSurface"
+ style="@style/DesktopModeHandleMenuActionButton" />
</LinearLayout>
<LinearLayout
- android:id="@+id/open_in_browser_pill"
+ android:id="@+id/open_in_app_or_browser_pill"
android:layout_width="match_parent"
android:layout_height="@dimen/desktop_mode_handle_menu_open_in_browser_pill_height"
android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
@@ -170,7 +178,7 @@
android:background="@drawable/desktop_mode_decor_handle_menu_background">
<Button
- android:id="@+id/open_in_browser_button"
+ android:id="@+id/open_in_app_or_browser_button"
android:layout_weight="1"
android:contentDescription="@string/open_in_browser_text"
android:text="@string/open_in_browser_text"
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 95c2bb59d202..a4aa3480fb46 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Maak in blaaier oop"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nuwe venster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string>
<string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Maak kieslys oop"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Meesleurend"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Stel terug"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Stel terug"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Spring na links"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In die app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In jou blaaier"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Maak hier apps vinnig in jou blaaier oop"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index ba74e342f353..1cd980460cee 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"በአሳሽ ውስጥ ክፈት"</string>
<string name="new_window_text" msgid="6318648868380652280">"አዲስ መስኮት"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string>
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ምናሌን ክፈት"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"አስማጭ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ወደነበረበት መልስ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ወደነበረበት መልስ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ወደ ግራ አሳድግ"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"በመተግበሪያው ውስጥ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"በአሳሽዎ ውስጥ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"እሺ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"እዚህ አሳሽዎ ውስጥ መተግበሪያዎችን በፍጥነት ይክፈቱ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index a8febc80ffc1..41ebfcd0ee85 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"فتح في المتصفِّح"</string>
<string name="new_window_text" msgid="6318648868380652280">"نافذة جديدة"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string>
<string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"فتح القائمة"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"في التطبيق"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"في المتصفِّح"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"حسنًا"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"فتح التطبيقات بسرعة في المتصفِّح هنا"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 8c924e342875..203fed0aecef 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ব্ৰাউজাৰত খোলক"</string>
<string name="new_window_text" msgid="6318648868380652280">"নতুন ৱিণ্ড’"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string>
<string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"মেনু খোলক"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"এপ্‌টোত"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপোনাৰ ব্ৰাউজাৰত"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ইয়াত আপোনাৰ ব্ৰাউজাৰত ক্ষিপ্ৰভাৱে এপ্ খোলক"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index aa232e330604..31ddc9b78b68 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerdə açın"</string>
<string name="new_window_text" msgid="6318648868380652280">"Yeni pəncərə"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string>
<string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menyunu açın"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"İmmersiv"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Bərpa edin"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Bərpa edin"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tərəf çəkin"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Tətbiqdə"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerinizdə"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Brauzerinizdəki tətbiqləri burada sürətlə açın"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 256344a4cb31..486b3cfbfee4 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Otvorite u pregledaču"</string>
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promenite razmeru"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvorite meni"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imerzivne"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećajte"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vratite"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prikačite levo"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledaču"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Potvrdi"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ovde možete brzo da otvarate aplikacije u pregledaču"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 701c51091aa4..cc42da947c36 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Адкрыць у браўзеры"</string>
<string name="new_window_text" msgid="6318648868380652280">"Новае акно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string>
<string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Адкрыць меню"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"З эфектам прысутнасці"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Аднавіць"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Аднавіць"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Размясціць злева"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У праграме"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У браўзеры"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Тут можна хутка адкрываць праграмы ў браўзеры"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 9ab86f4cbc56..c12b37b34d5a 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Отваряне в браузър"</string>
<string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string>
<string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отваряне на менюто"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Реалистично"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Възстановяване"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Възстановяване"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прилепване наляво"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложението"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузъра ви"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Бързо отваряйте приложения в браузъра си оттук"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 22a445f1754c..aca5b34ae4c0 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ব্রাউজারে খুলুন"</string>
<string name="new_window_text" msgid="6318648868380652280">"নতুন উইন্ডো"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
<string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"মেনু খুলুন"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"অ্যাপের মধ্যে"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপনার ব্রাউজারে"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"এখানে আপনার ব্রাউজারে দ্রুত অ্যাপ খুলুন"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 73f30d797883..6bd6473a5f13 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -127,14 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Otvaranje u pregledniku"</string>
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvaranje menija"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
- <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Interaktivno"</string>
- <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Uvjerljivo"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vraćanje"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vraćanje"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Uredu"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ovdje možete brzo otvarati aplikacije u pregledniku"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 499ed329e511..d9ad5a68d163 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Obre al navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Finestra nova"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string>
<string name="close_text" msgid="4986518933445178928">"Tanca"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Obre el menú"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiu"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaura"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaura"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajusta a l\'esquerra"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"A l\'aplicació"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Al navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"D\'acord"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Obre ràpidament aplicacions al navegador aquí"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 6a5780e01822..ab51b666cdda 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Otevřít v prohlížeči"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string>
<string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otevřít nabídku"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaci"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prohlížeči"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Odtud můžete v prohlížeči rychle otevírat aplikace"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 430cf96cd72f..443620804e10 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Åbn i browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nyt vindue"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string>
<string name="close_text" msgid="4986518933445178928">"Luk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Åbn menu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Opslugende"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Gendan"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gendan"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fastgør til venstre"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I din browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Åbn hurtigt apps i din browser her"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index cafaa89f57e3..b6e89c0eeb8e 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Im Browser öffnen"</string>
<string name="new_window_text" msgid="6318648868380652280">"Neues Fenster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string>
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menü öffnen"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiv"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Wiederherstellen"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Wiederherstellen"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links andocken"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In der App"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In deinem Browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Hier kannst du Apps schnell in deinem Browser öffnen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index d02fae2a986d..601c0ceee27f 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Άνοιγμα σε πρόγραμμα περιήγησης"</string>
<string name="new_window_text" msgid="6318648868380652280">"Νέο παράθυρο"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Διαχείριση παραθύρων"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string>
<string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Άνοιγμα μενού"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Στην εφαρμογή"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Στο πρόγραμμα περιήγησής σας"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ΟΚ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ανοίξτε γρήγορα εφαρμογές στο πρόγραμμα περιήγησής σας εδώ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index f9911451f4b5..fd6317530109 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 2d123ec3a3d4..dac1b9a1460d 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"New Window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage Windows"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open Menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index f9911451f4b5..fd6317530109 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index f9911451f4b5..fd6317530109 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 210b708b49af..e67fc8e2c63c 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nueva ventana"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir el menú"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Inmersivo"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restablecer"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restablecer"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar a la izquierda"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En un navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abre rápidamente apps en tu navegador aquí"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 3c7bfe5a3cb4..2f5ec64be629 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Ventana nueva"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir menú"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Inmersivo"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Acoplar a la izquierda"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la aplicación"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En el navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abre rápidamente aplicaciones en tu navegador aquí"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index d17ee02a3a7f..dd78628093d4 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Avamine brauseris"</string>
<string name="new_window_text" msgid="6318648868380652280">"Uus aken"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string>
<string name="close_text" msgid="4986518933445178928">"Sule"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ava menüü"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Kaasahaarav"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Taasta"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Taasta"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Tõmmake vasakule"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Rakenduses"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauseris"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Avage rakendusi kiiresti oma brauseris siin"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index f9419bc4614b..1cfc69457ce9 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Ireki arakatzailean"</string>
<string name="new_window_text" msgid="6318648868380652280">"Leiho berria"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string>
<string name="close_text" msgid="4986518933445178928">"Itxi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ireki menua"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Murgiltzailea"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Leheneratu"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Leheneratu"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ezarri ezkerrean"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Aplikazioan"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Arakatzailean"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ados"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ireki aplikazioak arakatzailean bizkor"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index a3d3cbc872fd..f76f67d2e796 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"باز کردن در مرورگر"</string>
<string name="new_window_text" msgid="6318648868380652280">"پنجره جدید"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجره‌ها"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string>
<string name="close_text" msgid="4986518933445178928">"بستن"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"باز کردن منو"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمی‌توان به اینجا منتقل کرد"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"فراگیر"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"بازیابی"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بازیابی کردن"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"کشیدن به‌چپ"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"در برنامه"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"در مرورگر"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"تأیید"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"در اینجا سریع برنامه‌ها را در مرورگرتان باز کنید"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index ee5dd6516098..a1ec0150ffae 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Avaa selaimessa"</string>
<string name="new_window_text" msgid="6318648868380652280">"Uusi ikkuna"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string>
<string name="close_text" msgid="4986518933445178928">"Sulje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Avaa valikko"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiivinen"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Palauta"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Palauta"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Siirrä vasemmalle"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sovelluksessa"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Selaimella"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Avaa sovellukset nopeasti selaimessa täältä"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index dc4789169146..1b9b74a45671 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans le navigateur"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string>
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ouvrir le menu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersif"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurer"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Épingler à gauche"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'appli"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ouvrez rapidement des applis dans votre navigateur ici"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index a52ab49da3ab..7e0a0b1acb7b 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans un navigateur"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string>
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ouvrir le menu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersif"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurer"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ancrer à gauche"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'application"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ouvrez rapidement des applications dans votre navigateur ici"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 97d5e51e5b98..bdd07476efcf 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Ventá nova"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string>
<string name="close_text" msgid="4986518933445178928">"Pechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir o menú"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Envolvente"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Axustar á esquerda"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na aplicación"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abre rapidamente aplicacións no navegador aquí"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 362ff8d874bb..d23c4fd8f0e0 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"બ્રાઉઝરમાં ખોલો"</string>
<string name="new_window_text" msgid="6318648868380652280">"નવી વિન્ડો"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string>
<string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"મેનૂ ખોલો"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ઇમર્સિવ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"રિસ્ટોર કરો"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"રિસ્ટોર કરો"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ડાબે સ્નૅપ કરો"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ઍપમાં"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"તમારા બ્રાઉઝરમાં"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ઓકે"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"તમારા બ્રાઉઝરમાં અહીં ઝડપથી ઍપ ખોલો"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 527793eac9c3..4eec6f877fab 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउज़र में खोलें"</string>
<string name="new_window_text" msgid="6318648868380652280">"नई विंडो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
<string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेन्यू खोलें"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ऐप्लिकेशन में"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"आपके ब्राउज़र में"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठीक है"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"अपने ब्राउज़र में तुरंत ऐप्लिकेशन खोलें"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 659d1ec39b73..a119d9e7f782 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Otvori u pregledniku"</string>
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string>
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvaranje izbornika"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"U redu"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ovdje brzo otvorite aplikacije u pregledniku"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 943b5eb30768..c07b6c3b9f1d 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Megnyitás böngészőben"</string>
<string name="new_window_text" msgid="6318648868380652280">"Új ablak"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string>
<string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menü megnyitása"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Magával ragadó"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Visszaállítás"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Visszaállítás"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Balra igazítás"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Az alkalmazásban"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"A böngészőben"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Itt gyorsan megnyithatja az alkalmazásokat a böngészőben"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 6bcfc9a22d6e..52eb18580de1 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -92,8 +92,8 @@
<string name="bubble_shortcut_label" msgid="666269077944378311">"Ամպիկներ"</string>
<string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Ցույց տալ ամպիկներ"</string>
<string name="restart_button_description" msgid="4564728020654658478">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար"</string>
- <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Փոխել հավելվածի կողմերի հարաբերակցությունը Կարգավորումներում"</string>
- <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Փոխել չափերի հարաբերակցությունը"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Փոխել հավելվածի կողմերի հարաբերությունը Կարգավորումներում"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Փոխել չափերի հարաբերությունը"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Բացել դիտարկիչում"</string>
<string name="new_window_text" msgid="6318648868380652280">"Նոր պատուհան"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string>
<string name="close_text" msgid="4986518933445178928">"Փակել"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Բացել ընտրացանկը"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Ներկայության էֆեկտով"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Վերականգնել"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Վերականգնել"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ամրացնել ձախ կողմում"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Հավելվածում"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Ձեր դիտարկիչում"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Եղավ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Արագ բացեք հավելվածները ձեր դիտարկիչում"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 96a3ebce9a45..f8f9d5e16439 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Buka di browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"Jendela Baru"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string>
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Buka Menu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imersif"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Pulihkan"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Maksimalkan ke kiri"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Di aplikasi"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Di browser Anda"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Oke"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Buka aplikasi di browser Anda dengan cepat di sini"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index ca1bc15edf33..8a9e3c0ca0a4 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Opna í vafra"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nýr gluggi"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string>
<string name="close_text" msgid="4986518933445178928">"Loka"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Opna valmynd"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Í forritinu"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Í vafranum"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Í lagi"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Opna forrit fljótt í vafranum þínum hér"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 87919b5dc1ff..138adefed160 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Apri nel browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nuova finestra"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string>
<string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Apri il menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"All\'interno dell\'app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Nel browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Apri rapidamente le app nel browser qui"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 17ffe8ee7600..917738dc1575 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -109,7 +109,7 @@
<string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"אפשר להפעיל מחדש את האפליקציה כדי שהיא תוצג באופן טוב יותר במסך, אבל ייתכן שההתקדמות שלך או כל שינוי שלא נשמר יאבדו"</string>
<string name="letterbox_restart_cancel" msgid="1342209132692537805">"ביטול"</string>
<string name="letterbox_restart_restart" msgid="8529976234412442973">"הפעלה מחדש"</string>
- <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"אין להציג שוב"</string>
+ <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"לא להציג שוב"</string>
<string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"אפשר להקיש הקשה כפולה כדי\nלהעביר את האפליקציה למקום אחר"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"הגדלה"</string>
<string name="minimize_button_text" msgid="271592547935841753">"מזעור"</string>
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"פתיחה בדפדפן"</string>
<string name="new_window_text" msgid="6318648868380652280">"חלון חדש"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי של יחס גובה-רוחב"</string>
<string name="close_text" msgid="4986518933445178928">"סגירה"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"פתיחת התפריט"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"סוחף"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"שחזור"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"שחזור"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"הצמדה לשמאל"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"באפליקציה"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"בדפדפן"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"אישור"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"כאן אפשר לפתוח אפליקציות בדפדפן במהירות"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index c7a77d9b9214..35c48212cf39 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ブラウザで開く"</string>
<string name="new_window_text" msgid="6318648868380652280">"新しいウィンドウ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string>
<string name="close_text" msgid="4986518933445178928">"閉じる"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"メニューを開く"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"アプリ内"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ブラウザ内"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ブラウザでアプリをすばやく開けます"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 39362ef4ca57..9b9966f152ee 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ბრაუზერში გახსნა"</string>
<string name="new_window_text" msgid="6318648868380652280">"ახალი ფანჯარა"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string>
<string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"მენიუს გახსნა"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"აპში"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"თქვენს ბრაუზერში"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"კარგი"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"სწრაფად გახსენით აპები თქვენს ბრაუზერში აქ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 45f85b9e9a70..8618ba9b2b0f 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Браузерден ашу"</string>
<string name="new_window_text" msgid="6318648868380652280">"Жаңа терезе"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string>
<string name="close_text" msgid="4986518933445178928">"Жабу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Мәзірді ашу"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Әсерлі"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Қалпына келтіру"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Қалпына келтіру"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солға тіркеу"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Қолданбада"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Браузерде"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайды"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Осындағы браузерде қолданбаларды жылдам ашуға болады."</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 9c4ae05f3a36..7f853f3e1e2f 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"បើកក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
<string name="new_window_text" msgid="6318648868380652280">"វិនដូ​ថ្មី"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រង​វិនដូ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរ​​សមាមាត្រ"</string>
<string name="close_text" msgid="4986518933445178928">"បិទ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"បើកម៉ឺនុយ"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"នៅក្នុងកម្មវិធី"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"នៅក្នុង​កម្មវិធីរុករកតាម​អ៊ីនធឺណិត​របស់អ្នក"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"យល់ព្រម"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"បើកកម្មវិធីយ៉ាងរហ័សនៅក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិតរបស់អ្នកនៅទីនេះ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index f365cfb34412..456dea2fdb0f 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ಬ್ರೌಸರ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string>
<string name="new_window_text" msgid="6318648868380652280">"ಹೊಸ ವಿಂಡೋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ಮೆನು ತೆರೆಯಿರಿ"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ಆ್ಯಪ್‌ನಲ್ಲಿ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ನಿಮ್ಮ ಬ್ರೌಸರ್‌ನಲ್ಲಿ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ಸರಿ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ಇಲ್ಲಿಂದ ನಿಮ್ಮ ಬ್ರೌಸರ್‌ನಲ್ಲಿ ಆ್ಯಪ್‌ಗಳನ್ನು ತ್ವರಿತವಾಗಿ ತೆರೆಯಿರಿ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 2bf1b05a919b..763cda738541 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"브라우저에서 열기"</string>
<string name="new_window_text" msgid="6318648868380652280">"새 창"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string>
<string name="close_text" msgid="4986518933445178928">"닫기"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"메뉴 열기"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"몰입형"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"복원"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"복원"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"왼쪽으로 맞추기"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"앱에서"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"브라우저에서"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"확인"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"이 브라우저에서 앱을 빠르게 여세요."</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 392ae4cab107..bffc3b1d11a8 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Серепчиден ачуу"</string>
<string name="new_window_text" msgid="6318648868380652280">"Жаңы терезе"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string>
<string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Менюну ачуу"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Сүңгүтүүчү"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Калыбына келтирүү"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Калыбына келтирүү"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солго жылдыруу"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Колдонмодо"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Серепчиңизде"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайт"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Бул жерде серепчиңизден колдонмолорду тез ачасыз"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 4e4b678755b2..b48b07067521 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ເປີດໃນໂປຣແກຣມທ່ອງເວັບ"</string>
<string name="new_window_text" msgid="6318648868380652280">"ໜ້າຈໍໃໝ່"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ຈັດການໜ້າຈໍ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string>
<string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ເປີດເມນູ"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ສົມຈິງ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ກູ້ຄືນ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ກູ້ຄືນ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ແນບຊ້າຍ"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ໃນແອັບ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ໃນໂປຣແກຣມທ່ອງເວັບຂອງທ່ານ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ຕົກລົງ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ເປີດແອັບຢ່າງວ່ອງໄວໃນໂປຣແກຣມທ່ອງເວັບຂອງທ່ານບ່ອນນີ້"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 5a7f58e5781a..d7a907cdc105 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Atidaryti naršyklėje"</string>
<string name="new_window_text" msgid="6318648868380652280">"Naujas langas"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string>
<string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Atidaryti meniu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Programoje"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Naršyklėje"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Gerai"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Čia greitai atidarykite programas naršyklėje"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 60912f627841..4ba7c2346a33 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Atvērt pārlūkā"</string>
<string name="new_window_text" msgid="6318648868380652280">"Jauns logs"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string>
<string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Atvērt izvēlni"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Iekļaujoši"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Atjaunot"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atjaunot"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Piestiprināt pa kreisi"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Lietotnē"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pārlūkprogrammā"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Labi"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"No šejienes varat ātri atvērt lietotnes savā pārlūkprogrammā"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 7c0c856ed1a7..d20eba55eac9 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Отвори во прелистувач"</string>
<string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорци"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string>
<string name="close_text" msgid="4986518933445178928">"Затворете"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отвори го менито"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Реалистично"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Врати"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Врати"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Фотографирај лево"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Во апликацијата"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Во прелистувачот"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Во ред"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Брзо отворајте ги апликациите во вашиот прелистувач овде"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index e14ab8b0161c..81c5094526c1 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ബ്രൗസറിൽ തുറക്കുക"</string>
<string name="new_window_text" msgid="6318648868380652280">"പുതിയ വിന്‍ഡോ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
<string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"മെനു തുറക്കുക"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്‌ക്രീൻ സ്‌നാപ്പ് ചെയ്യുക"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ഇമേഴ്‌സീവ്"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"പുനഃസ്ഥാപിക്കുക"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"പുനഃസ്ഥാപിക്കുക"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ഇടതുവശത്തേക്ക് സ്‌നാപ്പ് ചെയ്യുക"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ആപ്പിൽ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"നിങ്ങളുടെ ബ്രൗസറിൽ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ശരി"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"നിങ്ങളുടെ ബ്രൗസറിലെ ആപ്പുകൾ ഇവിടെ അതിവേഗം തുറക്കുക"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index d406b99e80b3..35da93e774b5 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Хөтчид нээх"</string>
<string name="new_window_text" msgid="6318648868380652280">"Шинэ цонх"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Windows-г удирдах"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Харьцааг өөрчлөх"</string>
<string name="close_text" msgid="4986518933445178928">"Хаах"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Цэсийг нээх"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Бодит мэт"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Сэргээх"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Сэргээх"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Зүүн тийш зэрэгцүүлэх"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Аппад"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Хөтчидөө"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Эндээс хөтчидөө аппуудыг шуурхай нээгээрэй"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 871bc3fcc8e7..c6b874a6780b 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउझरमध्ये उघडा"</string>
<string name="new_window_text" msgid="6318648868380652280">"नवीन विंडो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string>
<string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेनू उघडा"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ॲपमध्ये"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तुमच्या ब्राउझरमध्ये"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ओके"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"इथे तुमच्या ब्राउझरमध्ये अ‍ॅप्स झटपट उघडा"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 71666cae93c8..0fce0e9da45f 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Buka dalam penyemak imbas"</string>
<string name="new_window_text" msgid="6318648868380652280">"Tetingkap Baharu"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string>
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Buka Menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Pada apl"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pada penyemak imbas"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Buka apl dengan pantas dalam penyemak imbas anda di sini"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index ae34624c98a0..abc2a19ba989 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ဘရောင်ဇာတွင် ဖွင့်ရန်"</string>
<string name="new_window_text" msgid="6318648868380652280">"ဝင်းဒိုးအသစ်"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string>
<string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"မီနူး ဖွင့်ရန်"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"သုံးဘက်မြင်"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ပြန်ပြောင်းရန်"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ပြန်ပြောင်းရန်"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ဘယ်တွင် ချဲ့ရန်"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"အက်ပ်တွင်"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"သင်၏ဘရောင်ဇာတွင်"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"အက်ပ်များကို သင့်ဘရောင်ဇာတွင် ဤနေရာ၌ အမြန်ဖွင့်နိုင်သည်"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 9270dc859728..ed6fb900564f 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Åpne i nettleseren"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nytt vindu"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string>
<string name="close_text" msgid="4986518933445178928">"Lukk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Åpne menyen"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Oppslukende"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Gjenopprett"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gjenopprett"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fest til venstre"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I nettleseren"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Her kan du raskt åpne apper i nettleseren"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 7015b2c11b32..aff712901ff6 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउजरमा खोल्नुहोस्"</string>
<string name="new_window_text" msgid="6318648868380652280">"नयाँ विन्डो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
<string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेनु खोल्नुहोस्"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"एपमा"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तपाईंको ब्राउजरमा"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठिक छ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"आफ्नो ब्राउजरबाट यहाँ तुरुन्तै एपहरू खोल्नुहोस्"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 45305d62a69b..8db3a0ecfc2f 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Openen in browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nieuw venster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Vensters beheren"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string>
<string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menu openen"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersief"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Herstellen"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Herstellen"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links uitlijnen"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In de app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In je browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Open hier snel apps in je browser"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 2d30441ab6f4..69540898161c 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ"</string>
<string name="new_window_text" msgid="6318648868380652280">"ନୂଆ ୱିଣ୍ଡୋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ମେନୁ ଖୋଲନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ଇମର୍ସିଭ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ବାମରେ ସ୍ନାପ କରନ୍ତୁ"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ଆପରେ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ଆପଣଙ୍କ ବ୍ରାଉଜରରେ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ଠିକ ଅଛି"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ଏଠାରେ ଆପଣଙ୍କ ବ୍ରାଉଜରରେ ଥିବା ଆପ୍ସକୁ ଶୀଘ୍ର ଖୋଲନ୍ତୁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 26ba461cba5d..c627d7fcc2c5 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string>
<string name="new_window_text" msgid="6318648868380652280">"ਨਵੀਂ ਵਿੰਡੋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ਵਿੰਡੋਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
<string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ਇਮਰਸਿਵ"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ਖੱਬੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ਐਪ ਵਿੱਚ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ਤੁਹਾਡੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ਠੀਕ ਹੈ"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ਇੱਥੇ ਆਪਣੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਤੇਜ਼ੀ ਨਾਲ ਐਪਾਂ ਖੋਲ੍ਹੋ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 5f78b134ad22..a138c08d319a 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Otwórz w przeglądarce"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nowe okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Zarządzaj oknami"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmień format obrazu"</string>
<string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otwórz menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"W aplikacji"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"W przeglądarce"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Tutaj możesz szybko otwierać aplikacje w przeglądarce"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 8c7f9e73296d..9942f6980306 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -52,10 +52,10 @@
<string name="accessibility_split_right" msgid="8441001008181296837">"Dividir para a direita"</string>
<string name="accessibility_split_top" msgid="2789329702027147146">"Dividir para cima"</string>
<string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir para baixo"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo uma mão"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo uma mão"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo uma mão"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string>
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir o menu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imersivo"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abra apps no navegador rapidamente aqui"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index cd78ef95d88e..559eea2d3cab 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Faça a gestão das janelas"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Alterar formato"</string>
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abra rapidamente apps no navegador aqui"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 8c7f9e73296d..9942f6980306 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -52,10 +52,10 @@
<string name="accessibility_split_right" msgid="8441001008181296837">"Dividir para a direita"</string>
<string name="accessibility_split_top" msgid="2789329702027147146">"Dividir para cima"</string>
<string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir para baixo"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo uma mão"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo uma mão"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo uma mão"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string>
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir o menu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Imersivo"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abra apps no navegador rapidamente aqui"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index e3fe2804bdcd..df0ee45695b2 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Deschide în browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"Fereastră nouă"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionează ferestrele"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string>
<string name="close_text" msgid="4986518933445178928">"Închide"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Deschide meniul"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Captivant"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restabilește"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizează"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restabilește"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Trage la stânga"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"În aplicație"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"În browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Deschide rapid aplicații în browser aici"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 442fca3ef0a7..430f1b1448da 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Открыть в браузере"</string>
<string name="new_window_text" msgid="6318648868380652280">"Новое окно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управление окнами"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string>
<string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Открыть меню"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Погружение"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Восстановить"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Развернуть"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Восстановить"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Привязать слева"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложении"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузере"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Здесь можно быстро открывать приложения в браузере"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 8a7ad3b9f80c..3e3766768a7f 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"බ්‍රව්සරයේ විවෘත කරන්න"</string>
<string name="new_window_text" msgid="6318648868380652280">"නව කවුළුව"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"කවුළු කළමනාකරණය කරන්න"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
<string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"මෙනුව විවෘත කරන්න"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ගිලෙන සුළු"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ප්‍රතිසාධනය කරන්න"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ප්‍රතිසාධනය කරන්න"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"වමට ස්නැප් කරන්න"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"යෙදුම තුළ"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ඔබේ බ්‍රව්සරය තුළ"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"හරි"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ඔබේ බ්‍රව්සරයේ යෙදුම් ඉක්මනින් විවෘත කරන්න"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4234e8073bc8..56a7edd08b23 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Otvoriť v prehliadači"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Správa okien"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string>
<string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvoriť ponuku"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Pútavé"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnoviť"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovať"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnoviť"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prichytiť vľavo"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikácii"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prehliadači"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Tu môžete rýchlo otvárať aplikácie v prehliadači"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index ae7e524da6cc..b6344c981fc9 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Odpri v brskalniku"</string>
<string name="new_window_text" msgid="6318648868380652280">"Novo okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje oken"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string>
<string name="close_text" msgid="4986518933445178928">"Zapri"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Odpri meni"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaciji"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V brskalniku"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"V redu"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Tukaj hitro odprete aplikacije v brskalniku"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index de6f681cfe74..ab7499d505f8 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Hape në shfletues"</string>
<string name="new_window_text" msgid="6318648868380652280">"Dritare e re"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Menaxho dritaret"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string>
<string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Hap menynë"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Përfshirës"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restauro"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restauro"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Zhvendos majtas"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Në aplikacion"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Në shfletuesin tënd"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Në rregull"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Hapi me shpejtësi aplikacionet në shfletues këtu"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 901d6d967a7d..773ed16dc4b9 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Отворите у прегледачу"</string>
<string name="new_window_text" msgid="6318648868380652280">"Нови прозор"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управљајте прозорима"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промените размеру"</string>
<string name="close_text" msgid="4986518933445178928">"Затворите"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отворите мени"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Имерзивне"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Врати"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увећајте"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Вратите"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прикачите лево"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У апликацији"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У прегледачу"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Потврди"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Овде можете брзо да отварате апликације у прегледачу"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 6566801b7c63..6f6a97b4495f 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Öppna i webbläsaren"</string>
<string name="new_window_text" msgid="6318648868380652280">"Nytt fönster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Hantera fönster"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string>
<string name="close_text" msgid="4986518933445178928">"Stäng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Öppna menyn"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Uppslukande"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Återställ"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Återställ"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I webbläsaren"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Öppna snabbt appar i webbläsaren här"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index a952011385de..72b7384fa83a 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Fungua katika kivinjari"</string>
<string name="new_window_text" msgid="6318648868380652280">"Dirisha Jipya"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Dhibiti Windows"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string>
<string name="close_text" msgid="4986518933445178928">"Funga"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Fungua Menyu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Shirikishi"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Rejesha"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Panua"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Rejesha"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Telezesha kushoto"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Kwenye programu"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kwenye kivinjari chako"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Sawa"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Fungua programu kwa haraka katika kivinjari chako hapa"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 2c73d3a14620..9d902912b377 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"உலாவியில் திறக்கும்"</string>
<string name="new_window_text" msgid="6318648868380652280">"புதிய சாளரம்"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"சாளரங்களை நிர்வகிக்கலாம்"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string>
<string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"மெனுவைத் திறக்கும்"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ஆப்ஸில்"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"உங்கள் பிரவுசரில்"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"சரி"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"உங்கள் உலாவியில் ஆப்ஸை இங்கே விரைவாகத் திறக்கலாம்"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index b17d4d1afaf7..3c7c06a4fc1a 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"బ్రౌజర్‌లో తెరవండి"</string>
<string name="new_window_text" msgid="6318648868380652280">"కొత్త విండో"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"విండోలను మేనేజ్ చేయండి"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string>
<string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"మెనూను తెరవండి"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"యాప్‌లో"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"మీ బ్రౌజర్‌లో"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"సరే"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"మీ బ్రౌజర్‌లో ఇక్కడ యాప్‌లను వేగంగా తెరవండి"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 43cee41f5a15..9071bfb66e92 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"เปิดในเบราว์เซอร์"</string>
<string name="new_window_text" msgid="6318648868380652280">"หน้าต่างใหม่"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"จัดการหน้าต่าง"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"เปลี่ยนสัดส่วนการแสดงผล"</string>
<string name="close_text" msgid="4986518933445178928">"ปิด"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"เปิดเมนู"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ในแอป"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ในเบราว์เซอร์"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ตกลง"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"เปิดแอปในเบราว์เซอร์ได้อย่างรวดเร็วที่นี่"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 428499532005..a00f7839186b 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Buksan sa browser"</string>
<string name="new_window_text" msgid="6318648868380652280">"Bagong Window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pamahalaan ang Mga Window"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string>
<string name="close_text" msgid="4986518933445178928">"Isara"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Buksan ang Menu"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sa app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Sa iyong browser"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Mabilis na buksan ang mga app sa iyong browser dito"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 7eac4a8e4ffb..8310a66e9d33 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Tarayıcıda aç"</string>
<string name="new_window_text" msgid="6318648868380652280">"Yeni Pencere"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pencereleri yönet"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string>
<string name="close_text" msgid="4986518933445178928">"Kapat"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menüyü aç"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Etkileyici"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Geri yükle"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ekranı kapla"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Geri yükle"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tuttur"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Uygulamada"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Tarayıcınızda"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Tamam"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Buradan tarayıcınızda uygulamaları hızlıca açın"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 5fb14bf50e10..624a19e67724 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Відкрити у вебпереглядачі"</string>
<string name="new_window_text" msgid="6318648868380652280">"Нове вікно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Керувати вікнами"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змінити формат"</string>
<string name="close_text" msgid="4986518933445178928">"Закрити"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Відкрити меню"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Реалістичність"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Відновити"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Розгорнути"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Відновити"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Закріпити ліворуч"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У додатку"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У вебпереглядачі"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Швидко відкривайте додатки у вебпереглядачі"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index bb0358f12b7a..2ccaf50fee21 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"براؤزر میں کھولیں"</string>
<string name="new_window_text" msgid="6318648868380652280">"نئی ونڈو"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"‏‫Windows کا نظم کریں"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string>
<string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"مینو کھولیں"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"عمیق"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"بحال کریں"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بڑا کریں"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بحال کریں"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"دائیں منتقل کریں"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ایپ میں"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"آپ کے براؤزر میں"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ٹھیک ہے"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"اپنے براؤزر میں ایپس کو فوری طور پر یہاں کھولیں"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 0648dd1c1bb8..88edc929528b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerda ochish"</string>
<string name="new_window_text" msgid="6318648868380652280">"Yangi oyna"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Oynalarni boshqarish"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string>
<string name="close_text" msgid="4986518933445178928">"Yopish"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menyuni ochish"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersiv"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Tiklash"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Yoyish"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Tiklash"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chapga tortish"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ilovada"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerda"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Brauzerda ilovalarni shu yerda tezkor oching"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index dda2225b5f3e..c1c7653ce90c 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Mở trong trình duyệt"</string>
<string name="new_window_text" msgid="6318648868380652280">"Cửa sổ mới"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Quản lý cửa sổ"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string>
<string name="close_text" msgid="4986518933445178928">"Đóng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Mở Trình đơn"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Hiển thị tối đa"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Khôi phục"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Phóng to tối đa"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Khôi phục"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Di chuyển nhanh sang trái"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Trong ứng dụng"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Trên trình duyệt"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Mở nhanh các ứng dụng trong trình duyệt tại đây"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 2fb3f5ab5ea4..83e15d8cbdf6 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -127,6 +127,7 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"在浏览器中打开"</string>
<string name="new_window_text" msgid="6318648868380652280">"新窗口"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理窗口"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"更改宽高比"</string>
<string name="close_text" msgid="4986518933445178928">"关闭"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"打开菜单"</string>
@@ -144,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在此应用内"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在浏览器中"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"确定"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"在此处快速在浏览器中打开应用"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 1d7fb4c4c6e2..f60b3efc6f38 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
<string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string>
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"打開選單"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"身歷其境"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"還原"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"貼齊左邊"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在應用程式內"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在瀏覽器中"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"在此透過瀏覽器快速開啟應用程式"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 8083e378bf29..b2227deeccc3 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
<string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string>
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"開啟選單"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"沉浸"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"還原"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"靠左對齊"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"使用應用程式"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"使用瀏覽器"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"在這個瀏覽器中快速開啟應用程式"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 092efd6593fc..10d904fa17d2 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -127,16 +127,15 @@
<string name="open_in_browser_text" msgid="9181692926376072904">"Vula kubhrawuza"</string>
<string name="new_window_text" msgid="6318648868380652280">"Iwindi Elisha"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Phatha Amawindi"</string>
+ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string>
<string name="close_text" msgid="4986518933445178928">"Vala"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Vula Imenyu"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string>
- <!-- no translation found for desktop_mode_maximize_menu_immersive_button_text (559492223133829481) -->
- <skip />
- <!-- no translation found for desktop_mode_maximize_menu_immersive_restore_button_text (4900114367354709257) -->
- <skip />
+ <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Okugxilile"</string>
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Buyisela"</string>
<string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Khulisa"</string>
<string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Buyisela"</string>
<string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chofoza kwesobunxele"</string>
@@ -146,4 +145,5 @@
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ku-app"</string>
<string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kubhrawuza yakho"</string>
<string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"KULUNGILE"</string>
+ <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ngokushesha vula ama-app ebhrawuzeni yakho lapha"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 34f950c0a326..249e9a26e6a6 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -523,8 +523,9 @@
<dimen name="desktop_mode_handle_menu_width">216dp</dimen>
<!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
- additional actions pill 156dp, plus 2dp spacing between them plus 4dp top padding. -->
- <dimen name="desktop_mode_handle_menu_height">322dp</dimen>
+ additional actions pill 208dp, plus 2dp spacing between them plus 4dp top padding.
+ 52*3 + 52*4 + (4-1)*2 + 4 = 374 -->
+ <dimen name="desktop_mode_handle_menu_height">374dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -547,6 +548,9 @@
<!-- The height of the handle menu's "Open in browser" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_open_in_browser_pill_height">52dp</dimen>
+ <!-- The height of the handle menu's "Change aspect ratio" pill in desktop mode. -->
+ <dimen name="desktop_mode_handle_menu_change_aspect_ratio_height">52dp</dimen>
+
<!-- The margin between pills of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_pill_spacing_margin">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 52585d4ead35..012579a6d40c 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -301,10 +301,14 @@
<string name="screenshot_text">Screenshot</string>
<!-- Accessibility text for the handle menu open in browser button [CHAR LIMIT=NONE] -->
<string name="open_in_browser_text">Open in browser</string>
+ <!-- Accessibility text for the handle menu open in app button [CHAR LIMIT=NONE] -->
+ <string name="open_in_app_text">Open in App</string>
<!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] -->
<string name="new_window_text">New Window</string>
<!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] -->
<string name="manage_windows_text">Manage Windows</string>
+ <!-- Accessibility text for the handle menu change aspect ratio button [CHAR LIMIT=NONE] -->
+ <string name="change_aspect_ratio_text">Change aspect ratio</string>
<!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
<string name="close_text">Close</string>
<!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.aidl
index e21bf8fb723c..93e635dd937c 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.aidl
@@ -16,4 +16,4 @@
package com.android.wm.shell.shared;
-parcelable GroupedRecentTaskInfo; \ No newline at end of file
+parcelable GroupedTaskInfo; \ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 65e079ef4f72..03e0ab0591a1 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -17,7 +17,8 @@
package com.android.wm.shell.shared;
import android.annotation.IntDef;
-import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,69 +28,91 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.shared.split.SplitBounds;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
- * Simple container for recent tasks. May contain either a single or pair of tasks.
+ * Simple container for recent tasks which should be presented as a single task within the
+ * Overview UI.
*/
-public class GroupedRecentTaskInfo implements Parcelable {
+public class GroupedTaskInfo implements Parcelable {
- public static final int TYPE_SINGLE = 1;
+ public static final int TYPE_FULLSCREEN = 1;
public static final int TYPE_SPLIT = 2;
public static final int TYPE_FREEFORM = 3;
@IntDef(prefix = {"TYPE_"}, value = {
- TYPE_SINGLE,
+ TYPE_FULLSCREEN,
TYPE_SPLIT,
TYPE_FREEFORM
})
public @interface GroupType {}
+ /**
+ * The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or
+ * TYPE_FREEFORM.
+ */
+ @GroupType
+ protected final int mType;
+
+ /**
+ * The list of tasks associated with this single recent task info.
+ * TYPE_FULLSCREEN: Contains the stack of tasks associated with a single "task" in overview
+ * TYPE_SPLIT: Contains the two split roots of each side
+ * TYPE_FREEFORM: Contains the set of tasks currently in freeform mode
+ */
@NonNull
- private final ActivityManager.RecentTaskInfo[] mTasks;
+ protected final List<TaskInfo> mTasks;
+
+ /**
+ * Only set for TYPE_SPLIT.
+ *
+ * Information about the split bounds.
+ */
@Nullable
- private final SplitBounds mSplitBounds;
- @GroupType
- private final int mType;
- // TODO(b/348332802): move isMinimized inside each Task object instead once we have a
- // replacement for RecentTaskInfo
- private final int[] mMinimizedTaskIds;
+ protected final SplitBounds mSplitBounds;
/**
- * Create new for a single task
+ * Only set for TYPE_FREEFORM.
+ *
+ * TODO(b/348332802): move isMinimized inside each Task object instead once we have a
+ * replacement for RecentTaskInfo
*/
- public static GroupedRecentTaskInfo forSingleTask(
- @NonNull ActivityManager.RecentTaskInfo task) {
- return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
- TYPE_SINGLE, null /* minimizedFreeformTasks */);
+ @Nullable
+ protected final int[] mMinimizedTaskIds;
+
+ /**
+ * Create new for a stack of fullscreen tasks
+ */
+ public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) {
+ return new GroupedTaskInfo(List.of(task), null, TYPE_FULLSCREEN,
+ null /* minimizedFreeformTasks */);
}
/**
* Create new for a pair of tasks in split screen
*/
- public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
- @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
- return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
- splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */);
+ public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1,
+ @NonNull TaskInfo task2, @Nullable SplitBounds splitBounds) {
+ return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT,
+ null /* minimizedFreeformTasks */);
}
/**
* Create new for a group of freeform tasks
*/
- public static GroupedRecentTaskInfo forFreeformTasks(
- @NonNull ActivityManager.RecentTaskInfo[] tasks,
- @NonNull Set<Integer> minimizedFreeformTasks) {
- return new GroupedRecentTaskInfo(
- tasks,
- null /* splitBounds */,
- TYPE_FREEFORM,
+ public static GroupedTaskInfo forFreeformTasks(
+ @NonNull List<TaskInfo> tasks,
+ @NonNull Set<Integer> minimizedFreeformTasks) {
+ return new GroupedTaskInfo(tasks, null /* splitBounds */, TYPE_FREEFORM,
minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
}
- private GroupedRecentTaskInfo(
- @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ private GroupedTaskInfo(
+ @NonNull List<TaskInfo> tasks,
@Nullable SplitBounds splitBounds,
@GroupType int type,
@Nullable int[] minimizedFreeformTaskIds) {
@@ -100,52 +123,56 @@ public class GroupedRecentTaskInfo implements Parcelable {
ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
}
- private static void ensureAllMinimizedIdsPresent(
- @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ private void ensureAllMinimizedIdsPresent(
+ @NonNull List<TaskInfo> tasks,
@Nullable int[] minimizedFreeformTaskIds) {
if (minimizedFreeformTaskIds == null) {
return;
}
if (!Arrays.stream(minimizedFreeformTaskIds).allMatch(
- taskId -> Arrays.stream(tasks).anyMatch(task -> task.taskId == taskId))) {
+ taskId -> tasks.stream().anyMatch(task -> task.taskId == taskId))) {
throw new IllegalArgumentException("Minimized task IDs contain non-existent Task ID.");
}
}
- GroupedRecentTaskInfo(Parcel parcel) {
- mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
+ protected GroupedTaskInfo(@NonNull Parcel parcel) {
+ mTasks = new ArrayList();
+ final int numTasks = parcel.readInt();
+ for (int i = 0; i < numTasks; i++) {
+ mTasks.add(new TaskInfo(parcel));
+ }
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
mType = parcel.readInt();
mMinimizedTaskIds = parcel.createIntArray();
}
/**
- * Get primary {@link ActivityManager.RecentTaskInfo}
+ * Get primary {@link RecentTaskInfo}
*/
@NonNull
- public ActivityManager.RecentTaskInfo getTaskInfo1() {
- return mTasks[0];
+ public TaskInfo getTaskInfo1() {
+ return mTasks.getFirst();
}
/**
- * Get secondary {@link ActivityManager.RecentTaskInfo}.
+ * Get secondary {@link RecentTaskInfo}.
*
* Used in split screen.
*/
@Nullable
- public ActivityManager.RecentTaskInfo getTaskInfo2() {
- if (mTasks.length > 1) {
- return mTasks[1];
+ public TaskInfo getTaskInfo2() {
+ if (mTasks.size() > 1) {
+ return mTasks.get(1);
}
return null;
}
/**
- * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
+ * Get all {@link RecentTaskInfo}s grouped together.
*/
@NonNull
- public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
- return Arrays.asList(mTasks);
+ public List<TaskInfo> getTaskInfoList() {
+ return mTasks;
}
/**
@@ -164,28 +191,46 @@ public class GroupedRecentTaskInfo implements Parcelable {
return mType;
}
+ @Nullable
public int[] getMinimizedTaskIds() {
return mMinimizedTaskIds;
}
@Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof GroupedTaskInfo)) {
+ return false;
+ }
+ GroupedTaskInfo other = (GroupedTaskInfo) obj;
+ return mType == other.mType
+ && Objects.equals(mTasks, other.mTasks)
+ && Objects.equals(mSplitBounds, other.mSplitBounds)
+ && Arrays.equals(mMinimizedTaskIds, other.mMinimizedTaskIds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds));
+ }
+
+ @Override
public String toString() {
StringBuilder taskString = new StringBuilder();
- for (int i = 0; i < mTasks.length; i++) {
+ for (int i = 0; i < mTasks.size(); i++) {
if (i == 0) {
taskString.append("Task");
} else {
taskString.append(", Task");
}
- taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
+ taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks.get(i)));
}
if (mSplitBounds != null) {
taskString.append(", SplitBounds: ").append(mSplitBounds);
}
taskString.append(", Type=");
switch (mType) {
- case TYPE_SINGLE:
- taskString.append("TYPE_SINGLE");
+ case TYPE_FULLSCREEN:
+ taskString.append("TYPE_FULLSCREEN");
break;
case TYPE_SPLIT:
taskString.append("TYPE_SPLIT");
@@ -199,7 +244,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
return taskString.toString();
}
- private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
+ private String getTaskInfo(TaskInfo taskInfo) {
if (taskInfo == null) {
return null;
}
@@ -213,7 +258,12 @@ public class GroupedRecentTaskInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedArray(mTasks, flags);
+ // We don't use the parcel list methods because we want to only write the TaskInfo state
+ // and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated
+ parcel.writeInt(mTasks.size());
+ for (int i = 0; i < mTasks.size(); i++) {
+ mTasks.get(i).writeTaskToParcel(parcel, flags);
+ }
parcel.writeTypedObject(mSplitBounds, flags);
parcel.writeInt(mType);
parcel.writeIntArray(mMinimizedTaskIds);
@@ -224,13 +274,15 @@ public class GroupedRecentTaskInfo implements Parcelable {
return 0;
}
- public static final @android.annotation.NonNull Creator<GroupedRecentTaskInfo> CREATOR =
- new Creator<GroupedRecentTaskInfo>() {
- public GroupedRecentTaskInfo createFromParcel(Parcel source) {
- return new GroupedRecentTaskInfo(source);
+ public static final Creator<GroupedTaskInfo> CREATOR = new Creator() {
+ @Override
+ public GroupedTaskInfo createFromParcel(Parcel in) {
+ return new GroupedTaskInfo(in);
}
- public GroupedRecentTaskInfo[] newArray(int size) {
- return new GroupedRecentTaskInfo[size];
+
+ @Override
+ public GroupedTaskInfo[] newArray(int size) {
+ return new GroupedTaskInfo[size];
}
};
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index 88878c6adcf2..e033f673d07d 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -360,7 +360,7 @@ public class TransitionUtil {
windowConfiguration = new WindowConfiguration();
}
- Rect localBounds = new Rect();
+ Rect bounds = windowConfiguration.getBounds();
RemoteAnimationTarget target = new RemoteAnimationTarget(
taskId,
newModeToLegacyMode(mode),
@@ -373,12 +373,12 @@ public class TransitionUtil {
new Rect(0, 0, 0, 0),
order,
null,
- localBounds,
- new Rect(),
+ bounds,
+ bounds,
windowConfiguration,
isNotInRecents,
null,
- new Rect(),
+ bounds,
taskInfo,
false,
INVALID_WINDOW_TYPE
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt
index fc3dc1465dff..f93b35e868f6 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/PhysicsAnimatorTestUtils.kt
@@ -20,7 +20,7 @@ import android.os.Looper
import android.util.ArrayMap
import androidx.dynamicanimation.animation.FloatPropertyCompat
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils.prepareForTest
-import java.util.*
+import java.util.ArrayDeque
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList
@@ -74,14 +74,17 @@ object PhysicsAnimatorTestUtils {
@JvmStatic
fun tearDown() {
- val latch = CountDownLatch(1)
- animationThreadHandler.post {
+ if (Looper.myLooper() == animationThreadHandler.looper) {
animatorTestHelpers.keys.forEach { it.cancel() }
- latch.countDown()
+ } else {
+ val latch = CountDownLatch(1)
+ animationThreadHandler.post {
+ animatorTestHelpers.keys.forEach { it.cancel() }
+ latch.countDown()
+ }
+ latch.await(5, TimeUnit.SECONDS)
}
- latch.await()
-
animatorTestHelpers.clear()
animators.clear()
allAnimatedObjects.clear()
@@ -348,8 +351,9 @@ object PhysicsAnimatorTestUtils {
* Returns all of the values that have ever been reported to update listeners, per property.
*/
@Suppress("UNCHECKED_CAST")
- fun <T : Any> getAnimationUpdateFrames(animator: PhysicsAnimator<T>):
- UpdateFramesPerProperty<T> {
+ fun <T : Any> getAnimationUpdateFrames(
+ animator: PhysicsAnimator<T>
+ ): UpdateFramesPerProperty<T> {
return animatorTestHelpers[animator]?.getUpdates() as UpdateFramesPerProperty<T>
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
index 7086691e7431..bd129a28f049 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
@@ -56,6 +56,7 @@ abstract class BaseBubblePinController(private val screenSizeProvider: () -> Poi
onLeft = initialLocationOnLeft
screenCenterX = screenSizeProvider.invoke().x / 2
dismissZone = getExclusionRect()
+ listener?.onStart(if (initialLocationOnLeft) LEFT else RIGHT)
}
/** View has moved to [x] and [y] screen coordinates */
@@ -109,6 +110,7 @@ abstract class BaseBubblePinController(private val screenSizeProvider: () -> Poi
/** Get width for exclusion rect where dismiss takes over drag */
protected abstract fun getExclusionRectWidth(): Float
+
/** Get height for exclusion rect where dismiss takes over drag */
protected abstract fun getExclusionRectHeight(): Float
@@ -184,6 +186,9 @@ abstract class BaseBubblePinController(private val screenSizeProvider: () -> Poi
/** Receive updates on location changes */
interface LocationChangeListener {
+ /** Bubble bar dragging has started. Includes the initial location of the bar */
+ fun onStart(location: BubbleBarLocation) {}
+
/**
* Bubble bar has been dragged to a new [BubbleBarLocation]. And the drag is still in
* progress.
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
index 191875d38daf..84a22b873aaf 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.shared.bubbles
+import android.annotation.IntDef
import android.os.Parcel
import android.os.Parcelable
@@ -60,4 +61,36 @@ enum class BubbleBarLocation : Parcelable {
override fun newArray(size: Int) = arrayOfNulls<BubbleBarLocation>(size)
}
}
+
+ /** Define set of constants that allow to determine why location changed. */
+ @IntDef(
+ UpdateSource.DRAG_BAR,
+ UpdateSource.DRAG_BUBBLE,
+ UpdateSource.DRAG_EXP_VIEW,
+ UpdateSource.A11Y_ACTION_BAR,
+ UpdateSource.A11Y_ACTION_BUBBLE,
+ UpdateSource.A11Y_ACTION_EXP_VIEW,
+ )
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class UpdateSource {
+ companion object {
+ /** Location changed from dragging the bar */
+ const val DRAG_BAR = 1
+
+ /** Location changed from dragging the bubble */
+ const val DRAG_BUBBLE = 2
+
+ /** Location changed from dragging the expanded view */
+ const val DRAG_EXP_VIEW = 3
+
+ /** Location changed via a11y action on the bar */
+ const val A11Y_ACTION_BAR = 4
+
+ /** Location changed via a11y action on the bubble */
+ const val A11Y_ACTION_BUBBLE = 5
+
+ /** Location changed via a11y action on the expanded view */
+ const val A11Y_ACTION_EXP_VIEW = 6
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/OWNERS
new file mode 100644
index 000000000000..20d5c33dc8bf
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/OWNERS
@@ -0,0 +1,4 @@
+# WM shell sub-module PiP owner
+hwwang@google.com
+gabiyev@google.com
+wuperry@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f59fed906e2d..dfe76b8543e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -487,6 +487,20 @@ public class ShellTaskOrganizer extends TaskOrganizer {
return mHomeTaskOverlayContainer;
}
+ /**
+ * Returns the home task surface, not for wide use.
+ */
+ @Nullable
+ public SurfaceControl getHomeTaskSurface() {
+ for (int i = 0; i < mTasks.size(); i++) {
+ final TaskAppearedInfo info = mTasks.valueAt(i);
+ if (info.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
+ return info.getLeash();
+ }
+ }
+ return null;
+ }
+
@Override
public void addStartingWindow(StartingWindowInfo info) {
if (mStartingWindow != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
index 65132fe89063..7243ea36b137 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -20,7 +20,9 @@ package com.android.wm.shell.apptoweb
import android.content.Context
import android.content.Intent
+import android.content.Intent.ACTION_VIEW
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.content.Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER
import android.content.pm.PackageManager
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationUserState
@@ -31,7 +33,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup
private const val TAG = "AppToWebUtils"
private val GenericBrowserIntent = Intent()
- .setAction(Intent.ACTION_VIEW)
+ .setAction(ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse("http:"))
@@ -67,6 +69,20 @@ fun getBrowserIntent(uri: Uri, packageManager: PackageManager): Intent? {
}
/**
+ * Returns intent if there is a non-browser application available to handle the uri. Otherwise,
+ * returns null.
+ */
+fun getAppIntent(uri: Uri, packageManager: PackageManager): Intent? {
+ val intent = Intent(ACTION_VIEW, uri).apply {
+ flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
+ }
+ // If there is no application available to handle intent, return null
+ val component = intent.resolveActivity(packageManager) ?: return null
+ intent.setComponent(component)
+ return intent
+}
+
+/**
* Returns the [DomainVerificationUserState] of the user associated with the given
* [DomainVerificationManager] and the given package.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index f296c710f9a0..ce7a97703f44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -21,6 +21,7 @@ import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
+import static android.window.BackEvent.EDGE_NONE;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
@@ -29,6 +30,7 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
import static com.android.window.flags.Flags.migratePredictiveBackTransition;
import static com.android.window.flags.Flags.predictiveBackSystemAnims;
+import static com.android.window.flags.Flags.unifyBackNavigationTransition;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
@@ -532,7 +534,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (keyAction == MotionEvent.ACTION_DOWN) {
if (!mBackGestureStarted) {
- mShouldStartOnNextMoveEvent = true;
+ if (swipeEdge == EDGE_NONE) {
+ // start animation immediately for non-gestural sources (without ACTION_MOVE
+ // events)
+ mThresholdCrossed = true;
+ onGestureStarted(touchX, touchY, swipeEdge);
+ mShouldStartOnNextMoveEvent = false;
+ } else {
+ mShouldStartOnNextMoveEvent = true;
+ }
}
} else if (keyAction == MotionEvent.ACTION_MOVE) {
if (!mBackGestureStarted && mShouldStartOnNextMoveEvent) {
@@ -1073,6 +1083,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mCurrentTracker.updateStartLocation();
BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
dispatchOnBackStarted(mActiveCallback, startEvent);
+ // TODO(b/373544911): onBackStarted is dispatched here so that
+ // WindowOnBackInvokedDispatcher knows about the back navigation and intercepts touch
+ // events while it's active. It would be cleaner and safer to disable multitouch
+ // altogether (same as in gesture-nav).
+ dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent);
}
}
@@ -1107,7 +1122,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final BackMotionEvent backFinish = mCurrentTracker
.createProgressEvent();
dispatchOnBackProgressed(mActiveCallback, backFinish);
- if (!mBackGestureStarted) {
+ if (mCurrentTracker.isFinished()) {
// if the down -> up gesture happened before animation
// start, we have to trigger the uninterruptible transition
// to finish the back animation.
@@ -1279,6 +1294,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (transition == mClosePrepareTransition && aborted) {
mClosePrepareTransition = null;
applyFinishOpenTransition();
+ } else if (!aborted && unifyBackNavigationTransition()) {
+ // Since the closing target participates in the predictive back transition, the
+ // merged transition must be applied with the first transition to ensure a seamless
+ // animation.
+ if (mFinishOpenTransaction != null && finishTransaction != null) {
+ mFinishOpenTransaction.merge(finishTransaction);
+ }
}
}
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 5f0eed9daa1a..39dc26797a81 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
@@ -740,8 +740,10 @@ public class BubbleController implements ConfigurationChangeListener,
/**
* Update bubble bar location and trigger and update to listeners
*/
- public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
+ @BubbleBarLocation.UpdateSource int source) {
if (canShowAsBubbleBar()) {
+ BubbleBarLocation previousLocation = mBubblePositioner.getBubbleBarLocation();
mBubblePositioner.setBubbleBarLocation(bubbleBarLocation);
if (mLayerView != null && !mLayerView.isExpandedViewDragged()) {
mLayerView.updateExpandedView();
@@ -749,13 +751,47 @@ public class BubbleController implements ConfigurationChangeListener,
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
bubbleBarUpdate.bubbleBarLocation = bubbleBarLocation;
mBubbleStateListener.onBubbleStateChange(bubbleBarUpdate);
+
+ logBubbleBarLocationIfChanged(bubbleBarLocation, previousLocation, source);
+ }
+ }
+
+ private void logBubbleBarLocationIfChanged(BubbleBarLocation location,
+ BubbleBarLocation previous,
+ @BubbleBarLocation.UpdateSource int source) {
+ if (mLayerView == null) {
+ return;
+ }
+ boolean isRtl = mLayerView.isLayoutRtl();
+ boolean wasLeft = previous.isOnLeft(isRtl);
+ boolean onLeft = location.isOnLeft(isRtl);
+ if (wasLeft == onLeft) {
+ // No changes, skip logging
+ return;
+ }
+ switch (source) {
+ case BubbleBarLocation.UpdateSource.DRAG_BAR:
+ case BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR:
+ mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR
+ : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR);
+ break;
+ case BubbleBarLocation.UpdateSource.DRAG_BUBBLE:
+ case BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE:
+ mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE
+ : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE);
+ break;
+ case BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW:
+ case BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW:
+ // TODO(b/349845968): move logging from BubbleBarLayerView to here
+ break;
}
}
/**
* Animate bubble bar to the given location. The location change is transient. It does not
* update the state of the bubble bar.
- * To update bubble bar pinned location, use {@link #setBubbleBarLocation(BubbleBarLocation)}.
+ * To update bubble bar pinned location, use
+ * {@link #setBubbleBarLocation(BubbleBarLocation, int)}.
*/
public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
if (canShowAsBubbleBar()) {
@@ -1259,6 +1295,14 @@ public class BubbleController implements ConfigurationChangeListener,
// We still have bubbles, if we dragged an individual bubble to dismiss we were expanded
// so re-expand to whatever is selected.
showExpandedViewForBubbleBar();
+ if (bubbleKey.equals(selectedBubbleKey)) {
+ // We dragged the selected bubble to dismiss, log switch event
+ if (mBubbleData.getSelectedBubble() instanceof Bubble) {
+ // Log only bubbles as overflow can't be dragged
+ mLogger.log((Bubble) mBubbleData.getSelectedBubble(),
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+ }
+ }
}
}
@@ -1301,10 +1345,16 @@ public class BubbleController implements ConfigurationChangeListener,
public void expandStackAndSelectBubbleFromLauncher(String key, int topOnScreen) {
mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen);
+ boolean wasExpanded = (mLayerView != null && mLayerView.isExpanded());
+
if (BubbleOverflow.KEY.equals(key)) {
mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
mLayerView.showExpandedView(mBubbleData.getOverflow());
- mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+ if (wasExpanded) {
+ mLogger.log(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+ } else {
+ mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+ }
return;
}
@@ -1316,7 +1366,11 @@ public class BubbleController implements ConfigurationChangeListener,
// already in the stack
mBubbleData.setSelectedBubbleFromLauncher(b);
mLayerView.showExpandedView(b);
- mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+ if (wasExpanded) {
+ mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+ } else {
+ mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
+ }
} else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) {
// TODO: (b/271468319) handle overflow
} else {
@@ -1632,6 +1686,7 @@ public class BubbleController implements ConfigurationChangeListener,
if (!isShowingAsBubbleBar()) {
callback = b -> {
if (mStackView != null) {
+ b.setSuppressFlyout(true);
mStackView.addBubble(b);
mStackView.setSelectedBubble(b);
} else {
@@ -2044,6 +2099,9 @@ public class BubbleController implements ConfigurationChangeListener,
// Only need to update the layer view if we're currently expanded for selection changes.
if (mLayerView != null && mLayerView.isExpanded()) {
mLayerView.showExpandedView(selectedBubble);
+ if (selectedBubble instanceof Bubble bubble) {
+ mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED);
+ }
}
}
};
@@ -2567,9 +2625,10 @@ public class BubbleController implements ConfigurationChangeListener,
}
@Override
- public void setBubbleBarLocation(BubbleBarLocation location) {
+ public void setBubbleBarLocation(BubbleBarLocation location,
+ @BubbleBarLocation.UpdateSource int source) {
mMainExecutor.execute(() ->
- mController.setBubbleBarLocation(location));
+ mController.setBubbleBarLocation(location, source));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index ec4854b47aff..6423eed59165 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -32,7 +32,10 @@ interface BubbleExpandedViewManager {
fun isStackExpanded(): Boolean
fun isShowingAsBubbleBar(): Boolean
fun hideCurrentInputMethod()
- fun updateBubbleBarLocation(location: BubbleBarLocation)
+ fun updateBubbleBarLocation(
+ location: BubbleBarLocation,
+ @BubbleBarLocation.UpdateSource source: Int,
+ )
companion object {
/**
@@ -82,8 +85,11 @@ interface BubbleExpandedViewManager {
controller.hideCurrentInputMethod()
}
- override fun updateBubbleBarLocation(location: BubbleBarLocation) {
- controller.bubbleBarLocation = location
+ override fun updateBubbleBarLocation(
+ location: BubbleBarLocation,
+ @BubbleBarLocation.UpdateSource source: Int,
+ ) {
+ controller.setBubbleBarLocation(location, source)
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 1855b938f48e..9c2d35431554 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -44,7 +44,7 @@ interface IBubbles {
oneway void showUserEducation(in int positionX, in int positionY) = 8;
- oneway void setBubbleBarLocation(in BubbleBarLocation location) = 9;
+ oneway void setBubbleBarLocation(in BubbleBarLocation location, in int source) = 9;
oneway void updateBubbleBarTopOnScreen(in int topOnScreen) = 10;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 0ce651c3f1fe..3764bcd42ac6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -222,7 +222,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
mHandleView.setAccessibilityDelegate(new HandleViewAccessibilityDelegate());
}
- mMenuViewController = new BubbleBarMenuViewController(mContext, this);
+ mMenuViewController = new BubbleBarMenuViewController(mContext, mHandleView, this);
mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
@Override
public void onMenuVisibilityChanged(boolean visible) {
@@ -241,12 +241,14 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
if (mListener != null) {
mListener.onUnBubbleConversation(bubble.getKey());
}
+ mBubbleLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_APP_MENU_OPT_OUT);
}
@Override
public void onOpenAppSettings(Bubble bubble) {
mManager.collapseStack();
mContext.startActivityAsUser(bubble.getSettingsIntent(mContext), bubble.getUser());
+ mBubbleLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_APP_MENU_GO_TO_SETTINGS);
}
@Override
@@ -635,11 +637,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
return true;
}
if (action == R.id.action_move_bubble_bar_left) {
- mManager.updateBubbleBarLocation(BubbleBarLocation.LEFT);
+ mManager.updateBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW);
return true;
}
if (action == R.id.action_move_bubble_bar_right) {
- mManager.updateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ mManager.updateBubbleBarLocation(BubbleBarLocation.RIGHT,
+ BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW);
return true;
}
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index e781c07f01a7..712e41b0b3c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -17,17 +17,18 @@ package com.android.wm.shell.bubbles.bar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Outline;
-import android.graphics.Path;
-import android.graphics.RectF;
+import android.graphics.Canvas;
+import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewOutlineProvider;
import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.animation.IntProperty;
import androidx.core.content.ContextCompat;
import com.android.wm.shell.R;
@@ -37,14 +38,33 @@ import com.android.wm.shell.R;
*/
public class BubbleBarHandleView extends View {
private static final long COLOR_CHANGE_DURATION = 120;
- // Path used to draw the dots
- private final Path mPath = new Path();
+ /** Custom property to set handle color. */
+ private static final IntProperty<BubbleBarHandleView> HANDLE_COLOR = new IntProperty<>(
+ "handleColor") {
+ @Override
+ public void setValue(BubbleBarHandleView bubbleBarHandleView, int color) {
+ bubbleBarHandleView.setHandleColor(color);
+ }
+
+ @Override
+ public Integer get(BubbleBarHandleView bubbleBarHandleView) {
+ return bubbleBarHandleView.getHandleColor();
+ }
+ };
+
+ @VisibleForTesting
+ final Paint mHandlePaint = new Paint();
private final @ColorInt int mHandleLightColor;
private final @ColorInt int mHandleDarkColor;
- private @ColorInt int mCurrentColor;
+ private final ArgbEvaluator mArgbEvaluator = ArgbEvaluator.getInstance();
+ private final float mHandleHeight;
+ private final float mHandleWidth;
+ private float mCurrentHandleHeight;
+ private float mCurrentHandleWidth;
@Nullable
private ObjectAnimator mColorChangeAnim;
+ private @ColorInt int mRegionSamplerColor;
public BubbleBarHandleView(Context context) {
this(context, null /* attrs */);
@@ -61,30 +81,52 @@ public class BubbleBarHandleView extends View {
public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- final int handleHeight = getResources().getDimensionPixelSize(
+ mHandlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ mHandlePaint.setStyle(Paint.Style.FILL);
+ mHandlePaint.setColor(0);
+ mHandleHeight = getResources().getDimensionPixelSize(
R.dimen.bubble_bar_expanded_view_handle_height);
+ mHandleWidth = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_caption_width);
mHandleLightColor = ContextCompat.getColor(getContext(),
R.color.bubble_bar_expanded_view_handle_light);
mHandleDarkColor = ContextCompat.getColor(getContext(),
R.color.bubble_bar_expanded_view_handle_dark);
-
- setClipToOutline(true);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- final int handleCenterY = view.getHeight() / 2;
- final int handleTop = handleCenterY - handleHeight / 2;
- final int handleBottom = handleTop + handleHeight;
- final int radius = handleHeight / 2;
- RectF handle = new RectF(/* left = */ 0, handleTop, view.getWidth(), handleBottom);
- mPath.reset();
- mPath.addRoundRect(handle, radius, radius, Path.Direction.CW);
- outline.setPath(mPath);
- }
- });
+ mCurrentHandleHeight = mHandleHeight;
+ mCurrentHandleWidth = mHandleWidth;
setContentDescription(getResources().getString(R.string.handle_text));
}
+ private void setHandleColor(int color) {
+ mHandlePaint.setColor(color);
+ invalidate();
+ }
+
+ private int getHandleColor() {
+ return mHandlePaint.getColor();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ float handleLeft = (getWidth() - mCurrentHandleWidth) / 2;
+ float handleRight = handleLeft + mCurrentHandleWidth;
+ float handleCenterY = (float) getHeight() / 2;
+ float handleTop = (int) (handleCenterY - mCurrentHandleHeight / 2);
+ float handleBottom = handleTop + mCurrentHandleHeight;
+ float cornerRadius = mCurrentHandleHeight / 2;
+ canvas.drawRoundRect(handleLeft, handleTop, handleRight, handleBottom, cornerRadius,
+ cornerRadius, mHandlePaint);
+ }
+
+ /** Sets handle width, height and color. Does not change the layout properties */
+ private void setHandleProperties(float width, float height, int color) {
+ mCurrentHandleHeight = height;
+ mCurrentHandleWidth = width;
+ mHandlePaint.setColor(color);
+ invalidate();
+ }
+
/**
* Updates the handle color.
*
@@ -94,15 +136,15 @@ public class BubbleBarHandleView extends View {
*/
public void updateHandleColor(boolean isRegionDark, boolean animated) {
int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
- if (newColor == mCurrentColor) {
+ if (newColor == mRegionSamplerColor) {
return;
}
+ mRegionSamplerColor = newColor;
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
- mCurrentColor = newColor;
if (animated) {
- mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor);
+ mColorChangeAnim = ObjectAnimator.ofArgb(this, HANDLE_COLOR, newColor);
mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -112,7 +154,39 @@ public class BubbleBarHandleView extends View {
mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION);
mColorChangeAnim.start();
} else {
- setBackgroundColor(newColor);
+ setHandleColor(newColor);
}
}
+
+ /** Returns handle padding top. */
+ public int getHandlePaddingTop() {
+ return (getHeight() - getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_handle_height)) / 2;
+ }
+
+ /** Animates handle for the bubble menu. */
+ public void animateHandleForMenu(float progress, float widthDelta, float heightDelta,
+ int menuColor) {
+ float currentWidth = mHandleWidth + widthDelta * progress;
+ float currentHeight = mHandleHeight + heightDelta * progress;
+ int color = (int) mArgbEvaluator.evaluate(progress, mRegionSamplerColor, menuColor);
+ setHandleProperties(currentWidth, currentHeight, color);
+ setTranslationY(heightDelta * progress / 2);
+ }
+
+ /** Restores all the properties that were animated to the default values. */
+ public void restoreAnimationDefaults() {
+ setHandleProperties(mHandleWidth, mHandleHeight, mRegionSamplerColor);
+ setTranslationY(0);
+ }
+
+ /** Returns the handle height. */
+ public int getHandleHeight() {
+ return (int) mHandleHeight;
+ }
+
+ /** Returns the handle width. */
+ public int getHandleWidth() {
+ return (int) mHandleWidth;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 402818c80b01..0c05e3c5115c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.bubbles.bar;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE;
+import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import android.annotation.Nullable;
import android.content.Context;
@@ -66,8 +67,6 @@ public class BubbleBarLayerView extends FrameLayout
private static final String TAG = BubbleBarLayerView.class.getSimpleName();
- private static final float SCRIM_ALPHA = 0.2f;
-
private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
private final BubblePositioner mPositioner;
@@ -124,18 +123,7 @@ public class BubbleBarLayerView extends FrameLayout
mBubbleExpandedViewPinController = new BubbleExpandedViewPinController(
context, this, mPositioner);
- mBubbleExpandedViewPinController.setListener(
- new BaseBubblePinController.LocationChangeListener() {
- @Override
- public void onChange(@NonNull BubbleBarLocation bubbleBarLocation) {
- mBubbleController.animateBubbleBarLocation(bubbleBarLocation);
- }
-
- @Override
- public void onRelease(@NonNull BubbleBarLocation location) {
- mBubbleController.setBubbleBarLocation(location);
- }
- });
+ mBubbleExpandedViewPinController.setListener(new LocationChangeListener());
setOnClickListener(view -> hideModalOrCollapse());
}
@@ -238,11 +226,7 @@ public class BubbleBarLayerView extends FrameLayout
DragListener dragListener = inDismiss -> {
if (inDismiss && mExpandedBubble != null) {
mBubbleController.dismissBubble(mExpandedBubble.getKey(), DISMISS_USER_GESTURE);
- if (mExpandedBubble instanceof Bubble) {
- // Only a bubble can be dragged to dismiss
- mBubbleLogger.log((Bubble) mExpandedBubble,
- BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW);
- }
+ logBubbleEvent(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW);
}
};
mDragController = new BubbleBarExpandedViewDragController(
@@ -401,7 +385,7 @@ public class BubbleBarLayerView extends FrameLayout
if (show) {
mScrimView.animate()
.setInterpolator(ALPHA_IN)
- .alpha(SCRIM_ALPHA)
+ .alpha(BUBBLE_EXPANDED_SCRIM_ALPHA)
.start();
} else {
mScrimView.animate()
@@ -423,10 +407,48 @@ public class BubbleBarLayerView extends FrameLayout
}
}
+ /**
+ * Log the event only if {@link #mExpandedBubble} is a {@link Bubble}.
+ * <p>
+ * Skips logging if it is {@link BubbleOverflow}.
+ */
+ private void logBubbleEvent(BubbleLogger.Event event) {
+ if (mExpandedBubble != null && mExpandedBubble instanceof Bubble bubble) {
+ mBubbleLogger.log(bubble, event);
+ }
+ }
+
@Nullable
@VisibleForTesting
public BubbleBarExpandedViewDragController getDragController() {
return mDragController;
}
+ private class LocationChangeListener implements
+ BaseBubblePinController.LocationChangeListener {
+
+ private BubbleBarLocation mInitialLocation;
+
+ @Override
+ public void onStart(@NonNull BubbleBarLocation location) {
+ mInitialLocation = location;
+ }
+
+ @Override
+ public void onChange(@NonNull BubbleBarLocation bubbleBarLocation) {
+ mBubbleController.animateBubbleBarLocation(bubbleBarLocation);
+ }
+
+ @Override
+ public void onRelease(@NonNull BubbleBarLocation location) {
+ mBubbleController.setBubbleBarLocation(location,
+ BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW);
+ if (location != mInitialLocation) {
+ BubbleLogger.Event event = location.isOnLeft(isLayoutRtl())
+ ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW
+ : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW;
+ logBubbleEvent(event);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index 52b807abddd6..99e20097e61c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.bubbles.bar;
import android.annotation.ColorInt;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -43,13 +42,15 @@ import java.util.ArrayList;
*/
public class BubbleBarMenuView extends LinearLayout {
- public static final Object DISMISS_ACTION_TAG = new Object();
-
private ViewGroup mBubbleSectionView;
private ViewGroup mActionsSectionView;
private ImageView mBubbleIconView;
private ImageView mBubbleDismissIconView;
private TextView mBubbleTitleView;
+ // The animation has three stages. Each stage transition lasts until the animation ends. In
+ // stage 1, the title item content fades in. In stage 2, the background of the option items
+ // fades in. In stage 3, the option item content fades in.
+ private static final int SHOW_MENU_STAGES_COUNT = 3;
public BubbleBarMenuView(Context context) {
this(context, null /* attrs */);
@@ -100,6 +101,35 @@ public class BubbleBarMenuView extends LinearLayout {
}
}
+ /** Animates the menu from the specified start scale. */
+ public void animateFromStartScale(float currentScale, float progress) {
+ int menuItemElevation = getResources().getDimensionPixelSize(
+ R.dimen.bubble_manage_menu_elevation);
+ setScaleX(currentScale);
+ setScaleY(currentScale);
+ setAlphaForTitleViews(progress);
+ mBubbleSectionView.setElevation(menuItemElevation * progress);
+ float actionsBackgroundAlpha = Math.max(0,
+ (progress - (float) 1 / SHOW_MENU_STAGES_COUNT) * (SHOW_MENU_STAGES_COUNT - 1));
+ float actionItemsAlpha = Math.max(0,
+ (progress - (float) 2 / SHOW_MENU_STAGES_COUNT) * SHOW_MENU_STAGES_COUNT);
+ mActionsSectionView.setAlpha(actionsBackgroundAlpha);
+ mActionsSectionView.setElevation(menuItemElevation * actionsBackgroundAlpha);
+ setMenuItemViewsAlpha(actionItemsAlpha);
+ }
+
+ private void setAlphaForTitleViews(float alpha) {
+ mBubbleIconView.setAlpha(alpha);
+ mBubbleTitleView.setAlpha(alpha);
+ mBubbleDismissIconView.setAlpha(alpha);
+ }
+
+ private void setMenuItemViewsAlpha(float alpha) {
+ for (int i = mActionsSectionView.getChildCount() - 1; i >= 0; i--) {
+ mActionsSectionView.getChildAt(i).setAlpha(alpha);
+ }
+ }
+
/** Update menu details with bubble info */
void updateInfo(Bubble bubble) {
if (bubble.getIcon() != null) {
@@ -123,9 +153,6 @@ public class BubbleBarMenuView extends LinearLayout {
R.layout.bubble_bar_menu_item, mActionsSectionView, false);
itemView.update(action.mIcon, action.mTitle, action.mTint);
itemView.setOnClickListener(action.mOnClick);
- if (action.mTag != null) {
- itemView.setTag(action.mTag);
- }
mActionsSectionView.addView(itemView);
}
}
@@ -159,6 +186,11 @@ public class BubbleBarMenuView extends LinearLayout {
return mBubbleSectionView.getAlpha();
}
+ /** Return title menu item height. */
+ public float getTitleItemHeight() {
+ return mBubbleSectionView.getHeight();
+ }
+
/**
* Menu action details used to create menu items
*/
@@ -166,8 +198,6 @@ public class BubbleBarMenuView extends LinearLayout {
private Icon mIcon;
private @ColorInt int mTint;
private String mTitle;
- @Nullable
- private Object mTag;
private OnClickListener mOnClick;
MenuAction(Icon icon, String title, OnClickListener onClick) {
@@ -180,14 +210,5 @@ public class BubbleBarMenuView extends LinearLayout {
this.mTint = tint;
this.mOnClick = onClick;
}
-
- MenuAction(Icon icon, String title, @ColorInt int tint, @Nullable Object tag,
- OnClickListener onClick) {
- this.mIcon = icon;
- this.mTitle = title;
- this.mTint = tint;
- this.mTag = tag;
- this.mOnClick = onClick;
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 5ed01b66ec67..9dd0cae20370 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -15,6 +15,9 @@
*/
package com.android.wm.shell.bubbles.bar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -26,13 +29,10 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
+import com.android.app.animation.Interpolators;
import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
-import com.android.wm.shell.shared.animation.PhysicsAnimator;
import java.util.ArrayList;
@@ -40,22 +40,26 @@ import java.util.ArrayList;
* Manages bubble bar expanded view menu presentation and animations
*/
class BubbleBarMenuViewController {
- private static final float MENU_INITIAL_SCALE = 0.5f;
+
+ private static final float WIDTH_SWAP_FRACTION = 0.4F;
+ private static final long MENU_ANIMATION_DURATION = 600;
+
private final Context mContext;
private final ViewGroup mRootView;
+ private final BubbleBarHandleView mHandleView;
private @Nullable Listener mListener;
private @Nullable Bubble mBubble;
private @Nullable BubbleBarMenuView mMenuView;
/** A transparent view used to intercept touches to collapse menu when presented */
private @Nullable View mScrimView;
- private @Nullable PhysicsAnimator<BubbleBarMenuView> mMenuAnimator;
- private PhysicsAnimator.SpringConfig mMenuSpringConfig;
+ private @Nullable ValueAnimator mMenuAnimator;
+
- BubbleBarMenuViewController(Context context, ViewGroup rootView) {
+ BubbleBarMenuViewController(Context context, BubbleBarHandleView handleView,
+ ViewGroup rootView) {
mContext = context;
mRootView = rootView;
- mMenuSpringConfig = new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+ mHandleView = handleView;
}
/** Tells if the menu is visible or being animated */
@@ -81,20 +85,21 @@ class BubbleBarMenuViewController {
if (mMenuView == null || mScrimView == null) {
setupMenu();
}
- cancelAnimations();
- mMenuView.setVisibility(View.VISIBLE);
- mScrimView.setVisibility(View.VISIBLE);
- Runnable endActions = () -> {
- mMenuView.getChildAt(0).requestAccessibilityFocus();
- if (mListener != null) {
- mListener.onMenuVisibilityChanged(true /* isShown */);
+ runOnMenuIsMeasured(() -> {
+ mMenuView.setVisibility(View.VISIBLE);
+ mScrimView.setVisibility(View.VISIBLE);
+ Runnable endActions = () -> {
+ mMenuView.getChildAt(0).requestAccessibilityFocus();
+ if (mListener != null) {
+ mListener.onMenuVisibilityChanged(true /* isShown */);
+ }
+ };
+ if (animated) {
+ animateTransition(true /* show */, endActions);
+ } else {
+ endActions.run();
}
- };
- if (animated) {
- animateTransition(true /* show */, endActions);
- } else {
- endActions.run();
- }
+ });
}
/**
@@ -103,18 +108,30 @@ class BubbleBarMenuViewController {
*/
void hideMenu(boolean animated) {
if (mMenuView == null || mScrimView == null) return;
- cancelAnimations();
- Runnable endActions = () -> {
- mMenuView.setVisibility(View.GONE);
- mScrimView.setVisibility(View.GONE);
- if (mListener != null) {
- mListener.onMenuVisibilityChanged(false /* isShown */);
+ runOnMenuIsMeasured(() -> {
+ Runnable endActions = () -> {
+ mHandleView.restoreAnimationDefaults();
+ mMenuView.setVisibility(View.GONE);
+ mScrimView.setVisibility(View.GONE);
+ mHandleView.setVisibility(View.VISIBLE);
+ if (mListener != null) {
+ mListener.onMenuVisibilityChanged(false /* isShown */);
+ }
+ };
+ if (animated) {
+ animateTransition(false /* show */, endActions);
+ } else {
+ endActions.run();
}
- };
- if (animated) {
- animateTransition(false /* show */, endActions);
+ });
+ }
+
+ private void runOnMenuIsMeasured(Runnable action) {
+ if (mMenuView.getWidth() == 0 || mMenuView.getHeight() == 0) {
+ // the menu view is not yet measured, postpone showing the animation
+ mMenuView.post(() -> runOnMenuIsMeasured(action));
} else {
- endActions.run();
+ action.run();
}
}
@@ -125,24 +142,63 @@ class BubbleBarMenuViewController {
*/
private void animateTransition(boolean show, Runnable endActions) {
if (mMenuView == null) return;
- mMenuAnimator = PhysicsAnimator.getInstance(mMenuView);
- mMenuAnimator.setDefaultSpringConfig(mMenuSpringConfig);
- mMenuAnimator
- .spring(DynamicAnimation.ALPHA, show ? 1f : 0f)
- .spring(DynamicAnimation.SCALE_Y, show ? 1f : MENU_INITIAL_SCALE)
- .withEndActions(() -> {
- mMenuAnimator = null;
- endActions.run();
- })
- .start();
+ float startValue = show ? 0 : 1;
+ if (mMenuAnimator != null && mMenuAnimator.isRunning()) {
+ startValue = (float) mMenuAnimator.getAnimatedValue();
+ mMenuAnimator.cancel();
+ }
+ ValueAnimator showMenuAnimation = ValueAnimator.ofFloat(startValue, show ? 1 : 0);
+ showMenuAnimation.setDuration(MENU_ANIMATION_DURATION);
+ showMenuAnimation.setInterpolator(Interpolators.EMPHASIZED);
+ showMenuAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mMenuAnimator = null;
+ endActions.run();
+ }
+ });
+ mMenuAnimator = showMenuAnimation;
+ setupAnimatorListener(showMenuAnimation);
+ showMenuAnimation.start();
}
- /** Cancel running animations */
- private void cancelAnimations() {
- if (mMenuAnimator != null) {
- mMenuAnimator.cancel();
- mMenuAnimator = null;
+ /** Setup listener that orchestrates the animation. */
+ private void setupAnimatorListener(ValueAnimator showMenuAnimation) {
+ // Getting views properties start values
+ int widthDiff = mMenuView.getWidth() - mHandleView.getHandleWidth();
+ int handleHeight = mHandleView.getHandleHeight();
+ float targetWidth = mHandleView.getHandleWidth() + widthDiff * WIDTH_SWAP_FRACTION;
+ float targetHeight = targetWidth * mMenuView.getTitleItemHeight() / mMenuView.getWidth();
+ int menuColor;
+ try (TypedArray ta = mContext.obtainStyledAttributes(new int[]{
+ com.android.internal.R.attr.materialColorSurfaceBright,
+ })) {
+ menuColor = ta.getColor(0, Color.WHITE);
}
+ // Calculating deltas
+ float swapScale = targetWidth / mMenuView.getWidth();
+ float handleWidthDelta = targetWidth - mHandleView.getHandleWidth();
+ float handleHeightDelta = targetHeight - handleHeight;
+ // Setting update listener that will orchestrate the animation
+ showMenuAnimation.addUpdateListener(animator -> {
+ float animationProgress = (float) animator.getAnimatedValue();
+ boolean showHandle = animationProgress <= WIDTH_SWAP_FRACTION;
+ mHandleView.setVisibility(showHandle ? View.VISIBLE : View.GONE);
+ mMenuView.setVisibility(showHandle ? View.GONE : View.VISIBLE);
+ if (showHandle) {
+ float handleAnimationProgress = animationProgress / WIDTH_SWAP_FRACTION;
+ mHandleView.animateHandleForMenu(handleAnimationProgress, handleWidthDelta,
+ handleHeightDelta, menuColor);
+ } else {
+ mMenuView.setTranslationY(mHandleView.getHandlePaddingTop());
+ mMenuView.setPivotY(0);
+ mMenuView.setPivotX((float) mMenuView.getWidth() / 2);
+ float menuAnimationProgress =
+ (animationProgress - WIDTH_SWAP_FRACTION) / (1 - WIDTH_SWAP_FRACTION);
+ float currentMenuScale = swapScale + (1 - swapScale) * menuAnimationProgress;
+ mMenuView.animateFromStartScale(currentMenuScale, menuAnimationProgress);
+ }
+ });
}
/** Sets up and inflate menu views */
@@ -150,9 +206,6 @@ class BubbleBarMenuViewController {
// Menu view setup
mMenuView = (BubbleBarMenuView) LayoutInflater.from(mContext).inflate(
R.layout.bubble_bar_menu_view, mRootView, false);
- mMenuView.setAlpha(0f);
- mMenuView.setPivotY(0f);
- mMenuView.setScaleY(MENU_INITIAL_SCALE);
mMenuView.setOnCloseListener(() -> hideMenu(true /* animated */));
if (mBubble != null) {
mMenuView.updateInfo(mBubble);
@@ -212,7 +265,6 @@ class BubbleBarMenuViewController {
Icon.createWithResource(resources, R.drawable.ic_remove_no_shadow),
resources.getString(R.string.bubble_dismiss_text),
tintColor,
- BubbleBarMenuView.DISMISS_ACTION_TAG,
view -> {
hideMenu(true /* animated */);
if (mListener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
index 4abb35c2a428..193c593e2ab2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -16,8 +16,11 @@
package com.android.wm.shell.common.pip
import android.app.AppOpsManager
+import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.util.Pair
+import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.common.ShellExecutor
class PipAppOpsListener(
@@ -27,10 +30,12 @@ class PipAppOpsListener(
) {
private val mAppOpsManager: AppOpsManager = checkNotNull(
mContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager)
+ private var mTopPipActivityInfoSupplier: (Context) -> Pair<ComponentName?, Int> =
+ PipUtils::getTopPipActivity
private val mAppOpsChangedListener = AppOpsManager.OnOpChangedListener { _, packageName ->
try {
// Dismiss the PiP once the user disables the app ops setting for that package
- val topPipActivityInfo = PipUtils.getTopPipActivity(mContext)
+ val topPipActivityInfo = mTopPipActivityInfoSupplier.invoke(mContext)
val componentName = topPipActivityInfo.first ?: return@OnOpChangedListener
val userId = topPipActivityInfo.second
val appInfo = mContext.packageManager
@@ -75,4 +80,9 @@ class PipAppOpsListener(
/** Dismisses the PIP window. */
fun dismissPip()
}
+
+ @VisibleForTesting
+ fun setTopPipActivityInfoSupplier(supplier: (Context) -> Pair<ComponentName?, Int>) {
+ mTopPipActivityInfoSupplier = supplier
+ }
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index b83b5f341dda..8ef20d1d6b93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -44,7 +44,8 @@ object PipUtils {
private const val TAG = "PipUtils"
// Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
- private const val EPSILON = 1e-7
+ // TODO b/377530560: Restore epsilon once a long term fix is merged for non-config-at-end issue.
+ private const val EPSILON = 0.05f
/**
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
index d1b2347a4411..62d5098f2a27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
@@ -23,9 +23,15 @@ import android.content.Context
import com.android.internal.R
// TODO(b/347289970): Consider replacing with API
+/**
+ * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
+ * Currently includes all system ui activities and modal dialogs. However is the top activity is not
+ * being displayed, regardless of its configuration, we will not exempt it as to remain in the
+ * desktop windowing environment.
+ */
fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) =
- isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1
- && !task.isTopActivityStyleFloating)
+ (isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1))
+ && !task.isTopActivityNoDisplay
private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean {
val sysUiPackageName: String =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 6146ecd9ade6..c99d9ba862c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -188,6 +188,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
*/
private boolean mIsFirstReachabilityEducationRunning;
+ private boolean mIsInDesktopMode;
+
@NonNull
private final CompatUIStatusManager mCompatUIStatusManager;
@@ -253,18 +255,19 @@ public class CompatUIController implements OnDisplaysChangedListener,
if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()) {
mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
}
-
- if (taskInfo != null && taskListener != null) {
- updateActiveTaskInfo(taskInfo);
- }
-
- // We close all the Compat UI educations in case we're in desktop mode.
- if (taskInfo.configuration == null || taskListener == null
- || isInDesktopMode(taskInfo.displayId)) {
+ mIsInDesktopMode = isInDesktopMode(taskInfo);
+ // We close all the Compat UI educations in case TaskInfo has no configuration or
+ // TaskListener or in desktop mode.
+ if (taskInfo.configuration == null || taskListener == null || mIsInDesktopMode) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayouts(taskInfo.taskId);
return;
}
+ if (taskInfo != null && taskListener != null) {
+ updateActiveTaskInfo(taskInfo);
+ }
+
+
// We're showing the first reachability education so we ignore incoming TaskInfo
// until the education flow has completed or we double tap. The double-tap
// basically cancel all the onboarding flow. We don't have to ignore events in case
@@ -286,7 +289,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
// we need to ignore all the incoming TaskInfo until the education
// completes. If we come from a double tap we follow the normal flow.
final boolean topActivityPillarboxed =
- taskInfo.appCompatTaskInfo.isTopActivityPillarboxed();
+ taskInfo.appCompatTaskInfo.isTopActivityPillarboxShaped();
final boolean isFirstTimeHorizontalReachabilityEdu = topActivityPillarboxed
&& !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(taskInfo);
final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
@@ -443,7 +446,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
CompatUIWindowManager layout = mActiveCompatLayouts.get(taskInfo.taskId);
if (layout != null) {
- if (layout.needsToBeRecreated(taskInfo, taskListener)) {
+ if (layout.needsToBeRecreated(taskInfo, taskListener) || mIsInDesktopMode) {
mActiveCompatLayouts.remove(taskInfo.taskId);
layout.release();
} else {
@@ -456,7 +459,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
}
-
+ if (mIsInDesktopMode) {
+ // Return if in desktop mode.
+ return;
+ }
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
@@ -494,7 +500,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void createOrUpdateLetterboxEduLayout(@NonNull TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
if (mActiveLetterboxEduLayout != null) {
- if (mActiveLetterboxEduLayout.needsToBeRecreated(taskInfo, taskListener)) {
+ if (mActiveLetterboxEduLayout.needsToBeRecreated(taskInfo, taskListener)
+ || mIsInDesktopMode) {
mActiveLetterboxEduLayout.release();
mActiveLetterboxEduLayout = null;
} else {
@@ -507,6 +514,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
}
+ if (mIsInDesktopMode) {
+ // Return if in desktop mode.
+ return;
+ }
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
@@ -541,7 +552,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
RestartDialogWindowManager layout =
mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
if (layout != null) {
- if (layout.needsToBeRecreated(taskInfo, taskListener)) {
+ if (layout.needsToBeRecreated(taskInfo, taskListener) || mIsInDesktopMode) {
mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
layout.release();
} else {
@@ -556,6 +567,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
}
+ if (mIsInDesktopMode) {
+ // Return if in desktop mode.
+ return;
+ }
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
@@ -594,7 +609,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void createOrUpdateReachabilityEduLayout(@NonNull TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
if (mActiveReachabilityEduLayout != null) {
- if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)) {
+ if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)
+ || mIsInDesktopMode) {
mActiveReachabilityEduLayout.release();
mActiveReachabilityEduLayout = null;
} else {
@@ -608,6 +624,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
}
+ if (mIsInDesktopMode) {
+ // Return if in desktop mode.
+ return;
+ }
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
@@ -647,7 +667,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
if (mUserAspectRatioSettingsLayout != null) {
- if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)) {
+ if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)
+ || mIsInDesktopMode) {
mUserAspectRatioSettingsLayout.release();
mUserAspectRatioSettingsLayout = null;
} else {
@@ -660,7 +681,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
}
-
+ if (mIsInDesktopMode) {
+ // Return if in desktop mode.
+ return;
+ }
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
@@ -688,6 +712,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void launchUserAspectRatioSettings(
@NonNull TaskInfo taskInfo, @NonNull ShellTaskOrganizer.TaskListener taskListener) {
+ launchUserAspectRatioSettings(mContext, taskInfo);
+ }
+
+ /** Launch the user aspect ratio settings for the package of the given task. */
+ public static void launchUserAspectRatioSettings(
+ @NonNull Context context, @NonNull TaskInfo taskInfo) {
final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -697,7 +727,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
intent.setData(packageUri);
}
final UserHandle userHandle = UserHandle.of(taskInfo.userId);
- mContext.startActivityAsUser(intent, userHandle);
+ context.startActivityAsUser(intent, userHandle);
}
@VisibleForTesting
@@ -834,8 +864,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
boolean mHasShownUserAspectRatioSettingsButtonHint;
}
- private boolean isInDesktopMode(int displayId) {
- return Flags.skipCompatUiEducationInDesktopMode()
- && mInDesktopModePredicate.test(displayId);
+ private boolean isInDesktopMode(@Nullable TaskInfo taskInfo) {
+ return taskInfo != null && Flags.skipCompatUiEducationInDesktopMode()
+ && mInDesktopModePredicate.test(taskInfo.displayId);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 2a5a519272c7..77e041ee7cdb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -401,9 +401,6 @@ public abstract class WMShellBaseModule {
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
RootTaskDisplayAreaOrganizer rootTdaOrganizer) {
- if (!com.android.window.flags.Flags.explicitRefreshRateHints()) {
- return Optional.empty();
- }
final PerfHintController perfHintController =
new PerfHintController(context, shellInit, shellCommandHandler, rootTdaOrganizer);
return Optional.of(perfHintController.getHinter());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index fec4c16992a5..601cf70b93ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -67,6 +67,8 @@ import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
+import com.android.wm.shell.desktopmode.DesktopBackNavigationTransitionHandler;
+import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler;
@@ -778,7 +780,8 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
ReturnToDragStartAnimator returnToDragStartAnimator,
- @DynamicOverride DesktopRepository desktopRepository) {
+ @DynamicOverride DesktopRepository desktopRepository,
+ DesktopModeEventLogger desktopModeEventLogger) {
return new DesktopTilingDecorViewModel(
context,
displayController,
@@ -788,7 +791,8 @@ public abstract class WMShellModule {
shellTaskOrganizer,
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
- desktopRepository
+ desktopRepository,
+ desktopModeEventLogger
);
}
@@ -833,14 +837,21 @@ public abstract class WMShellModule {
@Provides
static Optional<DesktopImmersiveController> provideDesktopImmersiveController(
Context context,
+ ShellInit shellInit,
Transitions transitions,
@DynamicOverride DesktopRepository desktopRepository,
DisplayController displayController,
- ShellTaskOrganizer shellTaskOrganizer) {
+ ShellTaskOrganizer shellTaskOrganizer,
+ ShellCommandHandler shellCommandHandler) {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.of(
new DesktopImmersiveController(
- transitions, desktopRepository, displayController, shellTaskOrganizer));
+ shellInit,
+ transitions,
+ desktopRepository,
+ displayController,
+ shellTaskOrganizer,
+ shellCommandHandler));
}
return Optional.empty();
}
@@ -905,6 +916,16 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static DesktopBackNavigationTransitionHandler provideDesktopBackNavigationTransitionHandler(
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellAnimationThread ShellExecutor animExecutor,
+ DisplayController displayController) {
+ return new DesktopBackNavigationTransitionHandler(mainExecutor, animExecutor,
+ displayController);
+ }
+
+ @WMSingleton
+ @Provides
static DesktopModeDragAndDropTransitionHandler provideDesktopModeDragAndDropTransitionHandler(
Transitions transitions) {
return new DesktopModeDragAndDropTransitionHandler(transitions);
@@ -954,6 +975,7 @@ public abstract class WMShellModule {
Optional<DesktopRepository> desktopRepository,
Transitions transitions,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
ShellInit shellInit) {
return desktopRepository.flatMap(
repository ->
@@ -963,6 +985,7 @@ public abstract class WMShellModule {
repository,
transitions,
shellTaskOrganizer,
+ desktopMixedTransitionHandler.get(),
shellInit)));
}
@@ -975,6 +998,7 @@ public abstract class WMShellModule {
FreeformTaskTransitionHandler freeformTaskTransitionHandler,
CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
Optional<DesktopImmersiveController> desktopImmersiveController,
+ DesktopBackNavigationTransitionHandler desktopBackNavigationTransitionHandler,
InteractionJankMonitor interactionJankMonitor,
@ShellMainThread Handler handler,
ShellInit shellInit,
@@ -991,6 +1015,7 @@ public abstract class WMShellModule {
freeformTaskTransitionHandler,
closeDesktopTaskTransitionHandler,
desktopImmersiveController.get(),
+ desktopBackNavigationTransitionHandler,
interactionJankMonitor,
handler,
shellInit,
@@ -1016,6 +1041,30 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static Optional<DesktopDisplayEventHandler> provideDesktopDisplayEventHandler(
+ Context context,
+ ShellInit shellInit,
+ Transitions transitions,
+ DisplayController displayController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ IWindowManager windowManager
+ ) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)
+ || !Flags.enableDisplayWindowingModeSwitching()) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ new DesktopDisplayEventHandler(
+ context,
+ shellInit,
+ transitions,
+ displayController,
+ rootTaskDisplayAreaOrganizer,
+ windowManager));
+ }
+
+ @WMSingleton
+ @Provides
static AppHandleEducationDatastoreRepository provideAppHandleEducationDatastoreRepository(
Context context) {
return new AppHandleEducationDatastoreRepository(context);
@@ -1180,7 +1229,8 @@ public abstract class WMShellModule {
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
- Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
+ Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional,
+ Optional<DesktopDisplayEventHandler> desktopDisplayEventHandler) {
return new Object();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 3a4764d45f2c..3cd5df3121c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.dagger.pip;
import android.content.Context;
import android.os.Handler;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
@@ -41,6 +42,7 @@ import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
@@ -169,6 +171,8 @@ public abstract class Pip1Module {
PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenControllerOptional,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
+ Optional<DesktopRepository> desktopRepositoryOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
@@ -176,7 +180,8 @@ public abstract class Pip1Module {
syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
- splitScreenControllerOptional, pipPerfHintControllerOptional, displayController,
+ splitScreenControllerOptional, pipPerfHintControllerOptional,
+ desktopRepositoryOptional, rootTaskDisplayAreaOrganizer, displayController,
pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 8d1b15c1e631..78e676f8cd45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -22,6 +22,7 @@ import android.os.SystemClock;
import androidx.annotation.NonNull;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
@@ -214,6 +215,7 @@ public abstract class TvPipModule {
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenControllerOptional,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
@@ -221,8 +223,9 @@ public abstract class TvPipModule {
syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState,
tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
pipSurfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
- splitScreenControllerOptional, pipPerfHintControllerOptional, displayController,
- pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ splitScreenControllerOptional, pipPerfHintControllerOptional,
+ rootTaskDisplayAreaOrganizer, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt
new file mode 100644
index 000000000000..83b0f8413a28
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.animation.Animator
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.IBinder
+import android.util.DisplayMetrics
+import android.view.SurfaceControl.Transaction
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.animation.MinimizeAnimator.create
+import com.android.wm.shell.transition.Transitions
+
+/**
+ * The [Transitions.TransitionHandler] that handles transitions for tasks that are closing or going
+ * to back as part of back navigation. This handler is used only for animating transitions.
+ */
+class DesktopBackNavigationTransitionHandler(
+ private val mainExecutor: ShellExecutor,
+ private val animExecutor: ShellExecutor,
+ private val displayController: DisplayController,
+) : Transitions.TransitionHandler {
+
+ /** Shouldn't handle anything */
+ override fun handleRequest(
+ transition: IBinder,
+ request: TransitionRequestInfo,
+ ): WindowContainerTransaction? = null
+
+ /** Animates a transition with minimizing tasks */
+ override fun startAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: Transaction,
+ finishTransaction: Transaction,
+ finishCallback: Transitions.TransitionFinishCallback,
+ ): Boolean {
+ if (!TransitionUtil.isClosingType(info.type)) return false
+
+ val animations = mutableListOf<Animator>()
+ val onAnimFinish: (Animator) -> Unit = { animator ->
+ mainExecutor.execute {
+ // Animation completed
+ animations.remove(animator)
+ if (animations.isEmpty()) {
+ // All animations completed, finish the transition
+ finishCallback.onTransitionFinished(/* wct= */ null)
+ }
+ }
+ }
+
+ animations +=
+ info.changes
+ .filter {
+ it.mode == info.type &&
+ it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
+ }
+ .mapNotNull { createMinimizeAnimation(it, finishTransaction, onAnimFinish) }
+ if (animations.isEmpty()) return false
+ animExecutor.execute { animations.forEach(Animator::start) }
+ return true
+ }
+
+ private fun createMinimizeAnimation(
+ change: TransitionInfo.Change,
+ finishTransaction: Transaction,
+ onAnimFinish: (Animator) -> Unit
+ ): Animator? {
+ val t = Transaction()
+ val sc = change.leash
+ finishTransaction.hide(sc)
+ val displayMetrics: DisplayMetrics? =
+ change.taskInfo?.let {
+ displayController.getDisplayContext(it.displayId)?.getResources()?.displayMetrics
+ }
+ return displayMetrics?.let { create(it, change, t, onAnimFinish) }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
new file mode 100644
index 000000000000..ba383fac8b47
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+
+/** Handles display events in desktop mode */
+class DesktopDisplayEventHandler(
+ private val context: Context,
+ shellInit: ShellInit,
+ private val transitions: Transitions,
+ private val displayController: DisplayController,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val windowManager: IWindowManager,
+) : OnDisplaysChangedListener {
+
+ init {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+
+ private fun onInit() {
+ displayController.addDisplayWindowListener(this)
+ }
+
+ override fun onDisplayAdded(displayId: Int) {
+ if (displayId == DEFAULT_DISPLAY) {
+ return
+ }
+ refreshDisplayWindowingMode()
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {
+ if (displayId == DEFAULT_DISPLAY) {
+ return
+ }
+ refreshDisplayWindowingMode()
+ }
+
+ private fun refreshDisplayWindowingMode() {
+ // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
+ val isExtendedDisplayEnabled = 0 != Settings.Global.getInt(
+ context.contentResolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0
+ )
+ if (!isExtendedDisplayEnabled) {
+ // No action needed in mirror or projected mode.
+ return
+ }
+
+ val hasNonDefaultDisplay = rootTaskDisplayAreaOrganizer.getDisplayIds()
+ .any { displayId -> displayId != DEFAULT_DISPLAY }
+ val targetDisplayWindowingMode =
+ if (hasNonDefaultDisplay) {
+ WINDOWING_MODE_FREEFORM
+ } else {
+ // Use the default display windowing mode when no non-default display.
+ windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ }
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
+ requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
+ if (tdaInfo.configuration.windowConfiguration.windowingMode == targetDisplayWindowingMode) {
+ // Already in the target mode.
+ return
+ }
+
+ val wct = WindowContainerTransaction()
+ wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
+ transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
index f69aa6df6a1d..1acde73e68dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
@@ -34,10 +34,13 @@ import com.android.window.flags.Flags
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.transition.Transitions.TransitionObserver
import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
+import java.io.PrintWriter
/**
* A controller to move tasks in/out of desktop's full immersive state where the task
@@ -45,27 +48,34 @@ import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
* be transient below the status bar like in fullscreen immersive mode.
*/
class DesktopImmersiveController(
+ shellInit: ShellInit,
private val transitions: Transitions,
private val desktopRepository: DesktopRepository,
private val displayController: DisplayController,
private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val shellCommandHandler: ShellCommandHandler,
private val transactionSupplier: () -> SurfaceControl.Transaction,
) : TransitionHandler, TransitionObserver {
constructor(
+ shellInit: ShellInit,
transitions: Transitions,
desktopRepository: DesktopRepository,
displayController: DisplayController,
shellTaskOrganizer: ShellTaskOrganizer,
+ shellCommandHandler: ShellCommandHandler,
) : this(
+ shellInit,
transitions,
desktopRepository,
displayController,
shellTaskOrganizer,
+ shellCommandHandler,
{ SurfaceControl.Transaction() }
)
- private var state: TransitionState? = null
+ @VisibleForTesting
+ var state: TransitionState? = null
@VisibleForTesting
val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>()
@@ -79,10 +89,21 @@ class DesktopImmersiveController(
/** A listener to invoke on animation changes during entry/exit. */
var onTaskResizeAnimationListener: OnTaskResizeAnimationListener? = null
+ init {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+
+ fun onInit() {
+ shellCommandHandler.addDumpCallback(this::dump, this)
+ }
+
/** Starts a transition to enter full immersive state inside the desktop. */
fun moveTaskToImmersive(taskInfo: RunningTaskInfo) {
if (inProgress) {
- logV("Cannot start entry because transition already in progress.")
+ logV(
+ "Cannot start entry because transition(s) already in progress: %s",
+ getRunningTransitions()
+ )
return
}
val wct = WindowContainerTransaction().apply {
@@ -100,7 +121,10 @@ class DesktopImmersiveController(
fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo) {
if (inProgress) {
- logV("Cannot start exit because transition already in progress.")
+ logV(
+ "Cannot start exit because transition(s) already in progress: %s",
+ getRunningTransitions()
+ )
return
}
@@ -225,14 +249,19 @@ class DesktopImmersiveController(
finishCallback: Transitions.TransitionFinishCallback
): Boolean {
val state = requireState()
- if (transition != state.transition) return false
+ check(state.transition == transition) {
+ "Transition $transition did not match expected state=$state"
+ }
logD("startAnimation transition=%s", transition)
animateResize(
targetTaskId = state.taskId,
info = info,
startTransaction = startTransaction,
finishTransaction = finishTransaction,
- finishCallback = finishCallback
+ finishCallback = {
+ finishCallback.onTransitionFinished(/* wct= */ null)
+ clearState()
+ },
)
return true
}
@@ -242,12 +271,18 @@ class DesktopImmersiveController(
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
) {
logD("animateResize for task#%d", targetTaskId)
- val change = info.changes.first { c ->
+ val change = info.changes.firstOrNull { c ->
val taskInfo = c.taskInfo
- return@first taskInfo != null && taskInfo.taskId == targetTaskId
+ return@firstOrNull taskInfo != null && taskInfo.taskId == targetTaskId
+ }
+ if (change == null) {
+ logD("Did not find change for task#%d to animate", targetTaskId)
+ startTransaction.apply()
+ finishCallback.onTransitionFinished(/* wct= */ null)
+ return
}
animateResizeChange(change, startTransaction, finishTransaction, finishCallback)
}
@@ -288,7 +323,6 @@ class DesktopImmersiveController(
.apply()
onTaskResizeAnimationListener?.onAnimationEnd(taskId)
finishCallback.onTransitionFinished(null /* wct */)
- clearState()
}
)
addUpdateListener { animation ->
@@ -357,8 +391,17 @@ class DesktopImmersiveController(
// Check if this is a direct immersive enter/exit transition.
if (transition == state?.transition) {
val state = requireState()
- val startBounds = info.changes.first { c -> c.taskInfo?.taskId == state.taskId }
- .startAbsBounds
+ val immersiveChange = info.changes.firstOrNull { c ->
+ c.taskInfo?.taskId == state.taskId
+ }
+ if (immersiveChange == null) {
+ logV(
+ "Direct move for task#%d in %s direction missing immersive change.",
+ state.taskId, state.direction
+ )
+ return
+ }
+ val startBounds = immersiveChange.startAbsBounds
logV("Direct move for task#%d in %s direction verified", state.taskId, state.direction)
when (state.direction) {
Direction.ENTER -> {
@@ -446,11 +489,30 @@ class DesktopImmersiveController(
private fun requireState(): TransitionState =
state ?: error("Expected non-null transition state")
+ private fun getRunningTransitions(): List<IBinder> {
+ val running = mutableListOf<IBinder>()
+ state?.let {
+ running.add(it.transition)
+ }
+ pendingExternalExitTransitions.forEach {
+ running.add(it.transition)
+ }
+ return running
+ }
+
private fun TransitionInfo.hasTaskChange(taskId: Int): Boolean =
changes.any { c -> c.taskInfo?.taskId == taskId }
+ private fun dump(pw: PrintWriter, prefix: String) {
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}DesktopImmersiveController")
+ pw.println(innerPrefix + "state=" + state)
+ pw.println(innerPrefix + "pendingExternalExitTransitions=" + pendingExternalExitTransitions)
+ }
+
/** The state of the currently running transition. */
- private data class TransitionState(
+ @VisibleForTesting
+ data class TransitionState(
val transition: IBinder,
val displayId: Int,
val taskId: Int,
@@ -483,7 +545,8 @@ class DesktopImmersiveController(
fun asExit(): Exit? = if (this is Exit) this else null
}
- private enum class Direction {
+ @VisibleForTesting
+ enum class Direction {
ENTER, EXIT
}
@@ -495,9 +558,10 @@ class DesktopImmersiveController(
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
- private companion object {
+ companion object {
private const val TAG = "DesktopImmersive"
- private const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L
+ @VisibleForTesting
+ const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 48bb2a8b4a74..2001f9743094 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -52,6 +52,7 @@ class DesktopMixedTransitionHandler(
private val freeformTaskTransitionHandler: FreeformTaskTransitionHandler,
private val closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler,
private val desktopImmersiveController: DesktopImmersiveController,
+ private val desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler,
private val interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread private val handler: Handler,
shellInit: ShellInit,
@@ -161,6 +162,14 @@ class DesktopMixedTransitionHandler(
finishTransaction,
finishCallback
)
+ is PendingMixedTransition.Minimize -> animateMinimizeTransition(
+ pending,
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback
+ )
}
}
@@ -213,7 +222,12 @@ class DesktopMixedTransitionHandler(
findDesktopTaskChange(info, minimizingTask)
}
val launchChange = findDesktopTaskChange(info, pending.launchingTask)
- ?: error("Should have pending launching task change")
+ if (launchChange == null) {
+ check(minimizeChange == null)
+ check(immersiveExitChange == null)
+ logV("No launch Change, returning")
+ return false
+ }
var subAnimationCount = -1
var combinedWct: WindowContainerTransaction? = null
@@ -267,6 +281,42 @@ class DesktopMixedTransitionHandler(
)
}
+ private fun animateMinimizeTransition(
+ pending: PendingMixedTransition.Minimize,
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: TransitionFinishCallback,
+ ): Boolean {
+ if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue) return false
+
+ val minimizeChange = findDesktopTaskChange(info, pending.minimizingTask)
+ if (minimizeChange == null) {
+ logW("Should have minimizing desktop task")
+ return false
+ }
+ if (pending.isLastTask) {
+ // Dispatch close desktop task animation to the default transition handlers.
+ return dispatchToLeftoverHandler(
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback
+ )
+ }
+
+ // Animate minimizing desktop task transition with [DesktopBackNavigationTransitionHandler].
+ return desktopBackNavigationTransitionHandler.startAnimation(
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback,
+ )
+ }
+
override fun onTransitionConsumed(
transition: IBinder,
aborted: Boolean,
@@ -395,6 +445,14 @@ class DesktopMixedTransitionHandler(
val minimizingTask: Int?,
val exitingImmersiveTask: Int?,
) : PendingMixedTransition()
+
+ /** A task is minimizing. This should be used for task going to back and some closing cases
+ * with back navigation. */
+ data class Minimize(
+ override val transition: IBinder,
+ val minimizingTask: Int,
+ val isLastTask: Boolean,
+ ) : PendingMixedTransition()
}
private fun logV(msg: String, vararg arguments: Any?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index bed484c7a532..39586e39fdd4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -594,6 +594,10 @@ class DesktopModeEventLogger {
FrameworkStatsLog
.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__MAXIMIZE_MENU_RESIZE_TRIGGER
),
+ DRAG_TO_TOP_RESIZE_TRIGGER(
+ FrameworkStatsLog
+ .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__DRAG_TO_TOP_RESIZE_TRIGGER
+ ),
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index edcc877ef58e..c7cf31081c8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -275,7 +275,7 @@ private fun TaskInfo.hasPortraitTopActivity(): Boolean {
}
// Then check if the activity is portrait when letterboxed
- appCompatTaskInfo.isTopActivityLetterboxed -> appCompatTaskInfo.isTopActivityPillarboxed
+ appCompatTaskInfo.isTopActivityLetterboxed -> appCompatTaskInfo.isTopActivityPillarboxShaped
// Then check if the activity is portrait
appBounds != null -> appBounds.height() > appBounds.width()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index fda709a4a2d7..08ca55f93e3f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -102,6 +102,9 @@ class DesktopRepository (
/* Tracks last bounds of task before toggled to stable bounds. */
private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>()
+ /* Tracks last bounds of task before it is minimized. */
+ private val boundsBeforeMinimizeByTaskId = SparseArray<Rect>()
+
/* Tracks last bounds of task before toggled to immersive state. */
private val boundsBeforeFullImmersiveByTaskId = SparseArray<Rect>()
@@ -462,6 +465,14 @@ class DesktopRepository (
fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) =
boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
+ /** Removes and returns the bounds saved before minimizing the given task. */
+ fun removeBoundsBeforeMinimize(taskId: Int): Rect? =
+ boundsBeforeMinimizeByTaskId.removeReturnOld(taskId)
+
+ /** Saves the bounds of the given task before minimizing. */
+ fun saveBoundsBeforeMinimize(taskId: Int, bounds: Rect?) =
+ boundsBeforeMinimizeByTaskId.set(taskId, Rect(bounds))
+
/** Removes and returns the bounds saved before entering immersive with the given task. */
fun removeBoundsBeforeFullImmersive(taskId: Int): Rect? =
boundsBeforeFullImmersiveByTaskId.removeReturnOld(taskId)
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 75f8839692a2..6928c255edde 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
@@ -42,6 +42,7 @@ import android.os.Binder
import android.os.Handler
import android.os.IBinder
import android.os.SystemProperties
+import android.os.UserHandle
import android.util.Size
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
@@ -554,19 +555,6 @@ class DesktopTasksController(
}
}
- /** Move a desktop app to split screen. */
- fun moveToSplit(task: RunningTaskInfo) {
- logV( "moveToSplit taskId=%s", task.taskId)
- desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
- val wct = WindowContainerTransaction()
- wct.setBounds(task.token, Rect())
- // Rather than set windowing mode to multi-window at task level, set it to
- // undefined and inherit from split stage.
- wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
-
- transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
- }
-
private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
if (splitScreenController.isTaskInSplitScreen(taskInfo.taskId)) {
splitScreenController.prepareExitSplitScreen(
@@ -884,11 +872,10 @@ class DesktopTasksController(
return
}
- // TODO(b/375356605): Introduce a new ResizeTrigger for drag-to-top.
desktopModeEventLogger.logTaskResizingStarted(
- ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent, taskInfo, displayController
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent, taskInfo, displayController
)
- toggleDesktopTaskSize(taskInfo, ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent)
+ toggleDesktopTaskSize(taskInfo, ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent)
}
private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect {
@@ -1188,7 +1175,11 @@ class DesktopTasksController(
private fun addWallpaperActivity(wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
- val intent = Intent(context, DesktopWallpaperActivity::class.java)
+ val userHandle = UserHandle.of(userId)
+ val userContext =
+ context.createContextAsUser(userHandle, /* flags= */ 0)
+ val intent = Intent(userContext, DesktopWallpaperActivity::class.java)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId)
val options =
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
@@ -1196,11 +1187,13 @@ class DesktopTasksController(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
}
val pendingIntent =
- PendingIntent.getActivity(
- context,
- /* requestCode = */ 0,
+ PendingIntent.getActivityAsUser(
+ userContext,
+ /* requestCode= */ 0,
intent,
- PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_IMMUTABLE,
+ /* bundle= */ null,
+ userHandle
)
wct.sendPendingIntent(pendingIntent, intent, options.toBundle())
}
@@ -1304,7 +1297,11 @@ class DesktopTasksController(
// Check if freeform task launch during recents should be handled
shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
// Check if the closing task needs to be handled
- TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
+ TransitionUtil.isClosingType(request.type) -> handleTaskClosing(
+ task,
+ transition,
+ request.type
+ )
// Check if the top task shouldn't be allowed to enter desktop mode
isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
// Check if fullscreen task should be updated
@@ -1634,7 +1631,7 @@ class DesktopTasksController(
}
/** Handle task closing by removing wallpaper activity if it's the last active task */
- private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
+ private fun handleTaskClosing(task: RunningTaskInfo, transition: IBinder, requestType: Int): WindowContainerTransaction? {
logV("handleTaskClosing")
if (!isDesktopModeShowing(task.displayId))
return null
@@ -1650,8 +1647,15 @@ class DesktopTasksController(
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
taskRepository.addClosingTask(task.displayId, task.taskId)
desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
+ } else if (requestType == TRANSIT_CLOSE) {
+ // Handle closing tasks, tasks that are going to back are handled in
+ // [DesktopTasksTransitionObserver].
+ desktopMixedTransitionHandler.addPendingMixedTransition(
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition, task.taskId, taskRepository.getVisibleTaskCount(task.displayId) == 1
+ )
+ )
}
-
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(
task.displayId,
@@ -1783,9 +1787,13 @@ class DesktopTasksController(
transition: IBinder,
taskIdToMinimize: Int,
) {
- val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return
+ val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
desktopTasksLimiter.ifPresent {
- it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId)
+ it.addPendingMinimizeChange(
+ transition = transition,
+ displayId = taskToMinimize?.displayId ?: DEFAULT_DISPLAY,
+ taskId = taskIdToMinimize
+ )
}
}
@@ -2050,6 +2058,18 @@ class DesktopTasksController(
}
/**
+ * Cancel the drag-to-desktop transition.
+ *
+ * @param taskInfo the task being dragged.
+ */
+ fun onDragPositioningCancelThroughStatusBar(
+ taskInfo: RunningTaskInfo,
+ ) {
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
+ cancelDragToDesktop(taskInfo)
+ }
+
+ /**
* Perform checks required when drag ends under status bar area.
*
* @param taskInfo the task being dragged.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index f0e3a2bd8ffc..77af627a948a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -92,6 +92,12 @@ class DesktopTasksLimiter (
}
taskToMinimize.transitionInfo = info
activeTransitionTokensAndTasks[transition] = taskToMinimize
+
+ // Save current bounds before minimizing in case we need to restore to it later.
+ val boundsBeforeMinimize = info.changes.find { change ->
+ change.taskInfo?.taskId == taskToMinimize.taskId }?.startAbsBounds
+ taskRepository.saveBoundsBeforeMinimize(taskToMinimize.taskId, boundsBeforeMinimize)
+
this@DesktopTasksLimiter.minimizeTask(
taskToMinimize.displayId, taskToMinimize.taskId)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index d1534da9a078..c39c715e685c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -46,6 +46,7 @@ class DesktopTasksTransitionObserver(
private val desktopRepository: DesktopRepository,
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
shellInit: ShellInit
) : Transitions.TransitionObserver {
@@ -71,7 +72,7 @@ class DesktopTasksTransitionObserver(
// TODO: b/332682201 Update repository state
updateWallpaperToken(info)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
- handleBackNavigation(info)
+ handleBackNavigation(transition, info)
removeTaskIfNeeded(info)
}
removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
@@ -95,7 +96,7 @@ class DesktopTasksTransitionObserver(
}
}
- private fun handleBackNavigation(info: TransitionInfo) {
+ private fun handleBackNavigation(transition: IBinder, info: TransitionInfo) {
// When default back navigation happens, transition type is TO_BACK and the change is
// TO_BACK. Mark the task going to back as minimized.
if (info.type == TRANSIT_TO_BACK) {
@@ -105,10 +106,14 @@ class DesktopTasksTransitionObserver(
continue
}
- if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) > 0 &&
+ val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId)
+ if (visibleTaskCount > 0 &&
change.mode == TRANSIT_TO_BACK &&
taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+ desktopMixedTransitionHandler.addPendingMixedTransition(
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition, taskInfo.taskId, visibleTaskCount == 1))
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
index 1c2415c236ad..e835b2fec232 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
@@ -36,7 +36,6 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
class DesktopWallpaperActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate")
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index 96719fa2301a..941115083717 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -21,6 +21,7 @@ import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.graphics.Rect
import android.os.IBinder
+import android.view.Choreographer
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.TransitionInfo
@@ -126,6 +127,7 @@ class ToggleResizeDesktopTaskTransitionHandler(
tx.setPosition(leash, rect.left.toFloat(), rect.top.toFloat())
.setWindowCrop(leash, rect.width(), rect.height())
.show(leash)
+ .setFrameTimeline(Choreographer.getInstance().getVsyncId())
onTaskResizeAnimationListener.onBoundsChange(taskId, tx, rect)
}
start()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index dcbdfa349687..a611fe1db2ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.draganddrop;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID;
@@ -94,25 +95,30 @@ public class DragSession {
void updateRunningTask() {
final boolean hideDragSourceTask = hideDragSourceTaskId != -1;
final List<ActivityManager.RunningTaskInfo> tasks =
- mActivityTaskManager.getTasks(hideDragSourceTask ? 2 : 1,
- false /* filterOnlyVisibleRecents */);
- if (!tasks.isEmpty()) {
- for (int i = tasks.size() - 1; i >= 0; i--) {
- final ActivityManager.RunningTaskInfo task = tasks.get(i);
- if (hideDragSourceTask && hideDragSourceTaskId == task.taskId) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
- "Skipping running task: id=%d component=%s", task.taskId,
- task.baseIntent != null ? task.baseIntent.getComponent() : "null");
- continue;
- }
- runningTaskInfo = task;
- runningTaskWinMode = task.getWindowingMode();
- runningTaskActType = task.getActivityType();
+ mActivityTaskManager.getTasks(5, false /* filterOnlyVisibleRecents */);
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RunningTaskInfo task = tasks.get(i);
+ if (hideDragSourceTask && hideDragSourceTaskId == task.taskId) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
- "Running task: id=%d component=%s", task.taskId,
+ "Skipping running task: id=%d component=%s", task.taskId,
task.baseIntent != null ? task.baseIntent.getComponent() : "null");
- break;
+ continue;
}
+ if (!task.isVisible) {
+ // Skip invisible tasks
+ continue;
+ }
+ if (task.configuration.windowConfiguration.isAlwaysOnTop()) {
+ // Skip always-on-top floating tasks
+ continue;
+ }
+ runningTaskInfo = task;
+ runningTaskWinMode = task.getWindowingMode();
+ runningTaskActType = task.getActivityType();
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Running task: id=%d component=%s", task.taskId,
+ task.baseIntent != null ? task.baseIntent.getComponent() : "null");
+ break;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index f8d2011d0934..b618bf1215ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -53,6 +53,7 @@ import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -72,6 +73,9 @@ import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
public class KeyguardTransitionHandler
implements Transitions.TransitionHandler, KeyguardChangeListener,
TaskStackListenerCallback {
+ private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
+ Flags.ensureKeyguardDoesTransitionStarting();
+
private static final String TAG = "KeyguardTransition";
private final Transitions mTransitions;
@@ -194,7 +198,7 @@ public class KeyguardTransitionHandler
// Occlude/unocclude animations are only played if the keyguard is locked.
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
- if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) {
+ if (isKeyguardOccluding(info)) {
if (hasOpeningDream(info)) {
return startAnimation(mOccludeByDreamTransition, "occlude-by-dream",
transition, info, startTransaction, finishTransaction, finishCallback);
@@ -202,7 +206,7 @@ public class KeyguardTransitionHandler
return startAnimation(mOccludeTransition, "occlude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
- } else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
+ } else if (isKeyguardUnoccluding(info)) {
return startAnimation(mUnoccludeTransition, "unocclude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
@@ -325,6 +329,36 @@ public class KeyguardTransitionHandler
return false;
}
+ private static boolean isKeyguardOccluding(@NonNull TransitionInfo info) {
+ if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0;
+ }
+
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.hasFlags(TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA)
+ && change.getMode() == TRANSIT_TO_FRONT) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isKeyguardUnoccluding(@NonNull TransitionInfo info) {
+ if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
+ return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0;
+ }
+
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.hasFlags(TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA)
+ && change.getMode() == TRANSIT_TO_BACK) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void finishAnimationImmediately(IBinder transition, StartedTransition playing) {
final IBinder fakeTransition = new Binder();
final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index b27c428f1693..0154d0455e50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -45,12 +45,17 @@ public interface Pip {
}
/**
- * Set the callback when {@link PipTaskOrganizer#isInPip()} state is changed.
+ * Set the callback when isInPip state is changed.
*
- * @param callback The callback accepts the result of {@link PipTaskOrganizer#isInPip()}
- * when it's changed.
+ * @param callback The callback accepts the state of isInPip when it's changed.
*/
- default void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {}
+ default void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {}
+
+ /**
+ * Remove the callback when isInPip state is changed.
+ * @param callback The callback accepts the state of isInPip when it's changed.
+ */
+ default void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {}
/**
* Called when showing Pip menu.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 4aeecbec7dfb..5276d9d6a4df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -27,6 +27,7 @@ import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.app.AppCompatTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -176,12 +177,12 @@ public class PipAnimationController {
public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, float startingAngle,
- @Surface.Rotation int rotationDelta) {
+ @Surface.Rotation int rotationDelta, boolean alwaysAnimateTaskBounds) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
endBounds, sourceHintRect, direction, 0 /* startingAngle */,
- rotationDelta));
+ rotationDelta, alwaysAnimateTaskBounds));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -197,7 +198,8 @@ public class PipAnimationController {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
- endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
+ endBounds, sourceHintRect, direction, startingAngle, rotationDelta,
+ alwaysAnimateTaskBounds));
}
return mCurrentAnimator;
}
@@ -585,28 +587,32 @@ public class PipAnimationController {
static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceRectHint,
@PipAnimationController.TransitionDirection int direction, float startingAngle,
- @Surface.Rotation int rotationDelta) {
+ @Surface.Rotation int rotationDelta, boolean alwaysAnimateTaskBounds) {
final boolean isOutPipDirection = isOutPipDirection(direction);
final boolean isInPipDirection = isInPipDirection(direction);
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
final Rect initialSourceValue;
final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame;
- final boolean hasNonMatchFrame = mainWindowFrame != null;
+ final AppCompatTaskInfo compatInfo = taskInfo.appCompatTaskInfo;
+ final boolean isSizeCompatOrLetterboxed = compatInfo.isTopActivityInSizeCompat()
+ || compatInfo.isTopActivityLetterboxed();
+ // For the animation to swipe PIP to home or restore a PIP task from home, we don't
+ // override to the main window frame since we should animate the whole task.
+ final boolean shouldUseMainWindowFrame = mainWindowFrame != null
+ && !alwaysAnimateTaskBounds && !isSizeCompatOrLetterboxed;
final boolean changeOrientation =
rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270;
final Rect baseBounds = new Rect(baseValue);
final Rect startBounds = new Rect(startValue);
final Rect endBounds = new Rect(endValue);
if (isOutPipDirection) {
- // TODO(b/356277166): handle rotation change with activity that provides main window
- // frame.
- if (hasNonMatchFrame && !changeOrientation) {
+ if (shouldUseMainWindowFrame && !changeOrientation) {
endBounds.set(mainWindowFrame);
}
initialSourceValue = new Rect(endBounds);
} else if (isInPipDirection) {
- if (hasNonMatchFrame) {
+ if (shouldUseMainWindowFrame) {
baseBounds.set(mainWindowFrame);
if (startValue.equals(baseValue)) {
// If the start value is at initial state as in PIP animation, also override
@@ -635,9 +641,19 @@ public class PipAnimationController {
if (changeOrientation) {
lastEndRect = new Rect(endBounds);
rotatedEndRect = new Rect(endBounds);
- // Rotate the end bounds according to the rotation delta because the display will
- // be rotated to the same orientation.
- rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta);
+ // TODO(b/375977163): polish the animation to restoring the PIP task back from
+ // swipe-pip-to-home. Ideally we should send the transitionInfo after reparenting
+ // the PIP activity back to the original task.
+ if (shouldUseMainWindowFrame) {
+ // If we should animate the main window frame, set it to the rotatedRect
+ // instead. The end bounds reported by transitionInfo is the bounds before
+ // rotation, while main window frame is calculated after the rotation.
+ rotatedEndRect.set(mainWindowFrame);
+ } else {
+ // Rotate the end bounds according to the rotation delta because the display
+ // will be rotated to the same orientation.
+ rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta);
+ }
// Use the rect that has the same orientation as the hint rect.
initialContainerRect = isOutPipDirection ? rotatedEndRect : initialSourceValue;
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index c4e63dfdade9..30f1948efa2d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.pip;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -67,6 +68,7 @@ import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.DisplayAreaInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -74,7 +76,9 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ScreenshotUtils;
@@ -87,6 +91,7 @@ import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.Interpolators;
@@ -145,6 +150,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
@Nullable private final PipPerfHintController mPipPerfHintController;
+ private final Optional<DesktopRepository> mDesktopRepositoryOptional;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -388,6 +395,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
+ Optional<DesktopRepository> desktopRepositoryOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -414,6 +423,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mSplitScreenOptional = splitScreenOptional;
mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
+ mDesktopRepositoryOptional = desktopRepositoryOptional;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -741,10 +752,23 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/** Returns the bounds to restore to when exiting PIP mode. */
+ // TODO(b/377581840): Instead of manually tracking bounds, use bounds from Core.
public Rect getExitDestinationBounds() {
+ if (isPipLaunchedInDesktopMode()) {
+ final Rect freeformBounds = mDesktopRepositoryOptional.get().removeBoundsBeforeMinimize(
+ mTaskInfo.taskId);
+ return Objects.requireNonNullElseGet(freeformBounds, mPipBoundsState::getDisplayBounds);
+ }
return mPipBoundsState.getDisplayBounds();
}
+ /** Returns whether PiP was launched while in desktop mode. */
+ // TODO(377581840): Update this check to include non-minimized cases, e.g. split to PiP etc.
+ private boolean isPipLaunchedInDesktopMode() {
+ return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent()
+ && mDesktopRepositoryOptional.get().isMinimizedTask(mTaskInfo.taskId);
+ }
+
private void exitLaunchIntoPipTask(WindowContainerTransaction wct) {
wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */);
mTaskOrganizer.applyTransaction(wct);
@@ -1808,7 +1832,25 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* and can be overridden to restore to an alternate windowing mode.
*/
public int getOutPipWindowingMode() {
- // By default, simply reset the windowing mode to undefined.
+ final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
+ mTaskInfo.displayId);
+
+ // If PiP was launched while in desktop mode (we should return the task to freeform
+ // windowing mode):
+ // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will
+ // resolve the windowing mode to the display's windowing mode.
+ // 2) If the display windowing mode is not freeform, set windowing mode to freeform.
+ if (tdaInfo != null && isPipLaunchedInDesktopMode()) {
+ final int displayWindowingMode =
+ tdaInfo.configuration.windowConfiguration.getWindowingMode();
+ if (displayWindowingMode == WINDOWING_MODE_FREEFORM) {
+ return WINDOWING_MODE_UNDEFINED;
+ } else {
+ return WINDOWING_MODE_FREEFORM;
+ }
+ }
+
+ // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
return WINDOWING_MODE_UNDEFINED;
}
@@ -1838,9 +1880,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
? mPipBoundsState.getBounds() : currentBounds;
final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null
&& mPipAnimationController.getCurrentAnimator().isRunning();
+ // For resize animation, we always animate the whole PIP task bounds.
final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
- sourceHintRect, direction, startingAngle, rotationDelta);
+ sourceHintRect, direction, startingAngle, rotationDelta,
+ true /* alwaysAnimateTaskBounds */);
animator.setTransitionDirection(direction)
.setPipTransactionHandler(mPipTransactionHandler)
.setDuration(durationMs);
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 28b91c6cb812..f7aed4401247 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
@@ -530,6 +530,13 @@ public class PipTransition extends PipTransitionController {
if (mFixedRotationState != FIXED_ROTATION_TRANSITION
&& mFinishTransaction != null) {
mFinishTransaction.merge(tx);
+ // Set window crop and position to destination bounds to avoid flickering.
+ if (hasValidLeash) {
+ mFinishTransaction.setWindowCrop(leash, destinationBounds.width(),
+ destinationBounds.height());
+ mFinishTransaction.setPosition(leash, destinationBounds.left,
+ destinationBounds.top);
+ }
}
} else {
wct = new WindowContainerTransaction();
@@ -884,7 +891,8 @@ public class PipTransition extends PipTransitionController {
final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(taskInfo, pipChange.getLeash(),
startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP,
- 0 /* startingAngle */, pipRotateDelta);
+ 0 /* startingAngle */, pipRotateDelta,
+ false /* alwaysAnimateTaskBounds */);
animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -899,7 +907,7 @@ public class PipTransition extends PipTransitionController {
final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(taskInfo, leash, baseBounds, startBounds,
endBounds, sourceHintRect, TRANSITION_DIRECTION_LEAVE_PIP,
- 0 /* startingAngle */, rotationDelta);
+ 0 /* startingAngle */, rotationDelta, false /* alwaysAnimateTaskBounds */);
animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
.setDuration(mEnterExitAnimationDuration);
if (startTransaction != null) {
@@ -1095,8 +1103,6 @@ public class PipTransition extends PipTransitionController {
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
- // TODO(b/356277166): add support to swipe PIP to home with
- // non-match parent activity.
handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash,
sourceHintRect, destinationBounds, taskInfo);
return;
@@ -1118,7 +1124,7 @@ public class PipTransition extends PipTransitionController {
if (enterAnimationType == ANIM_TYPE_BOUNDS) {
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
- 0 /* startingAngle */, rotationDelta);
+ 0 /* startingAngle */, rotationDelta, false /* alwaysAnimateTaskBounds */);
if (sourceHintRect == null) {
// We use content overlay when there is no source rect hint to enter PiP use bounds
// animation. We also temporarily disallow app icon overlay and use color overlay
@@ -1241,10 +1247,14 @@ public class PipTransition extends PipTransitionController {
// to avoid flicker.
final Rect savedDisplayCutoutInsets = new Rect(pipTaskInfo.displayCutoutInsets);
pipTaskInfo.displayCutoutInsets.setEmpty();
+ // Always use the task bounds even if the PIP activity doesn't match parent because the app
+ // and the whole task will move behind. We should animate the whole task bounds in this
+ // case.
final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds,
destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
- 0 /* startingAngle */, ROTATION_0 /* rotationDelta */)
+ 0 /* startingAngle */, ROTATION_0 /* rotationDelta */,
+ true /* alwaysAnimateTaskBounds */)
.setPipTransactionHandler(mTransactionConsumer)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP);
// The start state is the end state for swipe-auto-pip.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 5ffc64f412f1..79a9ce5212c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -47,7 +47,6 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipMenuController;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.DefaultMixedHandler;
@@ -312,10 +311,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/** Whether a particular package is same as current pip package. */
- public boolean isPackageActiveInPip(String packageName) {
- final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
- return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
- && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+ public boolean isPackageActiveInPip(@Nullable String packageName) {
+ return packageName != null
+ && mPipBoundsState.getLastPipComponentName() != null
+ && packageName.equals(mPipBoundsState.getLastPipComponentName().getPackageName());
}
/** Add PiP-related changes to `outWCT` for the given request. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 7f6118689dad..588b88753eb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -104,6 +104,7 @@ import com.android.wm.shell.sysui.UserChangeListener;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -215,7 +216,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private boolean mIsKeyguardShowingOrAnimating;
- private Consumer<Boolean> mOnIsInPipStateChangedListener;
+ private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
@VisibleForTesting
interface PipAnimationListener {
@@ -501,11 +502,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
false /* saveRestoreSnapFraction */);
});
mPipTransitionState.addOnPipTransitionStateChangedListener((oldState, newState) -> {
- if (mOnIsInPipStateChangedListener != null) {
- final boolean wasInPip = PipTransitionState.isInPip(oldState);
- final boolean nowInPip = PipTransitionState.isInPip(newState);
- if (nowInPip != wasInPip) {
- mOnIsInPipStateChangedListener.accept(nowInPip);
+ final boolean wasInPip = PipTransitionState.isInPip(oldState);
+ final boolean nowInPip = PipTransitionState.isInPip(newState);
+ if (nowInPip != wasInPip) {
+ for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
+ listener.accept(nowInPip);
}
}
});
@@ -960,13 +961,19 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsState.getLauncherState().setAppIconSizePx(iconSizePx);
}
- private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
- mOnIsInPipStateChangedListener = callback;
- if (mOnIsInPipStateChangedListener != null) {
+ private void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
+ if (callback != null) {
+ mOnIsInPipStateChangedListeners.add(callback);
callback.accept(mPipTransitionState.isInPip());
}
}
+ private void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
+ if (callback != null) {
+ mOnIsInPipStateChangedListeners.remove(callback);
+ }
+ }
+
private void setShelfHeightLocked(boolean visible, int height) {
final int shelfHeight = visible ? height : 0;
mPipBoundsState.setShelfVisibility(visible, shelfHeight);
@@ -1222,9 +1229,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
+ public void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
+ mMainExecutor.execute(() -> {
+ PipController.this.addOnIsInPipStateChangedListener(callback);
+ });
+ }
+
+ @Override
+ public void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
mMainExecutor.execute(() -> {
- PipController.this.setOnIsInPipStateChangedListener(callback);
+ PipController.this.removeOnIsInPipStateChangedListener(callback);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 614ef2ab9831..fcba46108f67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -21,6 +21,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
@@ -61,6 +62,7 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
@NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
+ @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -68,8 +70,9 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
super(context, syncTransactionQueue, pipTransitionState, pipBoundsState,
pipDisplayLayoutState, boundsHandler, pipMenuController, pipAnimationController,
surfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
- splitScreenOptional, pipPerfHintControllerOptional, displayController,
- pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ splitScreenOptional, pipPerfHintControllerOptional, Optional.empty(),
+ rootTaskDisplayAreaOrganizer, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
mTvPipTransition = tvPipTransition;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
index 24077a35d41c..026482004d51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
@@ -24,7 +24,6 @@ import android.view.Choreographer;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
-import com.android.wm.shell.transition.Transitions;
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
@@ -180,8 +179,7 @@ public class PipSurfaceTransactionHelper {
// destination are different.
final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
final Rect crop = mTmpDestinationRect;
- crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
- : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
+ crop.set(0, 0, destW, destH);
// Inverse scale for crop to fit in screen coordinates.
crop.scale(1 / scale);
crop.offset(insets.left, insets.top);
@@ -200,8 +198,8 @@ public class PipSurfaceTransactionHelper {
}
}
mTmpTransform.setScale(scale, scale);
- mTmpTransform.postRotate(degrees);
mTmpTransform.postTranslate(positionX, positionY);
+ mTmpTransform.postRotate(degrees);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop);
return this;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
index e5137582822d..eb33ff4c1c8e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -20,6 +20,7 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -34,6 +35,7 @@ import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.pip.PipUtils;
@@ -45,8 +47,7 @@ import com.android.wm.shell.shared.pip.PipContentOverlay;
/**
* Animator that handles bounds animations for entering PIP.
*/
-public class PipEnterAnimator extends ValueAnimator
- implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
+public class PipEnterAnimator extends ValueAnimator {
@NonNull private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
private final SurfaceControl.Transaction mFinishTransaction;
@@ -56,49 +57,82 @@ public class PipEnterAnimator extends ValueAnimator
private final RectEvaluator mRectEvaluator;
private final Rect mEndBounds = new Rect();
- @Nullable private final Rect mSourceRectHint;
private final @Surface.Rotation int mRotation;
@Nullable private Runnable mAnimationStartCallback;
@Nullable private Runnable mAnimationEndCallback;
- private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
Matrix mTransformTensor = new Matrix();
final float[] mMatrixTmp = new float[9];
@Nullable private PipContentOverlay mContentOverlay;
+ private PipAppIconOverlaySupplier mPipAppIconOverlaySupplier;
// Internal state representing initial transform - cached to avoid recalculation.
private final PointF mInitScale = new PointF();
private final PointF mInitPos = new PointF();
private final Rect mInitCrop = new Rect();
- private final PointF mInitActivityScale = new PointF();
- private final PointF mInitActivityPos = new PointF();
+
+ private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ if (mAnimationStartCallback != null) {
+ mAnimationStartCallback.run();
+ }
+ if (mStartTransaction != null) {
+ onEnterAnimationUpdate(0f /* fraction */, mStartTransaction);
+ mStartTransaction.apply();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (mFinishTransaction != null) {
+ onEnterAnimationUpdate(1f /* fraction */, mFinishTransaction);
+ }
+ if (mAnimationEndCallback != null) {
+ mAnimationEndCallback.run();
+ }
+ }
+ };
+
+ private final AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ final float fraction = getAnimatedFraction();
+ onEnterAnimationUpdate(fraction, tx);
+ tx.apply();
+ }
+ };
public PipEnterAnimator(Context context,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@NonNull Rect endBounds,
- @Nullable Rect sourceRectHint,
@Surface.Rotation int rotation) {
mLeash = leash;
mStartTransaction = startTransaction;
mFinishTransaction = finishTransaction;
mRectEvaluator = new RectEvaluator(mAnimatedRect);
mEndBounds.set(endBounds);
- mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
mRotation = rotation;
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mPipAppIconOverlaySupplier = this::getAppIconOverlay;
final int enterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
setDuration(enterAnimationDuration);
setFloatValues(0f, 1f);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- addListener(this);
- addUpdateListener(this);
+ addListener(mAnimatorListener);
+ addUpdateListener(mAnimatorUpdateListener);
}
public void setAnimationStartCallback(@NonNull Runnable runnable) {
@@ -109,35 +143,6 @@ public class PipEnterAnimator extends ValueAnimator
mAnimationEndCallback = runnable;
}
- @Override
- public void onAnimationStart(@NonNull Animator animation) {
- if (mAnimationStartCallback != null) {
- mAnimationStartCallback.run();
- }
- if (mStartTransaction != null) {
- onEnterAnimationUpdate(0f /* fraction */, mStartTransaction);
- mStartTransaction.apply();
- }
- }
-
- @Override
- public void onAnimationEnd(@NonNull Animator animation) {
- if (mFinishTransaction != null) {
- onEnterAnimationUpdate(1f /* fraction */, mFinishTransaction);
- }
- if (mAnimationEndCallback != null) {
- mAnimationEndCallback.run();
- }
- }
-
- @Override
- public void onAnimationUpdate(@NonNull ValueAnimator animation) {
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- final float fraction = getAnimatedFraction();
- onEnterAnimationUpdate(fraction, tx);
- tx.apply();
- }
-
/**
* Updates the transaction to reflect the state of PiP leash at a certain fraction during enter.
*
@@ -177,14 +182,6 @@ public class PipEnterAnimator extends ValueAnimator
}
}
- // no-ops
-
- @Override
- public void onAnimationCancel(@NonNull Animator animation) {}
-
- @Override
- public void onAnimationRepeat(@NonNull Animator animation) {}
-
/**
* Caches the initial transform relevant values for the bounds enter animation.
*
@@ -201,18 +198,13 @@ public class PipEnterAnimator extends ValueAnimator
*/
public void setAppIconContentOverlay(Context context, Rect appBounds, Rect destinationBounds,
ActivityInfo activityInfo, int appIconSizePx) {
- reattachAppIconOverlay(
- new PipAppIconOverlay(context, appBounds, destinationBounds,
- new IconProvider(context).getIcon(activityInfo), appIconSizePx));
- }
-
- private void reattachAppIconOverlay(PipAppIconOverlay overlay) {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
if (mContentOverlay != null) {
mContentOverlay.detach(tx);
}
- mContentOverlay = overlay;
+ mContentOverlay = mPipAppIconOverlaySupplier.get(context, appBounds, destinationBounds,
+ activityInfo, appIconSizePx);
mContentOverlay.attach(tx, mLeash);
}
@@ -229,6 +221,13 @@ public class PipEnterAnimator extends ValueAnimator
mContentOverlay = null;
}
+ private PipAppIconOverlay getAppIconOverlay(
+ Context context, Rect appBounds, Rect destinationBounds,
+ ActivityInfo activityInfo, int iconSize) {
+ return new PipAppIconOverlay(context, appBounds, destinationBounds,
+ new IconProvider(context).getIcon(activityInfo), iconSize);
+ }
+
/**
* @return the app icon overlay leash; null if no overlay is attached.
*/
@@ -239,4 +238,21 @@ public class PipEnterAnimator extends ValueAnimator
}
return mContentOverlay.getLeash();
}
+
+ @VisibleForTesting
+ void setSurfaceControlTransactionFactory(
+ @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+ mSurfaceControlTransactionFactory = factory;
+ }
+
+ @VisibleForTesting
+ interface PipAppIconOverlaySupplier {
+ PipAppIconOverlay get(Context context, Rect appBounds, Rect destinationBounds,
+ ActivityInfo activityInfo, int iconSize);
+ }
+
+ @VisibleForTesting
+ void setPipAppIconOverlaySupplier(@NonNull PipAppIconOverlaySupplier supplier) {
+ mPipAppIconOverlaySupplier = supplier;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
index 3f9b0c30e314..fb1aba399585 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.pip2.animation;
+import static android.view.Surface.ROTATION_90;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
@@ -73,6 +75,7 @@ public class PipExpandAnimator extends ValueAnimator {
mAnimationStartCallback.run();
}
if (mStartTransaction != null) {
+ onExpandAnimationUpdate(mStartTransaction, 0f);
mStartTransaction.apply();
}
}
@@ -81,13 +84,7 @@ public class PipExpandAnimator extends ValueAnimator {
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mFinishTransaction != null) {
- // finishTransaction might override some state (eg. corner radii) so we want to
- // manually set the state to the end of the animation
- mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash,
- mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f),
- false /* isInPipDirection */, 1f)
- .round(mFinishTransaction, mLeash, false /* applyCornerRadius */)
- .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */);
+ onExpandAnimationUpdate(mFinishTransaction, 1f);
}
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
@@ -102,14 +99,7 @@ public class PipExpandAnimator extends ValueAnimator {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
-
- // TODO (b/350801661): implement fixed rotation
- Rect insets = getInsets(fraction);
- mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
- mBaseBounds, mAnimatedRect,
- insets, false /* isInPipDirection */, fraction)
- .round(tx, mLeash, false /* applyCornerRadius */)
- .shadow(tx, mLeash, false /* applyCornerRadius */);
+ onExpandAnimationUpdate(tx, fraction);
tx.apply();
}
};
@@ -167,6 +157,32 @@ public class PipExpandAnimator extends ValueAnimator {
mAnimationEndCallback = runnable;
}
+ private void onExpandAnimationUpdate(SurfaceControl.Transaction tx, float fraction) {
+ Rect insets = getInsets(fraction);
+ if (mRotation == Surface.ROTATION_0) {
+ mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mBaseBounds,
+ mAnimatedRect, insets, false /* isInPipDirection */, fraction);
+ } else {
+ // Fixed rotation case.
+ Rect start = mStartBounds;
+ Rect end = mEndBounds;
+ float degrees, x, y;
+ x = fraction * (end.left - start.left) + start.left;
+ y = fraction * (end.top - start.top) + start.top;
+
+ if (mRotation == ROTATION_90) {
+ degrees = 90 * fraction;
+ } else {
+ degrees = -90 * fraction;
+ }
+ mPipSurfaceTransactionHelper.rotateAndScaleWithCrop(tx, mLeash, mBaseBounds,
+ mAnimatedRect, insets, degrees, x, y,
+ true /* isExpanding */, mRotation == ROTATION_90);
+ }
+ mPipSurfaceTransactionHelper.round(tx, mLeash, false /* applyCornerRadius */)
+ .shadow(tx, mLeash, false /* applyShadowRadius */);
+ }
+
private Rect getInsets(float fraction) {
final Rect startInsets = mSourceRectHintInsets;
final Rect endInsets = mZeroInsets;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 012dabbbb9f8..4558a9f141c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -30,6 +30,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.shared.animation.Interpolators;
/**
* Animator that handles any resize related animation for PIP.
@@ -128,6 +129,7 @@ public class PipResizeAnimator extends ValueAnimator {
mRectEvaluator = new RectEvaluator(mAnimatedRect);
setObjectValues(startBounds, endBounds);
+ setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
addListener(mAnimatorListener);
addUpdateListener(mAnimatorUpdateListener);
setEvaluator(mRectEvaluator);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 58d2a8577d8c..44900ce1db8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -573,7 +573,7 @@ public class PhonePipMenuController implements PipMenuController,
@PipTransitionState.TransitionState int newState, Bundle extra) {
switch (newState) {
case PipTransitionState.ENTERED_PIP:
- attach(mPipTransitionState.mPinnedTaskLeash);
+ attach(mPipTransitionState.getPinnedTaskLeash());
break;
case PipTransitionState.EXITED_PIP:
detach();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 9a93371621c9..e901c39b8792 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -66,6 +67,8 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -94,7 +97,7 @@ public class PipController implements ConfigurationChangeListener,
private final PipTouchHandler mPipTouchHandler;
private final ShellExecutor mMainExecutor;
private final PipImpl mImpl;
- private Consumer<Boolean> mOnIsInPipStateChangedListener;
+ private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
// Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
private PipAnimationListener mPipRecentsAnimationListener;
@@ -319,7 +322,7 @@ public class PipController implements ConfigurationChangeListener,
mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
mPipBoundsState.setBounds(toBounds);
}
- t.setBounds(mPipTransitionState.mPipTaskToken, mPipBoundsState.getBounds());
+ t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
}
private void setDisplayLayout(DisplayLayout layout) {
@@ -376,18 +379,20 @@ public class PipController implements ConfigurationChangeListener,
private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"setLauncherKeepClearAreaHeight: visible=%b, height=%d", visible, height);
- if (visible) {
- Rect rect = new Rect(
- 0, mPipDisplayLayoutState.getDisplayBounds().bottom - height,
- mPipDisplayLayoutState.getDisplayBounds().right,
- mPipDisplayLayoutState.getDisplayBounds().bottom);
- mPipBoundsState.setNamedUnrestrictedKeepClearArea(
- PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
- } else {
- mPipBoundsState.setNamedUnrestrictedKeepClearArea(
- PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
- }
- mPipTouchHandler.onShelfVisibilityChanged(visible, height);
+ mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+ if (visible) {
+ Rect rect = new Rect(
+ 0, mPipDisplayLayoutState.getDisplayBounds().bottom - height,
+ mPipDisplayLayoutState.getDisplayBounds().right,
+ mPipDisplayLayoutState.getDisplayBounds().bottom);
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
+ } else {
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
+ }
+ mPipTouchHandler.onShelfVisibilityChanged(visible, height);
+ });
}
@Override
@@ -411,13 +416,13 @@ public class PipController implements ConfigurationChangeListener,
if (mPipTransitionState.isInSwipePipToHomeTransition()) {
mPipTransitionState.resetSwipePipToHomeState();
}
- if (mOnIsInPipStateChangedListener != null) {
- mOnIsInPipStateChangedListener.accept(true /* inPip */);
+ for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
+ listener.accept(true /* inPip */);
}
break;
case PipTransitionState.EXITED_PIP:
- if (mOnIsInPipStateChangedListener != null) {
- mOnIsInPipStateChangedListener.accept(false /* inPip */);
+ for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
+ listener.accept(false /* inPip */);
}
break;
}
@@ -449,13 +454,19 @@ public class PipController implements ConfigurationChangeListener,
mPipTransitionState.dump(pw, innerPrefix);
}
- private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
- mOnIsInPipStateChangedListener = callback;
- if (mOnIsInPipStateChangedListener != null) {
+ private void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
+ if (callback != null) {
+ mOnIsInPipStateChangedListeners.add(callback);
callback.accept(mPipTransitionState.isInPip());
}
}
+ private void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
+ if (callback != null) {
+ mOnIsInPipStateChangedListeners.remove(callback);
+ }
+ }
+
private void setLauncherAppIconSize(int iconSizePx) {
mPipBoundsState.getLauncherState().setAppIconSizePx(iconSizePx);
}
@@ -471,9 +482,16 @@ public class PipController implements ConfigurationChangeListener,
public void onSystemUiStateChanged(boolean isSysUiStateValid, long flag) {}
@Override
- public void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
+ public void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
+ mMainExecutor.execute(() -> {
+ PipController.this.addOnIsInPipStateChangedListener(callback);
+ });
+ }
+
+ @Override
+ public void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {
mMainExecutor.execute(() -> {
- PipController.this.setOnIsInPipStateChangedListener(callback);
+ PipController.this.removeOnIsInPipStateChangedListener(callback);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 17392bc521d8..3738353dd0a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -785,7 +785,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private void handleFlingTransition(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds) {
- startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
+ startTx.setPosition(mPipTransitionState.getPinnedTaskLeash(),
destinationBounds.left, destinationBounds.top);
startTx.apply();
@@ -799,7 +799,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private void startResizeAnimation(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds, int duration) {
- SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkState(pipLeash != null,
"No leash cached by mPipTransitionState=" + mPipTransitionState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 751175f0f3e9..d98be55f28e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -531,7 +531,7 @@ public class PipResizeGestureHandler implements
// If resize transition was scheduled from this component, handle leash updates.
mWaitingForBoundsChangeTransition = false;
- SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkState(pipLeash != null,
"No leash cached by mPipTransitionState=" + mPipTransitionState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 8b25b11e3a47..5438a014af00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -25,10 +25,13 @@ import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -48,11 +51,13 @@ public class PipScheduler {
private final ShellExecutor mMainExecutor;
private final PipTransitionState mPipTransitionState;
private PipTransitionController mPipTransitionController;
- private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@Nullable private Runnable mUpdateMovementBoundsRunnable;
+ private PipAlphaAnimatorSupplier mPipAlphaAnimatorSupplier;
+
public PipScheduler(Context context,
PipBoundsState pipBoundsState,
ShellExecutor mainExecutor,
@@ -64,10 +69,7 @@ public class PipScheduler {
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
- }
-
- ShellExecutor getMainExecutor() {
- return mMainExecutor;
+ mPipAlphaAnimatorSupplier = PipAlphaAnimator::new;
}
void setPipTransitionController(PipTransitionController pipTransitionController) {
@@ -76,27 +78,29 @@ public class PipScheduler {
@Nullable
private WindowContainerTransaction getExitPipViaExpandTransaction() {
- if (mPipTransitionState.mPipTaskToken == null) {
+ WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+ if (pipTaskToken == null) {
return null;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
// final expanded bounds to be inherited from the parent
- wct.setBounds(mPipTransitionState.mPipTaskToken, null);
+ wct.setBounds(pipTaskToken, null);
// if we are hitting a multi-activity case
// windowing mode change will reparent to original host task
- wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
return wct;
}
@Nullable
private WindowContainerTransaction getRemovePipTransaction() {
- if (mPipTransitionState.mPipTaskToken == null) {
+ WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+ if (pipTaskToken == null) {
return null;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mPipTransitionState.mPipTaskToken, null);
- wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
- wct.reorder(mPipTransitionState.mPipTaskToken, false);
+ wct.setBounds(pipTaskToken, null);
+ wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.reorder(pipTaskToken, false);
return wct;
}
@@ -117,8 +121,8 @@ public class PipScheduler {
/** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */
public void removePipAfterAnimation() {
SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- PipAlphaAnimator animator = new PipAlphaAnimator(mContext,
- mPipTransitionState.mPinnedTaskLeash, tx, PipAlphaAnimator.FADE_OUT);
+ PipAlphaAnimator animator = mPipAlphaAnimatorSupplier.get(mContext,
+ mPipTransitionState.getPinnedTaskLeash(), tx, PipAlphaAnimator.FADE_OUT);
animator.setAnimationEndCallback(this::scheduleRemovePipImmediately);
animator.start();
}
@@ -159,13 +163,14 @@ public class PipScheduler {
* for running the animator will get this as an extra.
*/
public void scheduleAnimateResizePip(Rect toBounds, boolean configAtEnd, int duration) {
- if (mPipTransitionState.mPipTaskToken == null || !mPipTransitionState.isInPip()) {
+ WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
+ if (pipTaskToken == null || !mPipTransitionState.isInPip()) {
return;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+ wct.setBounds(pipTaskToken, toBounds);
if (configAtEnd) {
- wct.deferConfigToTransitionEnd(mPipTransitionState.mPipTaskToken);
+ wct.deferConfigToTransitionEnd(pipTaskToken);
}
mPipTransitionController.startResizeTransition(wct, duration);
}
@@ -203,8 +208,8 @@ public class PipScheduler {
"%s: Attempted to user resize PIP to empty bounds, aborting.", TAG);
return;
}
- SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash;
- final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ SurfaceControl leash = mPipTransitionState.getPinnedTaskLeash();
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
Matrix transformTensor = new Matrix();
final float[] mMatrixTmp = new float[9];
@@ -218,7 +223,7 @@ public class PipScheduler {
tx.apply();
}
- void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
+ void setUpdateMovementBoundsRunnable(@Nullable Runnable updateMovementBoundsRunnable) {
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
}
@@ -235,4 +240,23 @@ public class PipScheduler {
mPipBoundsState.setBounds(newBounds);
maybeUpdateMovementBounds();
}
+
+ @VisibleForTesting
+ void setSurfaceControlTransactionFactory(
+ @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+ mSurfaceControlTransactionFactory = factory;
+ }
+
+ @VisibleForTesting
+ interface PipAlphaAnimatorSupplier {
+ PipAlphaAnimator get(@NonNull Context context,
+ SurfaceControl leash,
+ SurfaceControl.Transaction tx,
+ @PipAlphaAnimator.Fade int direction);
+ }
+
+ @VisibleForTesting
+ void setPipAlphaAnimatorSupplier(@NonNull PipAlphaAnimatorSupplier supplier) {
+ mPipAlphaAnimatorSupplier = supplier;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 2c7584af1f07..2f9371536a16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -29,6 +29,8 @@ import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
@@ -36,6 +38,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip2.animation.PipResizeAnimator;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import java.util.ArrayList;
@@ -49,7 +52,8 @@ import java.util.List;
public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
PipTransitionState.PipTransitionStateChangedListener {
private static final int ASPECT_RATIO_CHANGE_DURATION = 250;
- private static final String ANIMATING_ASPECT_RATIO_CHANGE = "animating_aspect_ratio_change";
+ @VisibleForTesting
+ static final String ANIMATING_ASPECT_RATIO_CHANGE = "animating_aspect_ratio_change";
private final Context mContext;
private final PipTransitionState mPipTransitionState;
@@ -63,6 +67,8 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
private boolean mWaitingForAspectRatioChange = false;
private final List<PipParamsChangedCallback> mPipParamsChangedListeners = new ArrayList<>();
+ private PipResizeAnimatorSupplier mPipResizeAnimatorSupplier;
+
public PipTaskListener(Context context,
ShellTaskOrganizer shellTaskOrganizer,
PipTransitionState pipTransitionState,
@@ -84,6 +90,7 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP);
});
}
+ mPipResizeAnimatorSupplier = PipResizeAnimator::new;
}
void setPictureInPictureParams(@Nullable PictureInPictureParams params) {
@@ -121,6 +128,9 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
if (mPictureInPictureParams.equals(params)) {
return;
}
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "onTaskInfoChanged: %s, state=%s oldParams=%s newParams=%s",
+ taskInfo.topActivity, mPipTransitionState, mPictureInPictureParams, params);
setPictureInPictureParams(params);
float newAspectRatio = mPictureInPictureParams.getAspectRatioFloat();
if (PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
@@ -167,18 +177,18 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
- Preconditions.checkNotNull(mPipTransitionState.mPinnedTaskLeash,
+ Preconditions.checkNotNull(mPipTransitionState.getPinnedTaskLeash(),
"Leash is null for bounds transition.");
if (mWaitingForAspectRatioChange) {
- PipResizeAnimator animator = new PipResizeAnimator(mContext,
- mPipTransitionState.mPinnedTaskLeash, startTx, finishTx,
+ mWaitingForAspectRatioChange = false;
+ PipResizeAnimator animator = mPipResizeAnimatorSupplier.get(mContext,
+ mPipTransitionState.getPinnedTaskLeash(), startTx, finishTx,
destinationBounds,
mPipBoundsState.getBounds(), destinationBounds, duration,
0f /* delta */);
- animator.setAnimationEndCallback(() -> {
- mPipScheduler.scheduleFinishResizePip(destinationBounds);
- });
+ animator.setAnimationEndCallback(
+ () -> mPipScheduler.scheduleFinishResizePip(destinationBounds));
animator.start();
}
break;
@@ -192,4 +202,22 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
default void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
}
}
+
+ @VisibleForTesting
+ interface PipResizeAnimatorSupplier {
+ PipResizeAnimator get(@NonNull Context context,
+ @NonNull SurfaceControl leash,
+ @Nullable SurfaceControl.Transaction startTx,
+ @Nullable SurfaceControl.Transaction finishTx,
+ @NonNull Rect baseBounds,
+ @NonNull Rect startBounds,
+ @NonNull Rect endBounds,
+ int duration,
+ float delta);
+ }
+
+ @VisibleForTesting
+ void setPipResizeAnimatorSupplier(@NonNull PipResizeAnimatorSupplier supplier) {
+ mPipResizeAnimatorSupplier = supplier;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 19d293e829ad..65972fb7df48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -231,17 +231,15 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
// KCA triggered movement to wait for other transitions (e.g. due to IME changes).
return;
}
- mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
- boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
- || mPipBoundsState.hasUserResizedPip());
- int delta = mPipBoundsAlgorithm.getEntryDestinationBounds().top
- - mPipBoundsState.getBounds().top;
+ boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
+ || mPipBoundsState.hasUserResizedPip());
+ int delta = mPipBoundsAlgorithm.getEntryDestinationBounds().top
+ - mPipBoundsState.getBounds().top;
- if (!mIsImeShowing && !hasUserInteracted && delta != 0) {
- // If the user hasn't interacted with PiP, we respect the keep clear areas
- mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
- }
- });
+ if (!mIsImeShowing && !hasUserInteracted && delta != 0) {
+ // If the user hasn't interacted with PiP, we respect the keep clear areas
+ mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
+ }
};
if (PipUtils.isPip2ExperimentEnabled()) {
@@ -877,7 +875,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mMovementWithinDismiss = touchState.getDownTouchPosition().y
>= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
- mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.mPinnedTaskLeash);
+ mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.getPinnedTaskLeash());
// If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 64d8887ae5cd..02f595537d03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
@@ -230,6 +231,11 @@ public class PipTransition extends PipTransitionController implements
// If there is no PiP change, exit this transition handler and potentially try others.
if (pipChange == null) return false;
+ // Other targets might have default transforms applied that are not relevant when
+ // playing PiP transitions, so reset those transforms if needed.
+ prepareOtherTargetTransforms(info, startTransaction, finishTransaction);
+
+ // Update the PipTransitionState while supplying the PiP leash and token to be cached.
Bundle extra = new Bundle();
extra.putParcelable(PIP_TASK_TOKEN, pipChange.getContainer());
extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash());
@@ -325,9 +331,7 @@ public class PipTransition extends PipTransitionController implements
return false;
}
- SurfaceControl pipLeash = pipChange.getLeash();
- Preconditions.checkNotNull(pipLeash, "Leash is null for swipe-up transition.");
-
+ final SurfaceControl pipLeash = getLeash(pipChange);
final Rect destinationBounds = pipChange.getEndAbsBounds();
final SurfaceControl swipePipToHomeOverlay = mPipTransitionState.getSwipePipToHomeOverlay();
if (swipePipToHomeOverlay != null) {
@@ -343,26 +347,24 @@ public class PipTransition extends PipTransitionController implements
(destinationBounds.height() - overlaySize) / 2f);
}
- final int startRotation = pipChange.getStartRotation();
- final int endRotation = mPipDisplayLayoutState.getRotation();
- final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
- : startRotation - endRotation;
+ final int delta = getFixedRotationDelta(info, pipChange);
if (delta != ROTATION_0) {
- mPipTransitionState.setInFixedRotation(true);
- handleBoundsTypeFixedRotation(pipChange, pipActivityChange, endRotation);
+ // Update transition target changes in place to prepare for fixed rotation.
+ handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange);
}
- Rect sourceRectHint = null;
- if (pipChange.getTaskInfo() != null
- && pipChange.getTaskInfo().pictureInPictureParams != null) {
- sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
- }
+ // Update the src-rect-hint in params in place, to set up initial animator transform.
+ Rect sourceRectHint = getAdjustedSourceRectHint(info, pipChange, pipActivityChange);
+ pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint().set(sourceRectHint);
+ // Config-at-end transitions need to have their activities transformed before starting
+ // the animation; this makes the buffer seem like it's been updated to final size.
prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
pipActivityChange);
+
startTransaction.merge(finishTransaction);
PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
- startTransaction, finishTransaction, destinationBounds, sourceRectHint, delta);
+ startTransaction, finishTransaction, destinationBounds, delta);
animator.setEnterStartState(pipChange);
animator.onEnterAnimationUpdate(1.0f /* fraction */, startTransaction);
startTransaction.apply();
@@ -395,55 +397,36 @@ public class PipTransition extends PipTransitionController implements
return false;
}
+ final SurfaceControl pipLeash = getLeash(pipChange);
final Rect startBounds = pipChange.getStartAbsBounds();
final Rect endBounds = pipChange.getEndAbsBounds();
-
final PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
- final float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params);
- final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds,
- endBounds);
- final Rect adjustedSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint)
- : PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio);
-
- final SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
-
- // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
- // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
- // by the Transitions framework to simplify Task opening transitions.
- if (TransitionUtil.isOpeningType(info.getType())) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getLeash() == null) continue;
- if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
- startTransaction.setAlpha(change.getLeash(), 1f);
- }
- }
- }
-
- final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
- int startRotation = pipChange.getStartRotation();
- int endRotation = fixedRotationChange != null
- ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
- final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
- : startRotation - endRotation;
+ final Rect adjustedSourceRectHint = getAdjustedSourceRectHint(info, pipChange,
+ pipActivityChange);
+ final int delta = getFixedRotationDelta(info, pipChange);
if (delta != ROTATION_0) {
- mPipTransitionState.setInFixedRotation(true);
- handleBoundsTypeFixedRotation(pipChange, pipActivityChange,
- fixedRotationChange.getEndFixedRotation());
+ // Update transition target changes in place to prepare for fixed rotation.
+ handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange);
}
PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
- startTransaction, finishTransaction, endBounds, adjustedSourceRectHint, delta);
- if (sourceRectHint == null) {
- // update the src-rect-hint in params in place, to set up initial animator transform.
- params.getSourceRectHint().set(adjustedSourceRectHint);
+ startTransaction, finishTransaction, endBounds, delta);
+ if (PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds, endBounds) == null) {
+ // If app provided src-rect-hint is invalid, use app icon overlay.
animator.setAppIconContentOverlay(
mContext, startBounds, endBounds, pipChange.getTaskInfo().topActivityInfo,
mPipBoundsState.getLauncherState().getAppIconSizePx());
}
+ // Update the src-rect-hint in params in place, to set up initial animator transform.
+ params.getSourceRectHint().set(adjustedSourceRectHint);
+
+ // Config-at-end transitions need to have their activities transformed before starting
+ // the animation; this makes the buffer seem like it's been updated to final size.
prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
pipActivityChange);
+
animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange));
animator.setAnimationEndCallback(() -> {
if (animator.getContentOverlayLeash() != null) {
@@ -465,11 +448,22 @@ public class PipTransition extends PipTransitionController implements
animator.start();
}
- private void handleBoundsTypeFixedRotation(TransitionInfo.Change pipTaskChange,
- TransitionInfo.Change pipActivityChange, int endRotation) {
- final Rect endBounds = pipTaskChange.getEndAbsBounds();
- final Rect endActivityBounds = pipActivityChange.getEndAbsBounds();
- int startRotation = pipTaskChange.getStartRotation();
+ private void handleBoundsEnterFixedRotation(TransitionInfo info,
+ TransitionInfo.Change outPipTaskChange,
+ TransitionInfo.Change outPipActivityChange) {
+ final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+ final Rect endBounds = outPipTaskChange.getEndAbsBounds();
+ final Rect endActivityBounds = outPipActivityChange.getEndAbsBounds();
+ int startRotation = outPipTaskChange.getStartRotation();
+ int endRotation = fixedRotationChange != null
+ ? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation();
+
+ if (startRotation == endRotation) {
+ return;
+ }
+
+ // This is used by display change listeners to respond properly to fixed rotation.
+ mPipTransitionState.setInFixedRotation(true);
// Cache the task to activity offset to potentially restore later.
Point activityEndOffset = new Point(endActivityBounds.left - endBounds.left,
@@ -498,6 +492,26 @@ public class PipTransition extends PipTransitionController implements
endBounds.top + activityEndOffset.y);
}
+ private void handleExpandFixedRotation(TransitionInfo.Change outPipTaskChange, int delta) {
+ final Rect endBounds = outPipTaskChange.getEndAbsBounds();
+ final int width = endBounds.width();
+ final int height = endBounds.height();
+ final int left = endBounds.left;
+ final int top = endBounds.top;
+ int newTop, newLeft;
+
+ if (delta == Surface.ROTATION_90) {
+ newLeft = top;
+ newTop = -(left + width);
+ } else {
+ newLeft = -(height + top);
+ newTop = left;
+ }
+ // Modify the endBounds, rotating and placing them potentially off-screen, so that
+ // as we translate and rotate around the origin, we place them right into the target.
+ endBounds.set(newLeft, newTop, newLeft + height, newTop + width);
+ }
+
private boolean startAlphaTypeEnterAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -509,7 +523,7 @@ public class PipTransition extends PipTransitionController implements
}
Rect destinationBounds = pipChange.getEndAbsBounds();
- SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
// Start transition with 0 alpha at the entry bounds.
@@ -529,7 +543,7 @@ public class PipTransition extends PipTransitionController implements
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- WindowContainerToken pipToken = mPipTransitionState.mPipTaskToken;
+ WindowContainerToken pipToken = mPipTransitionState.getPipTaskToken();
TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
if (pipChange == null) {
@@ -550,33 +564,47 @@ public class PipTransition extends PipTransitionController implements
}
}
- // for multi activity, we need to manually set the leash layer
- if (pipChange.getTaskInfo() == null) {
- TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
- if (parent != null) {
- startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
- }
+ // The parent change if we were in a multi-activity PiP; null if single activity PiP.
+ final TransitionInfo.Change parentBeforePip = pipChange.getTaskInfo() == null
+ ? getChangeByToken(info, pipChange.getParent()) : null;
+ if (parentBeforePip != null) {
+ // For multi activity, we need to manually set the leash layer
+ startTransaction.setLayer(parentBeforePip.getLeash(), Integer.MAX_VALUE - 1);
}
- Rect startBounds = pipChange.getStartAbsBounds();
- Rect endBounds = pipChange.getEndAbsBounds();
- SurfaceControl pipLeash = pipChange.getLeash();
- Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");
+ final Rect startBounds = pipChange.getStartAbsBounds();
+ final Rect endBounds = pipChange.getEndAbsBounds();
+ final SurfaceControl pipLeash = getLeash(pipChange);
- Rect sourceRectHint = null;
- if (pipChange.getTaskInfo() != null
- && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ PictureInPictureParams params = null;
+ if (pipChange.getTaskInfo() != null) {
// single activity
- sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
- } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
+ params = pipChange.getTaskInfo().pictureInPictureParams;
+ } else if (parentBeforePip != null && parentBeforePip.getTaskInfo() != null) {
// multi activity
- sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
+ params = parentBeforePip.getTaskInfo().pictureInPictureParams;
+ }
+ final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, endBounds,
+ startBounds);
+
+ // We define delta = startRotation - endRotation, so we need to flip the sign.
+ final int delta = -getFixedRotationDelta(info, pipChange);
+ if (delta != ROTATION_0) {
+ // Update PiP target change in place to prepare for fixed rotation;
+ handleExpandFixedRotation(pipChange, delta);
}
PipExpandAnimator animator = new PipExpandAnimator(mContext, pipLeash,
startTransaction, finishTransaction, endBounds, startBounds, endBounds,
- sourceRectHint, Surface.ROTATION_0);
- animator.setAnimationEndCallback(this::finishTransition);
+ sourceRectHint, delta);
+ animator.setAnimationEndCallback(() -> {
+ if (parentBeforePip != null) {
+ // TODO b/377362511: Animate local leash instead to also handle letterbox case.
+ // For multi-activity, set the crop to be null
+ finishTransaction.setCrop(pipLeash, null);
+ }
+ finishTransition();
+ });
animator.start();
return true;
}
@@ -631,6 +659,72 @@ public class PipTransition extends PipTransitionController implements
return null;
}
+ @NonNull
+ private Rect getAdjustedSourceRectHint(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change pipTaskChange,
+ @NonNull TransitionInfo.Change pipActivityChange) {
+ final Rect startBounds = pipTaskChange.getStartAbsBounds();
+ final Rect endBounds = pipTaskChange.getEndAbsBounds();
+ final PictureInPictureParams params = pipTaskChange.getTaskInfo().pictureInPictureParams;
+
+ // Get the source-rect-hint provided by the app and check its validity; null if invalid.
+ final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds,
+ endBounds);
+
+ final Rect adjustedSourceRectHint = new Rect();
+ if (sourceRectHint != null) {
+ adjustedSourceRectHint.set(sourceRectHint);
+ // If multi-activity PiP, use the parent task before PiP to retrieve display cutouts;
+ // then, offset the valid app provided source rect hint by the cutout insets.
+ // For single-activity PiP, just use the pinned task to get the cutouts instead.
+ TransitionInfo.Change parentBeforePip = pipActivityChange.getLastParent() != null
+ ? getChangeByToken(info, pipActivityChange.getLastParent()) : null;
+ Rect cutoutInsets = parentBeforePip != null
+ ? parentBeforePip.getTaskInfo().displayCutoutInsets
+ : pipTaskChange.getTaskInfo().displayCutoutInsets;
+ if (cutoutInsets != null
+ && getFixedRotationDelta(info, pipTaskChange) == ROTATION_90) {
+ adjustedSourceRectHint.offset(cutoutInsets.left, cutoutInsets.top);
+ }
+ } else {
+ // For non-valid app provided src-rect-hint, calculate one to crop into during
+ // app icon overlay animation.
+ float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params);
+ adjustedSourceRectHint.set(
+ PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio));
+ }
+ return adjustedSourceRectHint;
+ }
+
+ @Surface.Rotation
+ private int getFixedRotationDelta(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change pipChange) {
+ TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+ int startRotation = pipChange.getStartRotation();
+ int endRotation = fixedRotationChange != null
+ ? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation();
+ int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
+ : startRotation - endRotation;
+ return delta;
+ }
+
+ private void prepareOtherTargetTransforms(TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
+ // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
+ // by the Transitions framework to simplify Task opening transitions.
+ if (TransitionUtil.isOpeningType(info.getType())) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getLeash() == null) continue;
+ if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
+ startTransaction.setAlpha(change.getLeash(), 1f);
+ }
+ }
+ }
+
+ }
+
private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
// cache the original task token to check for multi-activity case later
@@ -679,11 +773,11 @@ public class PipTransition extends PipTransitionController implements
}
private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
- if (mPipTransitionState.mPipTaskToken == null) {
+ if (mPipTransitionState.getPipTaskToken() == null) {
// PiP removal makes sense if enter-PiP has cached a valid pinned task token.
return false;
}
- TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.mPipTaskToken);
+ TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.getPipTaskToken());
if (pipChange == null) {
// Search for the PiP change by token since the windowing mode might be FULLSCREEN now.
return false;
@@ -723,6 +817,13 @@ public class PipTransition extends PipTransitionController implements
}
}
+ @NonNull
+ private SurfaceControl getLeash(TransitionInfo.Change change) {
+ SurfaceControl leash = change.getLeash();
+ Preconditions.checkNotNull(leash, "Leash is null for change=" + change);
+ return leash;
+ }
+
//
// Miscellaneous callbacks and listeners
//
@@ -758,19 +859,19 @@ public class PipTransition extends PipTransitionController implements
Preconditions.checkState(extra != null,
"No extra bundle for " + mPipTransitionState);
- mPipTransitionState.mPipTaskToken = extra.getParcelable(
- PIP_TASK_TOKEN, WindowContainerToken.class);
- mPipTransitionState.mPinnedTaskLeash = extra.getParcelable(
- PIP_TASK_LEASH, SurfaceControl.class);
- boolean hasValidTokenAndLeash = mPipTransitionState.mPipTaskToken != null
- && mPipTransitionState.mPinnedTaskLeash != null;
+ mPipTransitionState.setPipTaskToken(extra.getParcelable(
+ PIP_TASK_TOKEN, WindowContainerToken.class));
+ mPipTransitionState.setPinnedTaskLeash(extra.getParcelable(
+ PIP_TASK_LEASH, SurfaceControl.class));
+ boolean hasValidTokenAndLeash = mPipTransitionState.getPipTaskToken() != null
+ && mPipTransitionState.getPinnedTaskLeash() != null;
Preconditions.checkState(hasValidTokenAndLeash,
"Unexpected bundle for " + mPipTransitionState);
break;
case PipTransitionState.EXITED_PIP:
- mPipTransitionState.mPipTaskToken = null;
- mPipTransitionState.mPinnedTaskLeash = null;
+ mPipTransitionState.setPipTaskToken(null);
+ mPipTransitionState.setPinnedTaskLeash(null);
break;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index ccdd66b5d1a8..8e90bfee2636 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -138,11 +138,11 @@ public class PipTransitionState {
// pinned PiP task's WC token
@Nullable
- WindowContainerToken mPipTaskToken;
+ private WindowContainerToken mPipTaskToken;
// pinned PiP task's leash
@Nullable
- SurfaceControl mPinnedTaskLeash;
+ private SurfaceControl mPinnedTaskLeash;
// Overlay leash potentially used during swipe PiP to home transition;
// if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid.
@@ -304,6 +304,22 @@ public class PipTransitionState {
mSwipePipToHomeAppBounds.setEmpty();
}
+ @Nullable WindowContainerToken getPipTaskToken() {
+ return mPipTaskToken;
+ }
+
+ public void setPipTaskToken(@Nullable WindowContainerToken token) {
+ mPipTaskToken = token;
+ }
+
+ @Nullable SurfaceControl getPinnedTaskLeash() {
+ return mPinnedTaskLeash;
+ }
+
+ void setPinnedTaskLeash(@Nullable SurfaceControl leash) {
+ mPinnedTaskLeash = leash;
+ }
+
/**
* @return true if either in swipe or button-nav fixed rotation.
*/
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 f739d65e63c3..49cf8ae81aa8 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
@@ -63,6 +63,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
"Bubbles"),
WM_SHELL_COMPAT_UI(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_COMPAT_UI),
+ WM_SHELL_APP_COMPAT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_APP_COMPAT),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
@@ -131,6 +133,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
private static final String TAG_WM_SPLIT_SCREEN = "ShellSplitScreen";
private static final String TAG_WM_DESKTOP_MODE = "ShellDesktopMode";
private static final String TAG_WM_COMPAT_UI = "CompatUi";
+ private static final String TAG_WM_APP_COMPAT = "AppCompat";
private static final boolean ENABLE_DEBUG = true;
private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index 799028a5507a..4a301cc0b603 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -24,7 +24,7 @@ import android.os.Bundle;
import com.android.wm.shell.recents.IRecentsAnimationRunner;
import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
/**
* Interface that is exposed to remote callers to fetch recent tasks.
@@ -44,7 +44,7 @@ interface IRecentTasks {
/**
* Gets the set of recent tasks.
*/
- GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
+ GroupedTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
/**
* Gets the set of running tasks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 371bdd5c6469..b58f0681c571 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -18,6 +18,8 @@ package com.android.wm.shell.recents;
import android.app.ActivityManager.RunningTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
+
/**
* Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
*/
@@ -44,8 +46,8 @@ oneway interface IRecentTasksListener {
void onRunningTaskChanged(in RunningTaskInfo taskInfo);
/** A task has moved to front. */
- oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo);
+ void onTaskMovedToFront(in GroupedTaskInfo[] visibleTasks);
/** A task info has changed. */
- oneway void onTaskInfoChanged(in RunningTaskInfo taskInfo);
+ void onTaskInfoChanged(in RunningTaskInfo taskInfo);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 8c5d1e7e069d..364a087211c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -19,7 +19,7 @@ package com.android.wm.shell.recents;
import android.annotation.Nullable;
import android.graphics.Color;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
import java.util.List;
@@ -35,7 +35,7 @@ public interface RecentTasks {
* Gets the set of recent tasks.
*/
default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
- Consumer<List<GroupedRecentTaskInfo>> callback) {
+ Consumer<List<GroupedTaskInfo>> callback) {
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index faa20159f64a..9911669d2cb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -24,10 +24,12 @@ import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_R
import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -55,7 +57,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -379,7 +381,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
return;
}
try {
- mListener.onTaskMovedToFront(taskInfo);
+ GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
+ mListener.onTaskMovedToFront(new GroupedTaskInfo[]{ runningTask });
} catch (RemoteException e) {
Slog.w(TAG, "Failed call onTaskMovedToFront", e);
}
@@ -407,27 +410,27 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@VisibleForTesting
- ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
+ ArrayList<GroupedTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
// Note: the returned task list is from the most-recent to least-recent order
- final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
+ final List<RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
maxNum, flags, userId);
// Make a mapping of task id -> task info
- final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
+ final SparseArray<TaskInfo> rawMapping = new SparseArray<>();
for (int i = 0; i < rawList.size(); i++) {
- final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
+ final TaskInfo taskInfo = rawList.get(i);
rawMapping.put(taskInfo.taskId, taskInfo);
}
- ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+ ArrayList<TaskInfo> freeformTasks = new ArrayList<>();
Set<Integer> minimizedFreeformTasks = new HashSet<>();
int mostRecentFreeformTaskIndex = Integer.MAX_VALUE;
// Pull out the pairs as we iterate back in the list
- ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
+ ArrayList<GroupedTaskInfo> recentTasks = new ArrayList<>();
for (int i = 0; i < rawList.size(); i++) {
- final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
+ final RecentTaskInfo taskInfo = rawList.get(i);
if (!rawMapping.contains(taskInfo.taskId)) {
// If it's not in the mapping, then it was already paired with another task
continue;
@@ -460,20 +463,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId, INVALID_TASK_ID);
if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
pairedTaskId)) {
- final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
+ final TaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(GroupedRecentTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
+ recentTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- recentTasks.add(GroupedRecentTaskInfo.forSingleTask(taskInfo));
+ recentTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo));
}
}
// Add a special entry for freeform tasks
if (!freeformTasks.isEmpty()) {
recentTasks.add(mostRecentFreeformTaskIndex,
- GroupedRecentTaskInfo.forFreeformTasks(
- freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0]),
+ GroupedTaskInfo.forFreeformTasks(
+ freeformTasks,
minimizedFreeformTasks));
}
@@ -514,16 +517,16 @@ public class RecentTasksController implements TaskStackListenerCallback,
* {@param ignoreTaskToken} if it is non-null.
*/
@Nullable
- public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName,
+ public RecentTaskInfo findTaskInBackground(ComponentName componentName,
int userId, @Nullable WindowContainerToken ignoreTaskToken) {
if (componentName == null) {
return null;
}
- List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ List<RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) {
- final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ final RecentTaskInfo task = tasks.get(i);
if (task.isVisible) {
continue;
}
@@ -541,12 +544,12 @@ public class RecentTasksController implements TaskStackListenerCallback,
* Find the background task that match the given taskId.
*/
@Nullable
- public ActivityManager.RecentTaskInfo findTaskInBackground(int taskId) {
- List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ public RecentTaskInfo findTaskInBackground(int taskId) {
+ List<RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) {
- final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ final RecentTaskInfo task = tasks.get(i);
if (task.isVisible) {
continue;
}
@@ -570,7 +573,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
pw.println(prefix + TAG);
pw.println(prefix + " mListener=" + mListener);
pw.println(prefix + "Tasks:");
- ArrayList<GroupedRecentTaskInfo> recentTasks = getRecentTasks(Integer.MAX_VALUE,
+ ArrayList<GroupedTaskInfo> recentTasks = getRecentTasks(Integer.MAX_VALUE,
ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
for (int i = 0; i < recentTasks.size(); i++) {
pw.println(innerPrefix + recentTasks.get(i));
@@ -584,9 +587,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
private class RecentTasksImpl implements RecentTasks {
@Override
public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
- Consumer<List<GroupedRecentTaskInfo>> callback) {
+ Consumer<List<GroupedTaskInfo>> callback) {
mMainExecutor.execute(() -> {
- List<GroupedRecentTaskInfo> tasks =
+ List<GroupedTaskInfo> tasks =
RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
executor.execute(() -> callback.accept(tasks));
});
@@ -650,7 +653,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ public void onTaskMovedToFront(GroupedTaskInfo[] taskInfo) {
mListener.call(l -> l.onTaskMovedToFront(taskInfo));
}
@@ -692,17 +695,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@Override
- public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
+ public GroupedTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
throws RemoteException {
if (mController == null) {
// The controller is already invalidated -- just return an empty task list for now
- return new GroupedRecentTaskInfo[0];
+ return new GroupedTaskInfo[0];
}
- final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null};
+ final GroupedTaskInfo[][] out = new GroupedTaskInfo[][]{null};
executeRemoteCallWithTaskPermission(mController, "getRecentTasks",
- (controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
- .toArray(new GroupedRecentTaskInfo[0]),
+ (controller) -> {
+ List<GroupedTaskInfo> tasks = controller.getRecentTasks(
+ maxNum, flags, userId);
+ out[0] = tasks.toArray(new GroupedTaskInfo[0]);
+ },
true /* blocking */);
return out[0];
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 6d4d4b410be8..417a6558ffcc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -34,6 +34,8 @@ import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION;
import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -67,6 +69,7 @@ import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUtils;
@@ -216,8 +219,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
break;
}
}
- final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
- mixer == null ? this : mixer);
+ final int transitionType = Flags.enableShellTopTaskTracking()
+ ? TRANSIT_START_RECENTS_TRANSITION
+ : TRANSIT_TO_FRONT;
+ final IBinder transition = mTransitions.startTransition(transitionType,
+ wct, mixer == null ? this : mixer);
if (mixer != null) {
setTransitionForMixer.accept(transition);
}
@@ -300,7 +306,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
"RecentsTransitionHandler.mergeAnimation: no controller found");
return;
}
- controller.merge(info, t, finishCallback);
+ controller.merge(info, t, mergeTarget, finishCallback);
}
@Override
@@ -367,6 +373,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
private boolean mPausingSeparateHome = false;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
private PictureInPictureSurfaceTransaction mPipTransaction = null;
+ // This is the transition that backs the entire recents transition, and the one that the
+ // pending finish transition below will be merged into
private IBinder mTransition = null;
private boolean mKeyguardLocked = false;
private boolean mWillFinishToHome = false;
@@ -386,6 +394,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
// next called.
private Pair<int[], TaskSnapshot[]> mPendingPauseSnapshotsForCancel;
+ // Used to track a pending finish transition
+ private IBinder mPendingFinishTransition;
+ private IResultReceiver mPendingRunnerFinishCb;
+
RecentsController(IRecentsAnimationRunner listener) {
mInstanceId = System.identityHashCode(this);
mListener = listener;
@@ -523,6 +535,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
mInfo = null;
mTransition = null;
mPendingPauseSnapshotsForCancel = null;
+ mPipTaskId = -1;
+ mPipTask = null;
+ mPipTransaction = null;
+ mPendingRunnerFinishCb = null;
+ mPendingFinishTransition = null;
mControllers.remove(this);
for (int i = 0; i < mStateListeners.size(); i++) {
mStateListeners.get(i).onAnimationStateChanged(false);
@@ -544,10 +561,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
.findFirst()
.get();
final RemoteAnimationTarget openingTarget = TransitionUtil.newSyntheticTarget(
- homeTask, mShellTaskOrganizer.getHomeTaskOverlayContainer(), TRANSIT_OPEN,
+ homeTask, mShellTaskOrganizer.getHomeTaskSurface(), TRANSIT_OPEN,
0, true /* isTranslucent */);
final RemoteAnimationTarget closingTarget = TransitionUtil.newSyntheticTarget(
- homeTask, mShellTaskOrganizer.getHomeTaskOverlayContainer(), TRANSIT_CLOSE,
+ homeTask, mShellTaskOrganizer.getHomeTaskSurface(), TRANSIT_CLOSE,
0, true /* isTranslucent */);
final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
apps.add(openingTarget);
@@ -734,6 +751,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
// the pausing apps.
t.setLayer(target.leash, layer);
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " not handling home taskId=%d", taskInfo.taskId);
// do nothing
} else if (TransitionUtil.isOpeningType(change.getMode())) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -872,16 +891,35 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
}
}
+ /**
+ * Note: because we use a book-end transition to finish the recents transition, we must
+ * either always merge the incoming transition, or always cancel the recents transition
+ * if we don't handle the incoming transition to ensure that the end transition is queued
+ * before any unhandled transitions.
+ */
@SuppressLint("NewApi")
- void merge(TransitionInfo info, SurfaceControl.Transaction t,
+ void merge(TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget,
Transitions.TransitionFinishCallback finishCallback) {
if (mFinishCB == null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.merge: skip, no finish callback",
mInstanceId);
- // This was no-op'd (likely a repeated start) and we've already sent finish.
+ // This was no-op'd (likely a repeated start) and we've already completed finish.
return;
}
+
+ if (Flags.enableShellTopTaskTracking()
+ && info.getType() == TRANSIT_END_RECENTS_TRANSITION
+ && mergeTarget == mTransition) {
+ // This is a pending finish, so merge the end transition to trigger completing the
+ // cleanup of the recents transition
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION",
+ mInstanceId);
+ finishCallback.onTransitionFinished(null /* wct */);
+ return;
+ }
+
if (info.getType() == TRANSIT_SLEEP) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.merge: transit_sleep", mInstanceId);
@@ -1245,8 +1283,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
return;
}
- if (mFinishCB == null) {
+ if (mFinishCB == null
+ || (Flags.enableShellTopTaskTracking() && mPendingFinishTransition != null)) {
Slog.e(TAG, "Duplicate call to finish");
+ if (runnerFinishCb != null) {
+ try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: calling finish callback",
+ mInstanceId);
+ runnerFinishCb.send(0, null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report transition finished", e);
+ }
+ }
return;
}
@@ -1254,19 +1303,22 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
&& !mWillFinishToHome
&& mPausingTasks != null
&& mState == STATE_NORMAL;
- if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
- mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
- } else if (!toHome) {
- // For some transitions, we may have notified home activity that it became visible.
- // We need to notify the observer that we are no longer going home.
- mHomeTransitionObserver.notifyHomeVisibilityChanged(false);
+ if (!Flags.enableShellTopTaskTracking()) {
+ // This is only necessary when the recents transition is finished using a finishWCT,
+ // otherwise a new transition will notify the relevant observers
+ if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
+ mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
+ } else if (!toHome) {
+ // For some transitions, we may have notified home activity that it became
+ // visible. We need to notify the observer that we are no longer going home.
+ mHomeTransitionObserver.notifyHomeVisibilityChanged(false);
+ }
}
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.finishInner: toHome=%b userLeave=%b "
- + "willFinishToHome=%b state=%d",
- mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState);
- final Transitions.TransitionFinishCallback finishCB = mFinishCB;
- mFinishCB = null;
+ + "willFinishToHome=%b state=%d reason=%s",
+ mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState, reason);
final SurfaceControl.Transaction t = mFinishTransaction;
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1328,6 +1380,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
for (int i = 0; i < mClosingTasks.size(); ++i) {
cleanUpPausingOrClosingTask(mClosingTasks.get(i), wct, t, sendUserLeaveHint);
}
+
if (mPipTransaction != null && sendUserLeaveHint) {
SurfaceControl pipLeash = null;
TransitionInfo.Change pipChange = null;
@@ -1379,15 +1432,50 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
mTransitions.startTransition(TRANSIT_PIP, wct, null /* handler */);
// We need to clear the WCT to send finishWCT=null for Recents.
wct.clear();
+
+ if (Flags.enableShellTopTaskTracking()) {
+ // In this case, we've already started the PIP transition, so we can
+ // clean up immediately
+ mPendingRunnerFinishCb = runnerFinishCb;
+ onFinishInner(null);
+ return;
+ }
}
}
- mPipTaskId = -1;
- mPipTask = null;
- mPipTransaction = null;
}
}
+
+ if (Flags.enableShellTopTaskTracking()) {
+ if (!wct.isEmpty()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: "
+ + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId);
+ mPendingRunnerFinishCb = runnerFinishCb;
+ mPendingFinishTransition = mTransitions.startTransition(
+ TRANSIT_END_RECENTS_TRANSITION, wct,
+ new PendingFinishTransitionHandler());
+ } else {
+ // If there's no work to do, just go ahead and clean up
+ mPendingRunnerFinishCb = runnerFinishCb;
+ onFinishInner(null /* wct */);
+ }
+ } else {
+ mPendingRunnerFinishCb = runnerFinishCb;
+ onFinishInner(wct);
+ }
+ }
+
+ /**
+ * Runs the actual logic to finish the recents transition.
+ */
+ private void onFinishInner(@Nullable WindowContainerTransaction wct) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: Completing finish", mInstanceId);
+ final Transitions.TransitionFinishCallback finishCb = mFinishCB;
+ final IResultReceiver runnerFinishCb = mPendingRunnerFinishCb;
+
cleanUp();
- finishCB.onTransitionFinished(wct.isEmpty() ? null : wct);
+ finishCb.onTransitionFinished(wct);
if (runnerFinishCb != null) {
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -1472,6 +1560,40 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
}
});
}
+
+ /**
+ * A temporary transition handler used with the pending finish transition, which runs the
+ * cleanup/finish logic once the pending transition is merged/handled.
+ * This is only initialized if Flags.enableShellTopTaskTracking() is enabled.
+ */
+ private class PendingFinishTransitionHandler implements Transitions.TransitionHandler {
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishTransaction) {
+ // Once we have merged (or not if the WCT didn't result in any changes), then we can
+ // run the pending finish logic
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.onTransitionConsumed: "
+ + "Consumed pending finish transition", mInstanceId);
+ onFinishInner(null /* wct */);
+ }
+ };
};
/** Utility class to track the state of a task as-seen by recents. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index b33f3e98f350..4407e5b3106f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -245,9 +245,9 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return;
}
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
- mVisible = taskInfo.isVisible && taskInfo.isVisibleRequested;
+ mVisible = isStageVisible();
mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */,
- mVisible);
+ taskInfo.isVisible && taskInfo.isVisibleRequested);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -293,6 +293,19 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
t.reparent(sc, findTaskSurface(taskId));
}
+ /**
+ * Checks against all children task info and return true if any are marked as visible.
+ */
+ private boolean isStageVisible() {
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ if (mChildrenTaskInfo.valueAt(i).isVisible
+ && mChildrenTaskInfo.valueAt(i).isVisibleRequested) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private SurfaceControl findTaskSurface(int taskId) {
if (mRootTaskInfo.taskId == taskId) {
return mRootLeash;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index a1a9ca9fd2bd..4ea4613185e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -136,6 +136,7 @@ public class DefaultSurfaceAnimator {
@NonNull final Animation mAnim;
@Nullable final Point mPosition;
@Nullable final Rect mClipRect;
+ @Nullable private final Rect mAnimClipRect;
final float mCornerRadius;
final boolean mIsActivity;
@@ -147,6 +148,7 @@ public class DefaultSurfaceAnimator {
mPosition = (position != null && (position.x != 0 || position.y != 0))
? position : null;
mClipRect = (clipRect != null && !clipRect.isEmpty()) ? clipRect : null;
+ mAnimClipRect = mClipRect != null ? new Rect() : null;
mCornerRadius = cornerRadius;
mIsActivity = isActivity;
}
@@ -169,18 +171,26 @@ public class DefaultSurfaceAnimator {
t.setAlpha(leash, transformation.getAlpha());
if (mClipRect != null) {
- Rect clipRect = mClipRect;
+ boolean needCrop = false;
+ mAnimClipRect.set(mClipRect);
+ if (transformation.hasClipRect()
+ && com.android.window.flags.Flags.respectAnimationClip()) {
+ mAnimClipRect.intersectUnchecked(transformation.getClipRect());
+ needCrop = true;
+ }
final Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
if (!extensionInsets.equals(Insets.NONE)) {
// Clip out any overflowing edge extension.
- clipRect = new Rect(mClipRect);
- clipRect.inset(extensionInsets);
- t.setCrop(leash, clipRect);
+ mAnimClipRect.inset(extensionInsets);
+ needCrop = true;
}
if (mCornerRadius > 0 && mAnim.hasRoundedCorners()) {
// Rounded corner can only be applied if a crop is set.
- t.setCrop(leash, clipRect);
t.setCornerRadius(leash, mCornerRadius);
+ needCrop = true;
+ }
+ if (needCrop) {
+ t.setCrop(leash, mAnimClipRect);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index ec58292b352c..9fcf98b9efc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -353,7 +353,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
boolean isSeamlessDisplayChange = false;
if (mode == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) {
- if (info.getType() == TRANSIT_CHANGE) {
+ if (info.getType() == TRANSIT_CHANGE || isOnlyTranslucent) {
final int anim = getRotationAnimationHint(change, info, mDisplayController);
isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS;
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
@@ -395,10 +395,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
continue;
}
// No default animation for this, so just update bounds/position.
- final int rootIdx = TransitionUtil.rootIndexFor(change, info);
- startTransaction.setPosition(change.getLeash(),
- change.getEndAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
- change.getEndAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
+ if (change.getParent() == null) {
+ // For independent change without a parent, we have reparented it to the root
+ // leash in Transitions#setupAnimHierarchy.
+ final int rootIdx = TransitionUtil.rootIndexFor(change, info);
+ startTransaction.setPosition(change.getLeash(),
+ change.getEndAbsBounds().left - info.getRoot(rootIdx).getOffset().x,
+ change.getEndAbsBounds().top - info.getRoot(rootIdx).getOffset().y);
+ } else {
+ startTransaction.setPosition(change.getLeash(),
+ change.getEndRelOffset().x, change.getEndRelOffset().y);
+ }
// Seamless display transition doesn't need to animate.
if (isSeamlessDisplayChange) continue;
if (isTask || (change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)
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 1d456aed5f4d..3f191497e1ed 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
@@ -203,6 +203,12 @@ public class Transitions implements RemoteCallable<Transitions>,
/** Transition type to minimize a task. */
public static final int TRANSIT_MINIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 20;
+ /** Transition to start the recents transition */
+ public static final int TRANSIT_START_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 21;
+
+ /** Transition to end the recents transition */
+ public static final int TRANSIT_END_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 22;
+
/** Transition type for desktop mode transitions. */
public static final int TRANSIT_DESKTOP_MODE_TYPES =
WindowManager.TRANSIT_FIRST_CUSTOM + 100;
@@ -1875,6 +1881,8 @@ public class Transitions implements RemoteCallable<Transitions>,
case TRANSIT_SPLIT_PASSTHROUGH -> "SPLIT_PASSTHROUGH";
case TRANSIT_CLEANUP_PIP_EXIT -> "CLEANUP_PIP_EXIT";
case TRANSIT_MINIMIZE -> "MINIMIZE";
+ case TRANSIT_START_RECENTS_TRANSITION -> "START_RECENTS_TRANSITION";
+ case TRANSIT_END_RECENTS_TRANSITION -> "END_RECENTS_TRANSITION";
default -> "";
};
return typeStr + "(FIRST_CUSTOM+" + (transitType - TRANSIT_FIRST_CUSTOM) + ")";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index be4fd7c5eeec..c9f2d2e8c0e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -24,6 +24,9 @@ import static android.content.pm.PackageManager.FEATURE_PC;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+
+import android.annotation.NonNull;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -108,6 +111,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
}
mMainExecutor.execute(() -> {
mExclusionRegion.set(systemGestureExclusion);
+ onExclusionRegionChanged(displayId, mExclusionRegion);
});
}
};
@@ -161,7 +165,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
boolean isFocusedGlobally) {
final WindowDecoration decor = mWindowDecorByTaskId.get(taskId);
if (decor != null) {
- decor.relayout(decor.mTaskInfo, isFocusedGlobally);
+ decor.relayout(decor.mTaskInfo, isFocusedGlobally, decor.mExclusionRegion);
}
}
@@ -195,7 +199,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
return;
}
- decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
+ if (enableDisplayFocusInShellTransitions()) {
+ // Pass the current global focus status to avoid updates outside of a ShellTransition.
+ decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion);
+ } else {
+ decoration.relayout(taskInfo, taskInfo.isFocused, decoration.mExclusionRegion);
+ }
}
@Override
@@ -233,7 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* setTaskCropAndPosition */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo));
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion);
}
}
@@ -247,7 +256,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* setTaskCropAndPosition */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo));
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion);
}
@Override
@@ -259,6 +268,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
decoration.close();
}
+ private void onExclusionRegionChanged(int displayId, @NonNull Region exclusionRegion) {
+ final int decorCount = mWindowDecorByTaskId.size();
+ for (int i = 0; i < decorCount; i++) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i);
+ if (decoration.mTaskInfo.displayId != displayId) continue;
+ decoration.onExclusionRegionChanged(exclusionRegion);
+ }
+ }
+
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return true;
@@ -326,7 +344,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
windowDecoration.setTaskDragResizer(taskPositioner);
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo));
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion);
}
private class CaptionTouchEventListener implements
@@ -496,4 +514,4 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
return Settings.Global.getInt(resolver,
DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;
}
-} \ No newline at end of file
+}
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 c9546731a193..982fda0ddf36 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
@@ -36,6 +36,7 @@ import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.util.Size;
@@ -174,7 +175,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
}
@Override
- void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
+ void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
// The crop and position of the task should only be set when a task is fluid resizing. In
// all other cases, it is expected that the transition handler positions and crops the task
@@ -186,7 +188,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
// synced with the buffer transaction (that draws the View). Both will be shown on screen
// at the same, whereas applying them independently causes flickering. See b/270202228.
relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
}
@VisibleForTesting
@@ -198,7 +200,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
boolean isStatusBarVisible,
boolean isKeyguardVisibleAndOccluded,
InsetsState displayInsetsState,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus,
+ @NonNull Region globalExclusionRegion) {
relayoutParams.reset();
relayoutParams.mRunningTaskInfo = taskInfo;
relayoutParams.mLayoutResId = R.layout.caption_window_decor;
@@ -210,6 +213,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
|| (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
+ relayoutParams.mDisplayExclusionRegion.set(globalExclusionRegion);
if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
// If the app is requesting to customize the caption bar, allow input to fall
@@ -236,7 +240,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
void relayout(RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus,
+ @NonNull Region globalExclusionRegion) {
final boolean isFreeform =
taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
final boolean isDragResizeable = ENABLE_WINDOWING_SCALED_RESIZING.isTrue()
@@ -249,7 +254,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw,
shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
mIsKeyguardVisibleAndOccluded,
- mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
+ mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
+ globalExclusionRegion);
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 67775f700be4..d71e61a4c4de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -31,6 +31,7 @@ import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
@@ -41,7 +42,6 @@ import static com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -49,6 +49,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
@@ -103,6 +104,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
@@ -121,8 +123,6 @@ import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -220,6 +220,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
mMainExecutor.execute(() -> {
mExclusionRegion.set(systemGestureExclusion);
+ onExclusionRegionChanged(displayId, mExclusionRegion);
});
}
};
@@ -432,7 +433,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
boolean isFocusedGlobally) {
final WindowDecoration decor = mWindowDecorByTaskId.get(taskId);
if (decor != null) {
- decor.relayout(decor.mTaskInfo, isFocusedGlobally);
+ decor.relayout(decor.mTaskInfo, isFocusedGlobally, decor.mExclusionRegion);
}
}
@@ -445,17 +446,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
@Override
public void setSplitScreenController(SplitScreenController splitScreenController) {
mSplitScreenController = splitScreenController;
- mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() {
- @Override
- public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
- if (visible && stage != STAGE_TYPE_UNDEFINED) {
- DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
- if (decor != null && DesktopModeStatus.canEnterDesktopMode(mContext)) {
- mDesktopTasksController.moveToSplit(decor.mTaskInfo);
- }
- }
- }
- });
}
@Override
@@ -476,11 +466,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
final RunningTaskInfo oldTaskInfo = decoration.mTaskInfo;
if (taskInfo.displayId != oldTaskInfo.displayId
- && !Flags.enableHandleInputFix()) {
+ && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
- decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
+ if (enableDisplayFocusInShellTransitions()) {
+ // Pass the current global focus status to avoid updates outside of a ShellTransition.
+ decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion);
+ } else {
+ decoration.relayout(taskInfo, taskInfo.isFocused, decoration.mExclusionRegion);
+ }
mActivityOrientationChangeHandler.ifPresent(handler ->
handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
}
@@ -520,7 +515,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo));
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo),
+ mExclusionRegion);
}
}
@@ -534,7 +530,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo));
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo),
+ mExclusionRegion);
}
@Override
@@ -545,7 +542,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.close();
final int displayId = taskInfo.displayId;
if (mEventReceiversByDisplay.contains(displayId)
- && !Flags.enableHandleInputFix()) {
+ && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
removeTaskFromEventReceiver(displayId);
}
// Remove the decoration from the cache last because WindowDecoration#close could still
@@ -554,6 +551,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mWindowDecorByTaskId.remove(taskInfo.taskId);
}
+ private void onExclusionRegionChanged(int displayId, @NonNull Region exclusionRegion) {
+ final int decorCount = mWindowDecorByTaskId.size();
+ for (int i = 0; i < decorCount; i++) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i);
+ if (decoration.mTaskInfo.displayId != displayId) continue;
+ decoration.onExclusionRegionChanged(exclusionRegion);
+ }
+ }
+
private void openHandleMenu(int taskId) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
decoration.createHandleMenu(checkNumberOfOtherInstances(decoration.mTaskInfo)
@@ -756,10 +762,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
/**
* Whether to pilfer the next motion event to send cancellations to the windows below.
- * Useful when the caption window is spy and the gesture should be handle by the system
+ * Useful when the caption window is spy and the gesture should be handled by the system
* instead of by the app for their custom header content.
+ * Should not have any effect when {@link Flags#enableAccessibleCustomHeaders()}, because
+ * a spy window is not used then.
*/
- private boolean mShouldPilferCaptionEvents;
+ private boolean mIsCustomHeaderGesture;
+ private boolean mIsResizeGesture;
private boolean mIsDragging;
private boolean mTouchscreenInUse;
private boolean mHasLongClicked;
@@ -773,7 +782,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mTaskToken = taskInfo.token;
mDragPositioningCallback = dragPositioningCallback;
final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- final long appHandleHoldToDragDuration = Flags.enableHoldToDragAppHandle()
+ final long appHandleHoldToDragDuration =
+ DesktopModeFlags.ENABLE_HOLD_TO_DRAG_APP_HANDLE.isTrue()
? APP_HANDLE_HOLD_TO_DRAG_DURATION_MS : 0;
mHandleDragDetector = new DragDetector(this, appHandleHoldToDragDuration,
touchSlop);
@@ -873,7 +883,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// to offset position relative to caption as a whole.
int[] viewLocation = new int[2];
v.getLocationInWindow(viewLocation);
- final boolean isResizeEvent = decoration.shouldResizeListenerHandleEvent(e,
+ mIsResizeGesture = decoration.shouldResizeListenerHandleEvent(e,
new Point(viewLocation[0], viewLocation[1]));
// The caption window may be a spy window when the caption background is
// transparent, which means events will fall through to the app window. Make
@@ -881,21 +891,23 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// customizable region and what the app reported as exclusion areas, because
// the drag-move or other caption gestures should take priority outside those
// regions.
- mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion
- && downInExclusionRegion && isTransparentCaption) && !isResizeEvent;
+ mIsCustomHeaderGesture = downInCustomizableCaptionRegion
+ && downInExclusionRegion && isTransparentCaption;
}
- if (!mShouldPilferCaptionEvents) {
- // The event will be handled by a window below or pilfered by resize handler.
+ if (mIsCustomHeaderGesture || mIsResizeGesture) {
+ // The event will be handled by the custom window below or pilfered by resize
+ // handler.
return false;
}
- // Otherwise pilfer so that windows below receive cancellations for this gesture, and
- // continue normal handling as a caption gesture.
- if (mInputManager != null) {
+ if (mInputManager != null
+ && !Flags.enableAccessibleCustomHeaders()) {
+ // Pilfer so that windows below receive cancellations for this gesture.
mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
}
if (isUpOrCancel) {
// Gesture is finished, reset state.
- mShouldPilferCaptionEvents = false;
+ mIsCustomHeaderGesture = false;
+ mIsResizeGesture = false;
}
if (isAppHandle) {
return mHandleDragDetector.onMotionEvent(v, e);
@@ -1240,7 +1252,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
relevantDecor.updateHoverAndPressStatus(ev);
final int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- if (!mTransitionDragActive && !Flags.enableHandleInputFix()) {
+ if (!mTransitionDragActive && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
relevantDecor.closeHandleMenuIfNeeded(ev);
}
}
@@ -1283,12 +1295,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
final boolean shouldStartTransitionDrag =
relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)
- || Flags.enableHandleInputFix();
+ || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue();
if (dragFromStatusBarAllowed && shouldStartTransitionDrag) {
mTransitionDragActive = true;
}
break;
}
+ case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (mTransitionDragActive) {
final DesktopModeVisualIndicator.DragStartState dragStartState =
@@ -1303,32 +1316,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// Though this isn't a hover event, we need to update handle's hover state
// as it likely will change.
relevantDecor.updateHoverAndPressStatus(ev);
- DesktopModeVisualIndicator.IndicatorType resultType =
- mDesktopTasksController.onDragPositioningEndThroughStatusBar(
- new PointF(ev.getRawX(), ev.getRawY()),
- relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface);
- // If we are entering split select, handle will no longer be visible and
- // should not be receiving any input.
- if (resultType == TO_SPLIT_LEFT_INDICATOR
- || resultType == TO_SPLIT_RIGHT_INDICATOR) {
- relevantDecor.disposeStatusBarInputLayer();
- // We should also dispose the other split task's input layer if
- // applicable.
- final int splitPosition = mSplitScreenController
- .getSplitPosition(relevantDecor.mTaskInfo.taskId);
- if (splitPosition != SPLIT_POSITION_UNDEFINED) {
- final int oppositePosition =
- splitPosition == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT
- : SPLIT_POSITION_TOP_OR_LEFT;
- final RunningTaskInfo oppositeTaskInfo =
- mSplitScreenController.getTaskInfo(oppositePosition);
- if (oppositeTaskInfo != null) {
- mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
- .disposeStatusBarInputLayer();
- }
- }
+ if (ev.getActionMasked() == ACTION_CANCEL) {
+ mDesktopTasksController.onDragPositioningCancelThroughStatusBar(
+ relevantDecor.mTaskInfo);
+ } else {
+ endDragToDesktop(ev, relevantDecor);
}
mMoveToDesktopAnimator = null;
return;
@@ -1377,10 +1369,35 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
break;
}
+ }
+ }
- case MotionEvent.ACTION_CANCEL: {
- mTransitionDragActive = false;
- mMoveToDesktopAnimator = null;
+ private void endDragToDesktop(MotionEvent ev, DesktopModeWindowDecoration relevantDecor) {
+ DesktopModeVisualIndicator.IndicatorType resultType =
+ mDesktopTasksController.onDragPositioningEndThroughStatusBar(
+ new PointF(ev.getRawX(), ev.getRawY()),
+ relevantDecor.mTaskInfo,
+ relevantDecor.mTaskSurface);
+ // If we are entering split select, handle will no longer be visible and
+ // should not be receiving any input.
+ if (resultType == TO_SPLIT_LEFT_INDICATOR
+ || resultType == TO_SPLIT_RIGHT_INDICATOR) {
+ relevantDecor.disposeStatusBarInputLayer();
+ // We should also dispose the other split task's input layer if
+ // applicable.
+ final int splitPosition = mSplitScreenController
+ .getSplitPosition(relevantDecor.mTaskInfo.taskId);
+ if (splitPosition != SPLIT_POSITION_UNDEFINED) {
+ final int oppositePosition =
+ splitPosition == SPLIT_POSITION_TOP_OR_LEFT
+ ? SPLIT_POSITION_BOTTOM_OR_RIGHT
+ : SPLIT_POSITION_TOP_OR_LEFT;
+ final RunningTaskInfo oppositeTaskInfo =
+ mSplitScreenController.getTaskInfo(oppositePosition);
+ if (oppositeTaskInfo != null) {
+ mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
+ .disposeStatusBarInputLayer();
+ }
}
}
}
@@ -1480,6 +1497,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
&& isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) {
return false;
}
+ if (isPartOfDefaultHomePackage(taskInfo)) {
+ return false;
+ }
return DesktopModeStatus.canEnterDesktopMode(mContext)
&& !DesktopWallpaperActivity.isWallpaperTask(taskInfo)
&& taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
@@ -1487,6 +1507,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
&& !taskInfo.configuration.windowConfiguration.isAlwaysOnTop();
}
+ private boolean isPartOfDefaultHomePackage(RunningTaskInfo taskInfo) {
+ final ComponentName currentDefaultHome =
+ mContext.getPackageManager().getHomeActivities(new ArrayList<>());
+ return currentDefaultHome != null && taskInfo.baseActivity != null
+ && currentDefaultHome.getPackageName()
+ .equals(taskInfo.baseActivity.getPackageName());
+ }
+
private void createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -1572,14 +1600,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
onManageWindows(windowDecoration);
return Unit.INSTANCE;
});
+ windowDecoration.setOnChangeAspectRatioClickListener(() -> {
+ CompatUIController.launchUserAspectRatioSettings(mContext, taskInfo);
+ return Unit.INSTANCE;
+ });
windowDecoration.setCaptionListeners(
touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo));
- if (!Flags.enableHandleInputFix()) {
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo),
+ mExclusionRegion);
+ if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
incrementEventReceiverTasks(taskInfo.displayId);
}
}
@@ -1604,6 +1637,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
pw.println(innerPrefix + "mTransitionDragActive=" + mTransitionDragActive);
pw.println(innerPrefix + "mEventReceiversByDisplay=" + mEventReceiversByDisplay);
pw.println(innerPrefix + "mWindowDecorByTaskId=" + mWindowDecorByTaskId);
+ pw.println(innerPrefix + "mExclusionRegion=" + mExclusionRegion);
}
private class DesktopModeOnTaskRepositionAnimationListener
@@ -1740,7 +1774,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
&& Flags.enableDesktopWindowingImmersiveHandleHiding()) {
decor.onInsetsStateChanged(insetsState);
}
- if (!Flags.enableHandleInputFix()) {
+ if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
// If status bar inset is visible, top task is not in immersive mode.
// This value is only needed when the App Handle input is being handled
// through the global input monitor (hence the flag check) to ignore gestures
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 af7cd0584c17..cdcf14e0cbf3 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
@@ -154,6 +154,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private Function0<Unit> mOnToSplitscreenClickListener;
private Function0<Unit> mOnNewWindowClickListener;
private Function0<Unit> mOnManageWindowsClickListener;
+ private Function0<Unit> mOnChangeAspectRatioClickListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private Runnable mCurrentViewHostRunnable = null;
@@ -364,6 +365,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnManageWindowsClickListener = listener;
}
+ /** Registers a listener to be called when the aspect ratio action is triggered. */
+ void setOnChangeAspectRatioClickListener(Function0<Unit> listener) {
+ mOnChangeAspectRatioClickListener = listener;
+ }
+
void setCaptionListeners(
View.OnClickListener onCaptionButtonClickListener,
View.OnTouchListener onCaptionTouchListener,
@@ -388,7 +394,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
@Override
- void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
+ void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
// The visibility, crop and position of the task should only be set when a task is
// fluid resizing. In all other cases, it is expected that the transition handler sets
@@ -409,7 +416,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// causes flickering. See b/270202228.
final boolean applyTransactionOnDraw = taskInfo.isFreeform();
relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus);
+ hasGlobalFocus, displayExclusionRegion);
if (!applyTransactionOnDraw) {
t.apply();
}
@@ -436,18 +443,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
if (taskInfo.isFreeform()) {
// The Task is in Freeform mode -> show its header in sync since it's an integral part
// of the window itself - a delayed header might cause bad UX.
relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
} else {
// The Task is outside Freeform mode -> allow the handle view to be delayed since the
// handle is just a small addition to the window.
relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
}
Trace.endSection();
}
@@ -456,11 +463,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
// Clear the current ViewHost runnable as we will update the ViewHost here
clearCurrentViewHostRunnable();
updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
- shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
+ shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion);
if (mResult.mRootView != null) {
updateViewHost(mRelayoutParams, startT, mResult);
}
@@ -483,7 +490,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
if (applyStartTransactionOnDraw) {
throw new IllegalArgumentException(
"We cannot both sync viewhost ondraw and delay viewhost creation.");
@@ -492,7 +500,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
clearCurrentViewHostRunnable();
updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus);
+ hasGlobalFocus, displayExclusionRegion);
if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
// Nothing is set up in this case including the decoration surface.
@@ -507,7 +515,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
if (Flags.enableDesktopWindowingAppToWeb()) {
setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
@@ -532,7 +540,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
mIsKeyguardVisibleAndOccluded, inFullImmersive,
- mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
+ mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
+ displayExclusionRegion);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -622,13 +631,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@Nullable
private Intent getBrowserLink() {
- // Do not show browser link in browser applications
- final ComponentName baseActivity = mTaskInfo.baseActivity;
- if (baseActivity != null && AppToWebUtils.isBrowserApp(mContext,
- baseActivity.getPackageName(), mUserContext.getUserId())) {
- return null;
- }
-
final Uri browserLink;
// If the captured link is available and has not expired, return the captured link.
// Otherwise, return the generic link which is set to null if a generic link is unavailable.
@@ -645,6 +647,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
+ @Nullable
+ private Intent getAppLink() {
+ return mWebUri == null ? null
+ : AppToWebUtils.getAppIntent(mWebUri, mContext.getPackageManager());
+ }
+
+ private boolean isBrowserApp() {
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+ return baseActivity != null && AppToWebUtils.isBrowserApp(mContext,
+ baseActivity.getPackageName(), mUserContext.getUserId());
+ }
+
UserHandle getUser() {
return mUserContext.getUser();
}
@@ -801,7 +815,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void disposeStatusBarInputLayer() {
if (!isAppHandle(mWindowDecorViewHolder)
- || !Flags.enableHandleInputFix()) {
+ || !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
return;
}
asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
@@ -868,7 +882,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean isKeyguardVisibleAndOccluded,
boolean inFullImmersiveMode,
@NonNull InsetsState displayInsetsState,
- boolean hasGlobalFocus) {
+ boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -879,6 +894,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
relayoutParams.mHasGlobalFocus = hasGlobalFocus;
+ relayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
final boolean showCaption;
if (Flags.enableFullyImmersiveInDesktop()) {
@@ -904,10 +920,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mIsInsetSource = isAppHeader && !inFullImmersiveMode;
if (isAppHeader) {
if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
- // If the app is requesting to customize the caption bar, allow input to fall
- // through to the windows below so that the app can respond to input events on
- // their custom content.
- relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ // The app is requesting to customize the caption bar, which means input on
+ // customizable/exclusion regions must go to the app instead of to the system.
+ // This may be accomplished with spy windows or custom touchable regions:
+ if (Flags.enableAccessibleCustomHeaders()) {
+ // Set the touchable region of the caption to only the areas where input should
+ // be handled by the system (i.e. non custom-excluded areas). The region will
+ // be calculated based on occluding caption elements and exclusion areas
+ // reported by the app.
+ relayoutParams.mLimitTouchRegionToSystemAreas = true;
+ } else {
+ // Allow input to fall through to the windows below so that the app can respond
+ // to input events on their custom content.
+ relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ }
} else {
if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) {
// Force-consume the caption bar insets when the app tries to hide the caption.
@@ -945,7 +971,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
relayoutParams.mOccludingCaptionElements.add(controlsElement);
- } else if (isAppHandle && !Flags.enableHandleInputFix()) {
+ } else if (isAppHandle && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
// The focused decor (fullscreen/split) does not need to handle input because input in
// the App Handle is handled by the InputMonitor in DesktopModeWindowDecorViewModel.
// Note: This does not apply with the above flag enabled as the status bar input layer
@@ -1358,8 +1384,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
&& Flags.enableDesktopWindowingMultiInstanceFeatures();
final boolean shouldShowManageWindowsButton = supportsMultiInstance
&& mMinimumInstancesFound;
+ final boolean shouldShowChangeAspectRatioButton = HandleMenu.Companion
+ .shouldShowChangeAspectRatioButton(mTaskInfo);
final boolean inDesktopImmersive = mDesktopRepository
.isTaskInFullImmersiveState(mTaskInfo.taskId);
+ final boolean isBrowserApp = isBrowserApp();
mHandleMenu = mHandleMenuFactory.create(
this,
mWindowManagerWrapper,
@@ -1370,7 +1399,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
canEnterDesktopMode(mContext),
supportsMultiInstance,
shouldShowManageWindowsButton,
- getBrowserLink(),
+ shouldShowChangeAspectRatioButton,
+ isBrowserApp,
+ isBrowserApp ? getAppLink() : getBrowserLink(),
mResult.mCaptionWidth,
mResult.mCaptionHeight,
mResult.mCaptionX,
@@ -1390,6 +1421,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
/* onToSplitScreenClickListener= */ mOnToSplitscreenClickListener,
/* onNewWindowClickListener= */ mOnNewWindowClickListener,
/* onManageWindowsClickListener= */ mOnManageWindowsClickListener,
+ /* onAspectRatioSettingsClickListener= */ mOnChangeAspectRatioClickListener,
/* openInBrowserClickListener= */ (intent) -> {
mOpenInBrowserClickListener.accept(intent);
onCapturedLinkExpired();
@@ -1550,13 +1582,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) {
if (isHandleMenuActive() || !isAppHandle(mWindowDecorViewHolder)
- || Flags.enableHandleInputFix()) {
+ || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
return false;
}
// The status bar input layer can only receive input in handle coordinates to begin with,
// so checking coordinates is unnecessary as input is always within handle bounds.
if (isAppHandle(mWindowDecorViewHolder)
- && Flags.enableHandleInputFix()
+ && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()
&& isCaptionVisible()) {
return true;
}
@@ -1593,7 +1625,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @param ev the MotionEvent to compare
*/
void checkTouchEvent(MotionEvent ev) {
- if (mResult.mRootView == null || Flags.enableHandleInputFix()) return;
+ if (mResult.mRootView == null || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) return;
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final View handle = caption.findViewById(R.id.caption_handle);
final boolean inHandle = !isHandleMenuActive()
@@ -1606,7 +1638,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// If the whole handle menu can be touched directly, rely on FLAG_WATCH_OUTSIDE_TOUCH.
// This is for the case that some of the handle menu is underneath the status bar.
if (isAppHandle(mWindowDecorViewHolder)
- && !Flags.enableHandleInputFix()) {
+ && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
mHandleMenu.checkMotionEvent(ev);
closeHandleMenuIfNeeded(ev);
}
@@ -1620,7 +1652,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @param ev the MotionEvent to compare against.
*/
void updateHoverAndPressStatus(MotionEvent ev) {
- if (mResult.mRootView == null || Flags.enableHandleInputFix()) return;
+ if (mResult.mRootView == null || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) return;
final View handle = mResult.mRootView.findViewById(R.id.caption_handle);
final boolean inHandle = !isHandleMenuActive()
&& checkTouchEventInFocusedCaptionHandle(ev);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 93bd9290dfeb..54c247bff984 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -19,6 +19,7 @@ import android.annotation.ColorInt
import android.annotation.DimenRes
import android.annotation.SuppressLint
import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
@@ -38,12 +39,14 @@ import android.widget.Button
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
+import android.window.DesktopModeFlags
import android.window.SurfaceSyncGroup
+import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import androidx.compose.ui.graphics.toArgb
import androidx.core.view.isGone
-import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.apptoweb.isBrowserApp
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -71,7 +74,9 @@ class HandleMenu(
private val shouldShowWindowingPill: Boolean,
private val shouldShowNewWindowButton: Boolean,
private val shouldShowManageWindowsButton: Boolean,
- private val openInBrowserIntent: Intent?,
+ private val shouldShowChangeAspectRatioButton: Boolean,
+ private val isBrowserApp: Boolean,
+ private val openInAppOrBrowserIntent: Intent?,
private val captionWidth: Int,
private val captionHeight: Int,
captionX: Int,
@@ -81,7 +86,7 @@ class HandleMenu(
private val taskInfo: RunningTaskInfo = parentDecor.mTaskInfo
private val isViewAboveStatusBar: Boolean
- get() = (Flags.enableHandleInputFix() && !taskInfo.isFreeform)
+ get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform)
private val pillElevation: Int = loadDimensionPixelSize(
R.dimen.desktop_mode_handle_menu_pill_elevation)
@@ -109,7 +114,11 @@ class HandleMenu(
private val globalMenuPosition: Point = Point()
private val shouldShowBrowserPill: Boolean
- get() = openInBrowserIntent != null
+ get() = openInAppOrBrowserIntent != null
+
+ private val shouldShowMoreActionsPill: Boolean
+ get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
+ shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
init {
updateHandleMenuPillPositions(captionX, captionY)
@@ -121,7 +130,8 @@ class HandleMenu(
onToSplitScreenClickListener: () -> Unit,
onNewWindowClickListener: () -> Unit,
onManageWindowsClickListener: () -> Unit,
- openInBrowserClickListener: (Intent) -> Unit,
+ onChangeAspectRatioClickListener: () -> Unit,
+ openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
@@ -138,7 +148,8 @@ class HandleMenu(
onToSplitScreenClickListener = onToSplitScreenClickListener,
onNewWindowClickListener = onNewWindowClickListener,
onManageWindowsClickListener = onManageWindowsClickListener,
- openInBrowserClickListener = openInBrowserClickListener,
+ onChangeAspectRatioClickListener = onChangeAspectRatioClickListener,
+ openInAppOrBrowserClickListener = openInAppOrBrowserClickListener,
onOpenByDefaultClickListener = onOpenByDefaultClickListener,
onCloseMenuClickListener = onCloseMenuClickListener,
onOutsideTouchListener = onOutsideTouchListener,
@@ -158,7 +169,8 @@ class HandleMenu(
onToSplitScreenClickListener: () -> Unit,
onNewWindowClickListener: () -> Unit,
onManageWindowsClickListener: () -> Unit,
- openInBrowserClickListener: (Intent) -> Unit,
+ onChangeAspectRatioClickListener: () -> Unit,
+ openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
@@ -171,16 +183,19 @@ class HandleMenu(
shouldShowWindowingPill = shouldShowWindowingPill,
shouldShowBrowserPill = shouldShowBrowserPill,
shouldShowNewWindowButton = shouldShowNewWindowButton,
- shouldShowManageWindowsButton = shouldShowManageWindowsButton
+ shouldShowManageWindowsButton = shouldShowManageWindowsButton,
+ shouldShowChangeAspectRatioButton = shouldShowChangeAspectRatioButton,
+ isBrowserApp = isBrowserApp
).apply {
- bind(taskInfo, appIconBitmap, appName)
+ bind(taskInfo, appIconBitmap, appName, shouldShowMoreActionsPill)
this.onToDesktopClickListener = onToDesktopClickListener
this.onToFullscreenClickListener = onToFullscreenClickListener
this.onToSplitScreenClickListener = onToSplitScreenClickListener
this.onNewWindowClickListener = onNewWindowClickListener
this.onManageWindowsClickListener = onManageWindowsClickListener
- this.onOpenInBrowserClickListener = {
- openInBrowserClickListener.invoke(openInBrowserIntent!!)
+ this.onChangeAspectRatioClickListener = onChangeAspectRatioClickListener
+ this.onOpenInAppOrBrowserClickListener = {
+ openInAppOrBrowserClickListener.invoke(openInAppOrBrowserIntent!!)
}
this.onOpenByDefaultClickListener = onOpenByDefaultClickListener
this.onCloseMenuClickListener = onCloseMenuClickListener
@@ -190,7 +205,8 @@ class HandleMenu(
val x = handleMenuPosition.x.toInt()
val y = handleMenuPosition.y.toInt()
handleMenuViewContainer =
- if ((!taskInfo.isFreeform && Flags.enableHandleInputFix()) || forceShowSystemBars) {
+ if ((!taskInfo.isFreeform && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue())
+ || forceShowSystemBars) {
AdditionalSystemViewContainer(
windowManagerWrapper = windowManagerWrapper,
taskId = taskInfo.taskId,
@@ -226,7 +242,7 @@ class HandleMenu(
menuX = marginMenuStart
menuY = captionY + marginMenuTop
} else {
- if (Flags.enableHandleInputFix()) {
+ if (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
// In a focused decor, we use global coordinates for handle menu. Therefore we
// need to account for other factors like split stage and menu/handle width to
// center the menu.
@@ -392,8 +408,11 @@ class HandleMenu(
R.dimen.desktop_mode_handle_menu_manage_windows_height
)
}
- if (!SHOULD_SHOW_SCREENSHOT_BUTTON && !shouldShowNewWindowButton
- && !shouldShowManageWindowsButton) {
+ if (!shouldShowChangeAspectRatioButton) {
+ menuHeight -= loadDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height)
+ }
+ if (!shouldShowMoreActionsPill) {
menuHeight -= pillTopMargin
}
if (!shouldShowBrowserPill) {
@@ -421,13 +440,15 @@ class HandleMenu(
/** The view within the Handle Menu, with options to change the windowing mode and more. */
@SuppressLint("ClickableViewAccessibility")
class HandleMenuView(
- context: Context,
+ private val context: Context,
menuWidth: Int,
captionHeight: Int,
private val shouldShowWindowingPill: Boolean,
private val shouldShowBrowserPill: Boolean,
private val shouldShowNewWindowButton: Boolean,
- private val shouldShowManageWindowsButton: Boolean
+ private val shouldShowManageWindowsButton: Boolean,
+ private val shouldShowChangeAspectRatioButton: Boolean,
+ private val isBrowserApp: Boolean
) {
val rootView = LayoutInflater.from(context)
.inflate(R.layout.desktop_mode_window_decor_handle_menu, null /* root */) as View
@@ -454,12 +475,15 @@ class HandleMenu(
private val newWindowBtn = moreActionsPill.requireViewById<Button>(R.id.new_window_button)
private val manageWindowBtn = moreActionsPill
.requireViewById<Button>(R.id.manage_windows_button)
-
- // Open in Browser Pill.
- private val openInBrowserPill = rootView.requireViewById<View>(R.id.open_in_browser_pill)
- private val browserBtn = openInBrowserPill.requireViewById<Button>(
- R.id.open_in_browser_button)
- private val openByDefaultBtn = openInBrowserPill.requireViewById<ImageButton>(
+ private val changeAspectRatioBtn = moreActionsPill
+ .requireViewById<Button>(R.id.change_aspect_ratio_button)
+
+ // Open in Browser/App Pill.
+ private val openInAppOrBrowserPill = rootView.requireViewById<View>(
+ R.id.open_in_app_or_browser_pill)
+ private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<Button>(
+ R.id.open_in_app_or_browser_button)
+ private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>(
R.id.open_by_default_button)
private val decorThemeUtil = DecorThemeUtil(context)
private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat())
@@ -472,7 +496,8 @@ class HandleMenu(
var onToSplitScreenClickListener: (() -> Unit)? = null
var onNewWindowClickListener: (() -> Unit)? = null
var onManageWindowsClickListener: (() -> Unit)? = null
- var onOpenInBrowserClickListener: (() -> Unit)? = null
+ var onChangeAspectRatioClickListener: (() -> Unit)? = null
+ var onOpenInAppOrBrowserClickListener: (() -> Unit)? = null
var onOpenByDefaultClickListener: (() -> Unit)? = null
var onCloseMenuClickListener: (() -> Unit)? = null
var onOutsideTouchListener: (() -> Unit)? = null
@@ -481,13 +506,14 @@ class HandleMenu(
fullscreenBtn.setOnClickListener { onToFullscreenClickListener?.invoke() }
splitscreenBtn.setOnClickListener { onToSplitScreenClickListener?.invoke() }
desktopBtn.setOnClickListener { onToDesktopClickListener?.invoke() }
- browserBtn.setOnClickListener { onOpenInBrowserClickListener?.invoke() }
+ openInAppOrBrowserBtn.setOnClickListener { onOpenInAppOrBrowserClickListener?.invoke() }
openByDefaultBtn.setOnClickListener {
onOpenByDefaultClickListener?.invoke()
}
collapseMenuButton.setOnClickListener { onCloseMenuClickListener?.invoke() }
newWindowBtn.setOnClickListener { onNewWindowClickListener?.invoke() }
manageWindowBtn.setOnClickListener { onManageWindowsClickListener?.invoke() }
+ changeAspectRatioBtn.setOnClickListener { onChangeAspectRatioClickListener?.invoke() }
rootView.setOnTouchListener { _, event ->
if (event.actionMasked == ACTION_OUTSIDE) {
@@ -499,7 +525,12 @@ class HandleMenu(
}
/** Binds the menu views to the new data. */
- fun bind(taskInfo: RunningTaskInfo, appIconBitmap: Bitmap?, appName: CharSequence?) {
+ fun bind(
+ taskInfo: RunningTaskInfo,
+ appIconBitmap: Bitmap?,
+ appName: CharSequence?,
+ shouldShowMoreActionsPill: Boolean
+ ) {
this.taskInfo = taskInfo
this.style = calculateMenuStyle(taskInfo)
@@ -507,11 +538,14 @@ class HandleMenu(
if (shouldShowWindowingPill) {
bindWindowingPill(style)
}
- bindMoreActionsPill(style)
- bindOpenInBrowserPill(style)
+ moreActionsPill.isGone = !shouldShowMoreActionsPill
+ if (shouldShowMoreActionsPill) {
+ bindMoreActionsPill(style)
+ }
+ bindOpenInAppOrBrowserPill(style)
}
- /** Animates the menu opening. */
+ /** Animates the menu openInAppOrBrowserg. */
fun animateOpenMenu() {
if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
animator.animateCaptionHandleExpandToOpen()
@@ -616,37 +650,37 @@ class HandleMenu(
}
private fun bindMoreActionsPill(style: MenuStyle) {
- moreActionsPill.apply {
- isGone = !shouldShowNewWindowButton && !SHOULD_SHOW_SCREENSHOT_BUTTON
- && !shouldShowManageWindowsButton
- }
- screenshotBtn.apply {
- isGone = !SHOULD_SHOW_SCREENSHOT_BUTTON
- background.setTint(style.backgroundColor)
- setTextColor(style.textColor)
- compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
- }
- newWindowBtn.apply {
- isGone = !shouldShowNewWindowButton
- background.setTint(style.backgroundColor)
- setTextColor(style.textColor)
- compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
- }
- manageWindowBtn.apply {
- isGone = !shouldShowManageWindowsButton
- background.setTint(style.backgroundColor)
- setTextColor(style.textColor)
- compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+ arrayOf(
+ screenshotBtn to SHOULD_SHOW_SCREENSHOT_BUTTON,
+ newWindowBtn to shouldShowNewWindowButton,
+ manageWindowBtn to shouldShowManageWindowsButton,
+ changeAspectRatioBtn to shouldShowChangeAspectRatioButton,
+ ).forEach {
+ val button = it.first
+ val shouldShow = it.second
+ button.apply {
+ isGone = !shouldShow
+ background.setTint(style.backgroundColor)
+ setTextColor(style.textColor)
+ compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+ }
}
}
- private fun bindOpenInBrowserPill(style: MenuStyle) {
- openInBrowserPill.apply {
+ private fun bindOpenInAppOrBrowserPill(style: MenuStyle) {
+ openInAppOrBrowserPill.apply {
isGone = !shouldShowBrowserPill
background.setTint(style.backgroundColor)
}
- browserBtn.apply {
+ val btnText = if (isBrowserApp) {
+ getString(R.string.open_in_app_text)
+ } else {
+ getString(R.string.open_in_browser_text)
+ }
+ openInAppOrBrowserBtn.apply {
+ text = btnText
+ contentDescription = btnText
setTextColor(style.textColor)
compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
}
@@ -654,6 +688,8 @@ class HandleMenu(
openByDefaultBtn.imageTintList = ColorStateList.valueOf(style.textColor)
}
+ private fun getString(@StringRes resId: Int): String = context.resources.getString(resId)
+
private data class MenuStyle(
@ColorInt val backgroundColor: Int,
@ColorInt val textColor: Int,
@@ -664,6 +700,14 @@ class HandleMenu(
companion object {
private const val TAG = "HandleMenu"
private const val SHOULD_SHOW_SCREENSHOT_BUTTON = false
+
+ /**
+ * Returns whether the aspect ratio button should be shown for the task. It usually means
+ * that the task is on a large screen with ignore-orientation-request.
+ */
+ fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
+ taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
+ taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
}
}
@@ -679,7 +723,9 @@ interface HandleMenuFactory {
shouldShowWindowingPill: Boolean,
shouldShowNewWindowButton: Boolean,
shouldShowManageWindowsButton: Boolean,
- openInBrowserIntent: Intent?,
+ shouldShowChangeAspectRatioButton: Boolean,
+ isBrowserApp: Boolean,
+ openInAppOrBrowserIntent: Intent?,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -699,7 +745,9 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowWindowingPill: Boolean,
shouldShowNewWindowButton: Boolean,
shouldShowManageWindowsButton: Boolean,
- openInBrowserIntent: Intent?,
+ shouldShowChangeAspectRatioButton: Boolean,
+ isBrowserApp: Boolean,
+ openInAppOrBrowserIntent: Intent?,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -715,7 +763,9 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowWindowingPill,
shouldShowNewWindowButton,
shouldShowManageWindowsButton,
- openInBrowserIntent,
+ shouldShowChangeAspectRatioButton,
+ isBrowserApp,
+ openInAppOrBrowserIntent,
captionWidth,
captionHeight,
captionX,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
index 0c475f12f53b..470e5a1d88b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
@@ -74,7 +74,8 @@ class HandleMenuAnimator(
private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill)
private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill)
private val moreActionsPill: ViewGroup = handleMenu.requireViewById(R.id.more_actions_pill)
- private val openInBrowserPill: ViewGroup = handleMenu.requireViewById(R.id.open_in_browser_pill)
+ private val openInAppOrBrowserPill: ViewGroup =
+ handleMenu.requireViewById(R.id.open_in_app_or_browser_pill)
/** Animates the opening of the handle menu. */
fun animateOpen() {
@@ -83,7 +84,7 @@ class HandleMenuAnimator(
animateAppInfoPillOpen()
animateWindowingPillOpen()
animateMoreActionsPillOpen()
- animateOpenInBrowserPill()
+ animateOpenInAppOrBrowserPill()
runAnimations {
appInfoPill.post {
appInfoPill.requireViewById<View>(R.id.collapse_menu_button).sendAccessibilityEvent(
@@ -103,7 +104,7 @@ class HandleMenuAnimator(
animateAppInfoPillOpen()
animateWindowingPillOpen()
animateMoreActionsPillOpen()
- animateOpenInBrowserPill()
+ animateOpenInAppOrBrowserPill()
runAnimations {
appInfoPill.post {
appInfoPill.requireViewById<View>(R.id.collapse_menu_button).sendAccessibilityEvent(
@@ -124,7 +125,7 @@ class HandleMenuAnimator(
animateAppInfoPillFadeOut()
windowingPillClose()
moreActionsPillClose()
- openInBrowserPillClose()
+ openInAppOrBrowserPillClose()
runAnimations(after)
}
@@ -141,7 +142,7 @@ class HandleMenuAnimator(
animateAppInfoPillFadeOut()
windowingPillClose()
moreActionsPillClose()
- openInBrowserPillClose()
+ openInAppOrBrowserPillClose()
runAnimations(after)
}
@@ -154,7 +155,7 @@ class HandleMenuAnimator(
appInfoPill.children.forEach { it.alpha = 0f }
windowingPill.alpha = 0f
moreActionsPill.alpha = 0f
- openInBrowserPill.alpha = 0f
+ openInAppOrBrowserPill.alpha = 0f
// Setup pivots.
handleMenu.pivotX = menuWidth / 2f
@@ -166,8 +167,8 @@ class HandleMenuAnimator(
moreActionsPill.pivotX = menuWidth / 2f
moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat()
- openInBrowserPill.pivotX = menuWidth / 2f
- openInBrowserPill.pivotY = appInfoPill.measuredHeight.toFloat()
+ openInAppOrBrowserPill.pivotX = menuWidth / 2f
+ openInAppOrBrowserPill.pivotY = appInfoPill.measuredHeight.toFloat()
}
private fun animateAppInfoPillOpen() {
@@ -297,36 +298,36 @@ class HandleMenuAnimator(
}
}
- private fun animateOpenInBrowserPill() {
+ private fun animateOpenInAppOrBrowserPill() {
// Open in Browser X & Y Scaling Animation
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
startDelay = BODY_SCALE_OPEN_DELAY
duration = BODY_SCALE_OPEN_DURATION
}
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
startDelay = BODY_SCALE_OPEN_DELAY
duration = BODY_SCALE_OPEN_DURATION
}
// Open in Browser Opacity Animation
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 1f).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, ALPHA, 1f).apply {
startDelay = BODY_ALPHA_OPEN_DELAY
duration = BODY_ALPHA_OPEN_DURATION
}
// Open in Browser Elevation Animation
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, TRANSLATION_Z, 1f).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, TRANSLATION_Z, 1f).apply {
startDelay = ELEVATION_OPEN_DELAY
duration = BODY_ELEVATION_OPEN_DURATION
}
// Open in Browser Button Opacity Animation
- val button = openInBrowserPill.requireViewById<Button>(R.id.open_in_browser_button)
+ val button = openInAppOrBrowserPill.requireViewById<Button>(R.id.open_in_app_or_browser_button)
animators +=
ObjectAnimator.ofFloat(button, ALPHA, 1f).apply {
startDelay = BODY_ALPHA_OPEN_DELAY
@@ -438,33 +439,33 @@ class HandleMenuAnimator(
}
}
- private fun openInBrowserPillClose() {
+ private fun openInAppOrBrowserPillClose() {
// Open in Browser X & Y Scaling Animation
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, SCALE_X, HALF_INITIAL_SCALE).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_X, HALF_INITIAL_SCALE).apply {
duration = BODY_CLOSE_DURATION
}
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, SCALE_Y, HALF_INITIAL_SCALE).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_Y, HALF_INITIAL_SCALE).apply {
duration = BODY_CLOSE_DURATION
}
// Open in Browser Opacity Animation
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 0f).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, ALPHA, 0f).apply {
duration = BODY_CLOSE_DURATION
}
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 0f).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, ALPHA, 0f).apply {
duration = BODY_CLOSE_DURATION
}
// Upward Open in Browser y-translation Animation
val yStart: Float = -captionHeight / 2
animators +=
- ObjectAnimator.ofFloat(openInBrowserPill, TRANSLATION_Y, yStart).apply {
+ ObjectAnimator.ofFloat(openInAppOrBrowserPill, TRANSLATION_Y, yStart).apply {
duration = BODY_CLOSE_DURATION
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt
index cf82bb4f9919..8bc56e0807a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt
@@ -16,13 +16,12 @@
package com.android.wm.shell.windowdecor
import android.app.ActivityManager.RunningTaskInfo
-import com.android.window.flags.Flags
-import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
-
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.ImageButton
+import android.window.DesktopModeFlags
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
/**
* A custom [ImageButton] for buttons inside handle menu that intentionally doesn't handle hovers.
@@ -39,7 +38,7 @@ class HandleMenuImageButton(
lateinit var taskInfo: RunningTaskInfo
override fun onHoverEvent(motionEvent: MotionEvent): Boolean {
- if (Flags.enableHandleInputFix() || taskInfo.isFreeform) {
+ if (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() || taskInfo.isFreeform) {
return super.onHoverEvent(motionEvent)
} else {
return false
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index b016c755e323..a3c75bf33cde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -127,7 +127,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
mDisplayController.removeDisplayWindowListener(this);
- relayout(mTaskInfo, mHasGlobalFocus);
+ relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion);
}
};
@@ -143,7 +143,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
SurfaceControl mDecorationContainerSurface;
SurfaceControl mCaptionContainerSurface;
- private WindowlessWindowManager mCaptionWindowManager;
+ private CaptionWindowlessWindowManager mCaptionWindowManager;
private SurfaceControlViewHost mViewHost;
private Configuration mWindowDecorConfig;
TaskDragResizer mTaskDragResizer;
@@ -152,6 +152,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
boolean mIsStatusBarVisible;
boolean mIsKeyguardVisibleAndOccluded;
boolean mHasGlobalFocus;
+ final Region mExclusionRegion = Region.obtain();
/** The most recent set of insets applied to this window decoration. */
private WindowDecorationInsets mWindowDecorationInsets;
@@ -218,7 +219,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
* constructor.
* @param hasGlobalFocus Whether the task is focused
*/
- abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus);
+ abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion);
/**
* Used by the {@link DragPositioningCallback} associated with the implementing class to
@@ -244,6 +246,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mTaskInfo = params.mRunningTaskInfo;
}
mHasGlobalFocus = params.mHasGlobalFocus;
+ mExclusionRegion.set(params.mDisplayExclusionRegion);
final int oldLayoutResId = mLayoutResId;
mLayoutResId = params.mLayoutResId;
@@ -402,7 +405,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final int elementWidthPx =
resources.getDimensionPixelSize(element.mWidthResId);
boundingRects[i] =
- calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
+ calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect);
// Subtract the regions used by the caption elements, the rest is
// customizable.
if (params.hasInputFeatureSpy()) {
@@ -477,9 +480,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (mCaptionWindowManager == null) {
// Put caption under a container surface because ViewRootImpl sets the destination frame
// of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
- mCaptionWindowManager = new WindowlessWindowManager(
- mTaskInfo.getConfiguration(), mCaptionContainerSurface,
- null /* hostInputToken */);
+ mCaptionWindowManager = new CaptionWindowlessWindowManager(
+ mTaskInfo.getConfiguration(), mCaptionContainerSurface);
}
mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
final WindowManager.LayoutParams lp =
@@ -492,6 +494,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
lp.setTrustedOverlay();
lp.inputFeatures = params.mInputFeatures;
+ final Rect localCaptionBounds = new Rect(
+ outResult.mCaptionX,
+ outResult.mCaptionY,
+ outResult.mCaptionX + outResult.mCaptionWidth,
+ outResult.mCaptionY + outResult.mCaptionHeight);
+ final Region touchableRegion = params.mLimitTouchRegionToSystemAreas
+ ? calculateLimitedTouchableRegion(params, localCaptionBounds)
+ : null;
if (mViewHost == null) {
Trace.beginSection("CaptionViewHostLayout-new");
mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
@@ -503,6 +513,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
}
outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+ if (params.mLimitTouchRegionToSystemAreas) {
+ mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion);
+ }
mViewHost.setView(outResult.mRootView, lp);
Trace.endSection();
} else {
@@ -514,13 +527,71 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
}
outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+ if (params.mLimitTouchRegionToSystemAreas) {
+ mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion);
+ }
mViewHost.relayout(lp);
Trace.endSection();
}
+ if (touchableRegion != null) {
+ touchableRegion.recycle();
+ }
Trace.endSection(); // CaptionViewHostLayout
}
- private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element,
+ @NonNull
+ private Region calculateLimitedTouchableRegion(
+ RelayoutParams params,
+ @NonNull Rect localCaptionBounds) {
+ // Make caption bounds relative to display to align with exclusion region.
+ final Point positionInParent = params.mRunningTaskInfo.positionInParent;
+ final Rect captionBoundsInDisplay = new Rect(localCaptionBounds);
+ captionBoundsInDisplay.offsetTo(positionInParent.x, positionInParent.y);
+
+ final Region boundingRects = calculateBoundingRectsRegion(params, captionBoundsInDisplay);
+
+ final Region customizedRegion = Region.obtain();
+ customizedRegion.set(captionBoundsInDisplay);
+ customizedRegion.op(boundingRects, Region.Op.DIFFERENCE);
+ customizedRegion.op(params.mDisplayExclusionRegion, Region.Op.INTERSECT);
+
+ final Region touchableRegion = Region.obtain();
+ touchableRegion.set(captionBoundsInDisplay);
+ touchableRegion.op(customizedRegion, Region.Op.DIFFERENCE);
+ // Return resulting region back to window coordinates.
+ touchableRegion.translate(-positionInParent.x, -positionInParent.y);
+
+ boundingRects.recycle();
+ customizedRegion.recycle();
+ return touchableRegion;
+ }
+
+ @NonNull
+ private Region calculateBoundingRectsRegion(
+ @NonNull RelayoutParams params,
+ @NonNull Rect captionBoundsInDisplay) {
+ final int numOfElements = params.mOccludingCaptionElements.size();
+ final Region region = Region.obtain();
+ if (numOfElements == 0) {
+ // The entire caption is a bounding rect.
+ region.set(captionBoundsInDisplay);
+ return region;
+ }
+ final Resources resources = mDecorWindowContext.getResources();
+ for (int i = 0; i < numOfElements; i++) {
+ final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i);
+ final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId);
+ final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx,
+ captionBoundsInDisplay);
+ // Bounding rect is initially calculated relative to the caption, so offset it to make
+ // it relative to the display.
+ boundingRect.offset(captionBoundsInDisplay.left, captionBoundsInDisplay.top);
+ region.union(boundingRect);
+ }
+ return region;
+ }
+
+ private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element,
int elementWidthPx, @NonNull Rect captionRect) {
switch (element.mAlignment) {
case START -> {
@@ -539,7 +610,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mIsKeyguardVisibleAndOccluded = visible && occluded;
final boolean changed = prevVisAndOccluded != mIsKeyguardVisibleAndOccluded;
if (changed) {
- relayout(mTaskInfo, mHasGlobalFocus);
+ relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion);
}
}
@@ -549,10 +620,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final boolean changed = prevStatusBarVisibility != mIsStatusBarVisible;
if (changed) {
- relayout(mTaskInfo, mHasGlobalFocus);
+ relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion);
}
}
+ void onExclusionRegionChanged(@NonNull Region exclusionRegion) {
+ relayout(mTaskInfo, mHasGlobalFocus, exclusionRegion);
+ }
+
/**
* Update caption visibility state and views.
*/
@@ -751,9 +826,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
int mCaptionHeightId;
int mCaptionWidthId;
final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
+ boolean mLimitTouchRegionToSystemAreas;
int mInputFeatures;
boolean mIsInsetSource = true;
@InsetsSource.Flags int mInsetSourceFlags;
+ final Region mDisplayExclusionRegion = Region.obtain();
int mShadowRadiusId;
int mCornerRadius;
@@ -772,9 +849,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionHeightId = Resources.ID_NULL;
mCaptionWidthId = Resources.ID_NULL;
mOccludingCaptionElements.clear();
+ mLimitTouchRegionToSystemAreas = false;
mInputFeatures = 0;
mIsInsetSource = true;
mInsetSourceFlags = 0;
+ mDisplayExclusionRegion.setEmpty();
mShadowRadiusId = Resources.ID_NULL;
mCornerRadius = 0;
@@ -830,6 +909,19 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
}
+ private static class CaptionWindowlessWindowManager extends WindowlessWindowManager {
+ CaptionWindowlessWindowManager(
+ @NonNull Configuration configuration,
+ @NonNull SurfaceControl rootSurface) {
+ super(configuration, rootSurface, /* hostInputToken= */ null);
+ }
+
+ /** Set the view host's touchable region. */
+ void setTouchRegion(@NonNull SurfaceControlViewHost viewHost, @NonNull Region region) {
+ setTouchRegion(viewHost.getWindowToken().asBinder(), region);
+ }
+ }
+
@VisibleForTesting
public interface SurfaceControlViewHostFactory {
default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index e43c3a613157..0e40a5350a43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -30,6 +30,7 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
@@ -48,6 +49,7 @@ class DesktopTilingDecorViewModel(
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val taskRepository: DesktopRepository,
+ private val desktopModeEventLogger: DesktopModeEventLogger,
) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
@@ -80,6 +82,7 @@ class DesktopTilingDecorViewModel(
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
taskRepository,
+ desktopModeEventLogger,
)
tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
newHandler
@@ -100,7 +103,7 @@ class DesktopTilingDecorViewModel(
fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean {
return tilingTransitionHandlerByDisplayId
.get(taskInfo.displayId)
- ?.moveTiledPairToFront(taskInfo) ?: false
+ ?.moveTiledPairToFront(taskInfo, isTaskFocused = true) ?: false
}
fun onOverviewAnimationStateChange(isRunning: Boolean) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
index 209eb5e501b2..6cdc517c9cb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -23,6 +23,7 @@ import android.graphics.Rect
import android.graphics.Region
import android.os.Binder
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.RoundedCorner
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
@@ -39,6 +40,7 @@ import android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER
import android.view.WindowlessWindowManager
import com.android.wm.shell.R
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import java.util.function.Supplier
/**
@@ -141,8 +143,9 @@ class DesktopTilingDividerWindowManager(
t.setRelativeLayer(leash, relativeLeash, 1)
}
- override fun onDividerMoveStart(pos: Int) {
+ override fun onDividerMoveStart(pos: Int, motionEvent: MotionEvent) {
setSlippery(false)
+ transitionHandler.onDividerHandleDragStart(motionEvent)
}
/**
@@ -161,13 +164,13 @@ class DesktopTilingDividerWindowManager(
* Notifies the transition handler of tiling operations ending, which might result in resizing
* WindowContainerTransactions if the sizes of the tiled tasks changed.
*/
- override fun onDividerMovedEnd(pos: Int) {
+ override fun onDividerMovedEnd(pos: Int, motionEvent: MotionEvent) {
setSlippery(true)
val t = transactionSupplier.get()
t.setPosition(leash, pos.toFloat() - maxRoundedCornerRadius, dividerBounds.top.toFloat())
val dividerWidth = dividerBounds.width()
dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
- transitionHandler.onDividerHandleDragEnd(dividerBounds, t)
+ transitionHandler.onDividerHandleDragEnd(dividerBounds, t, motionEvent)
}
private fun getWindowManagerParams(): WindowManager.LayoutParams {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index c46767c3a51d..418b8ecd5534 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -25,6 +25,7 @@ import android.graphics.Rect
import android.os.IBinder
import android.os.UserHandle
import android.util.Slog
+import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_CHANGE
@@ -44,6 +45,8 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
@@ -70,6 +73,7 @@ class DesktopTilingWindowDecoration(
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val taskRepository: DesktopRepository,
+ private val desktopModeEventLogger: DesktopModeEventLogger,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
) :
Transitions.TransitionHandler,
@@ -218,6 +222,25 @@ class DesktopTilingWindowDecoration(
return tilingManager
}
+ fun onDividerHandleDragStart(motionEvent: MotionEvent) {
+ val leftTiledTask = leftTaskResizingHelper ?: return
+ val rightTiledTask = rightTaskResizingHelper ?: return
+
+ desktopModeEventLogger.logTaskResizingStarted(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ leftTiledTask.taskInfo,
+ displayController,
+ )
+
+ desktopModeEventLogger.logTaskResizingStarted(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ rightTiledTask.taskInfo,
+ displayController,
+ )
+ }
+
fun onDividerHandleMoved(dividerBounds: Rect, t: SurfaceControl.Transaction): Boolean {
val leftTiledTask = leftTaskResizingHelper ?: return false
val rightTiledTask = rightTaskResizingHelper ?: return false
@@ -266,10 +289,32 @@ class DesktopTilingWindowDecoration(
return true
}
- fun onDividerHandleDragEnd(dividerBounds: Rect, t: SurfaceControl.Transaction) {
+ fun onDividerHandleDragEnd(
+ dividerBounds: Rect,
+ t: SurfaceControl.Transaction,
+ motionEvent: MotionEvent,
+ ) {
val leftTiledTask = leftTaskResizingHelper ?: return
val rightTiledTask = rightTaskResizingHelper ?: return
+ desktopModeEventLogger.logTaskResizingEnded(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ leftTiledTask.taskInfo,
+ leftTiledTask.newBounds.height(),
+ leftTiledTask.newBounds.width(),
+ displayController,
+ )
+
+ desktopModeEventLogger.logTaskResizingEnded(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ rightTiledTask.taskInfo,
+ rightTiledTask.newBounds.height(),
+ rightTiledTask.newBounds.width(),
+ displayController,
+ )
+
if (leftTiledTask.newBounds == leftTiledTask.bounds) {
leftTiledTask.hideVeil()
rightTiledTask.hideVeil()
@@ -426,9 +471,9 @@ class DesktopTilingWindowDecoration(
}
}
+ // Only called if [taskInfo] relates to a focused task
private fun isTilingFocusRemoved(taskInfo: RunningTaskInfo): Boolean {
- return taskInfo.isFocused &&
- isTilingFocused &&
+ return isTilingFocused &&
taskInfo.taskId != leftTaskResizingHelper?.taskInfo?.taskId &&
taskInfo.taskId != rightTaskResizingHelper?.taskInfo?.taskId
}
@@ -439,9 +484,9 @@ class DesktopTilingWindowDecoration(
}
}
+ // Only called if [taskInfo] relates to a focused task
private fun isTilingRefocused(taskInfo: RunningTaskInfo): Boolean {
return !isTilingFocused &&
- taskInfo.isFocused &&
(taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId)
}
@@ -528,9 +573,19 @@ class DesktopTilingWindowDecoration(
removeTaskIfTiled(taskId, taskVanished = true, shouldDelayUpdate = true)
}
- fun moveTiledPairToFront(taskInfo: RunningTaskInfo): Boolean {
+ /**
+ * Moves the tiled pair to the front of the task stack, if the [taskInfo] is focused and one of
+ * the two tiled tasks.
+ *
+ * If specified, [isTaskFocused] will override [RunningTaskInfo.isFocused]. This is to be used
+ * when called when the task will be focused, but the [taskInfo] hasn't been updated yet.
+ */
+ fun moveTiledPairToFront(taskInfo: RunningTaskInfo, isTaskFocused: Boolean? = null): Boolean {
if (!isTilingManagerInitialised) return false
+ val isFocused = isTaskFocused ?: taskInfo.isFocused
+ if (!isFocused) return false
+
// If a task that isn't tiled is being focused, let the generic handler do the work.
if (isTilingFocusRemoved(taskInfo)) {
isTilingFocused = false
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
index b3b30ad4c09e..9799d01afc9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DividerMoveCallback.kt
@@ -15,14 +15,16 @@
*/
package com.android.wm.shell.windowdecor.tiling
+import android.view.MotionEvent
+
/** Divider move callback to whichever entity that handles the moving logic. */
interface DividerMoveCallback {
/** Called on the divider move start gesture. */
- fun onDividerMoveStart(pos: Int)
+ fun onDividerMoveStart(pos: Int, motionEvent: MotionEvent)
/** Called on the divider moved by dragging it. */
fun onDividerMove(pos: Int): Boolean
/** Called on divider move gesture end. */
- fun onDividerMovedEnd(pos: Int)
+ fun onDividerMovedEnd(pos: Int, motionEvent: MotionEvent)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
index 89229051941c..111e28e450bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -206,7 +206,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
if (!isWithinHandleRegion(yTouchPosInDivider)) return true
- callback.onDividerMoveStart(touchPos)
+ callback.onDividerMoveStart(touchPos, event)
setTouching()
canResize = true
}
@@ -230,7 +230,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
if (!canResize) return true
if (moving && resized) {
dividerBounds.left = dividerBounds.left + lastAcceptedPos - startPos
- callback.onDividerMovedEnd(dividerBounds.left)
+ callback.onDividerMovedEnd(dividerBounds.left, event)
}
moving = false
canResize = false
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index b5700ffb046b..503ad92d4d71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -34,15 +34,14 @@ import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageButton
+import android.window.DesktopModeFlags
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import com.android.internal.policy.SystemBarUtils
-import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
-import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data
/**
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
@@ -141,7 +140,7 @@ internal class AppHandleViewHolder(
private fun createStatusBarInputLayer(handlePosition: Point,
handleWidth: Int,
handleHeight: Int) {
- if (!Flags.enableHandleInputFix()) return
+ if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) return
statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 4fe66f3357a3..4cddf31321d6 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -23,8 +23,9 @@ import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize
import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
-import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowBecomesInvisible
+import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd
import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd
import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
@@ -44,6 +45,7 @@ import android.tools.flicker.assertors.assertions.LauncherWindowReplacesAppAsTop
import android.tools.flicker.config.AssertionTemplates
import android.tools.flicker.config.FlickerConfigEntry
import android.tools.flicker.config.ScenarioId
+import android.tools.flicker.config.common.Components.LAUNCHER
import android.tools.flicker.config.desktopmode.Components.DESKTOP_MODE_APP
import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER
import android.tools.flicker.config.desktopmode.Components.NON_RESIZABLE_APP
@@ -365,5 +367,57 @@ class DesktopModeFlickerScenarios {
AppWindowAlignsWithOnlyOneDisplayCornerAtEnd(DESKTOP_MODE_APP)
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
+
+ val MINIMIZE_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("MINIMIZE_APP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ return transitions
+ .filter { it.type == TransitionType.MINIMIZE }
+ .sortedByDescending { it.id }
+ .drop(1)
+ }
+ }
+ ),
+ assertions =
+ AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ AppWindowOnTopAtStart(DESKTOP_MODE_APP),
+ AppWindowBecomesInvisible(DESKTOP_MODE_APP),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
+
+ val MINIMIZE_LAST_APP =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("MINIMIZE_LAST_APP"),
+ extractor =
+ ShellTransitionScenarioExtractor(
+ transitionMatcher =
+ object : ITransitionMatcher {
+ override fun findAll(
+ transitions: Collection<Transition>
+ ): Collection<Transition> {
+ val lastTransition =
+ transitions
+ .filter { it.type == TransitionType.MINIMIZE }
+ .maxByOrNull { it.id }!!
+ return listOf(lastTransition)
+ }
+ }
+ ),
+ assertions =
+ AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ AppWindowOnTopAtStart(DESKTOP_MODE_APP),
+ AppWindowBecomesInvisible(DESKTOP_MODE_APP),
+ AppWindowOnTopAtEnd(LAUNCHER),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt
new file mode 100644
index 000000000000..58582b02c212
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_LAST_APP
+import com.android.wm.shell.scenarios.MinimizeAppWindows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Minimize app windows by pressing the minimize button.
+ *
+ * Assert that the app windows gets hidden.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MinimizeAppsLandscape : MinimizeAppWindows(rotation = ROTATION_90) {
+ @ExpectedScenarios(["MINIMIZE_APP", "MINIMIZE_LAST_APP"])
+ @Test
+ override fun minimizeAllAppWindows() = super.minimizeAllAppWindows()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(MINIMIZE_APP)
+ .use(MINIMIZE_LAST_APP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.kt
new file mode 100644
index 000000000000..7970426a6ee8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_LAST_APP
+import com.android.wm.shell.scenarios.MinimizeAppWindows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Minimize app windows by pressing the minimize button.
+ *
+ * Assert that the app windows gets hidden.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MinimizeAppsPortrait : MinimizeAppWindows() {
+ @ExpectedScenarios(["MINIMIZE_APP", "MINIMIZE_LAST_APP"])
+ @Test
+ override fun minimizeAllAppWindows() = super.minimizeAllAppWindows()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(MINIMIZE_APP)
+ .use(MINIMIZE_LAST_APP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAppWindowsTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAppWindowsTest.kt
new file mode 100644
index 000000000000..e16159c0613a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAppWindowsTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.MinimizeAppWindows
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [MinimizeAppWindows]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class MinimizeAppWindowsTest : MinimizeAppWindows()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index 824c4482c1e6..f442fdb31592 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.scenarios
import android.tools.NavBar
import android.tools.Rotation
+import com.android.internal.R
import com.android.window.flags.Flags
import com.android.wm.shell.Utils
import org.junit.After
@@ -40,6 +41,9 @@ constructor(
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ // Skip the test when the drag-to-maximize is enabled on this device.
+ Assume.assumeFalse(Flags.enableDragToMaximize() &&
+ instrumentation.context.resources.getBoolean(R.bool.config_dragToMaximizeInDesktopMode))
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
testApp.enterDesktopWithDrag(wmHelper, device)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
new file mode 100644
index 000000000000..b5483634b057
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Base scenario test for minimizing all the desktop app windows one-by-one by clicking their
+ * minimize buttons.
+ */
+@Ignore("Test Base Class")
+abstract class MinimizeAppWindows
+constructor(private val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp1 = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val testApp2 = DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
+ private val testApp3 = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ Assume.assumeTrue(Flags.enableMinimizeButton())
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ testApp1.enterDesktopWithDrag(wmHelper, device)
+ testApp2.launchViaIntent(wmHelper)
+ testApp3.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun minimizeAllAppWindows() {
+ testApp3.minimizeDesktopApp(wmHelper, device)
+ testApp2.minimizeDesktopApp(wmHelper, device)
+ testApp1.minimizeDesktopApp(wmHelper, device)
+ }
+
+ @After
+ fun teardown() {
+ testApp1.exit(wmHelper)
+ testApp2.exit(wmHelper)
+ testApp3.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 266e48482568..2ed7d07ac75e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -62,6 +62,7 @@ public class BackProgressAnimatorTest {
@Before
public void setUp() throws Exception {
+ mTargetProgressCalled = new CountDownLatch(1);
mMainThreadHandler = new Handler(Looper.getMainLooper());
final BackMotionEvent backEvent = backMotionEventFrom(0, 0);
mMainThreadHandler.post(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
index d38b848fbb4d..329a10998f23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
@@ -16,9 +16,8 @@
package com.android.wm.shell.bubbles.bar;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
-import android.graphics.drawable.ColorDrawable;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -47,10 +46,9 @@ public class BubbleBarHandleViewTest extends ShellTestCase {
public void testUpdateHandleColor_lightBg() {
mHandleView.updateHandleColor(false /* isRegionDark */, false /* animated */);
- assertTrue(mHandleView.getClipToOutline());
- assertTrue(mHandleView.getBackground() instanceof ColorDrawable);
- ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground();
- assertEquals(bgDrawable.getColor(),
+ assertFalse(mHandleView.getClipToOutline());
+ int handleColor = mHandleView.mHandlePaint.getColor();
+ assertEquals(handleColor,
ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_dark));
}
@@ -58,10 +56,9 @@ public class BubbleBarHandleViewTest extends ShellTestCase {
public void testUpdateHandleColor_darkBg() {
mHandleView.updateHandleColor(true /* isRegionDark */, false /* animated */);
- assertTrue(mHandleView.getClipToOutline());
- assertTrue(mHandleView.getBackground() instanceof ColorDrawable);
- ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground();
- assertEquals(bgDrawable.getColor(),
+ assertFalse(mHandleView.getClipToOutline());
+ int handleColor = mHandleView.mHandlePaint.getColor();
+ assertEquals(handleColor,
ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
new file mode 100644
index 000000000000..b9490b881d08
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.pip;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test against {@link PipAppOpsListener}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipAppOpsListenerTest {
+
+ @Mock private Context mMockContext;
+ @Mock private PackageManager mMockPackageManager;
+ @Mock private AppOpsManager mMockAppOpsManager;
+ @Mock private PipAppOpsListener.Callback mMockCallback;
+ @Mock private ShellExecutor mMockExecutor;
+
+ private PipAppOpsListener mPipAppOpsListener;
+
+ private ArgumentCaptor<AppOpsManager.OnOpChangedListener> mOnOpChangedListenerCaptor;
+ private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
+ private Pair<ComponentName, Integer> mTopPipActivity;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
+ .thenReturn(mMockAppOpsManager);
+ mOnOpChangedListenerCaptor = ArgumentCaptor.forClass(
+ AppOpsManager.OnOpChangedListener.class);
+ mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ }
+
+ @Test
+ public void onActivityPinned_registerAppOpsListener() {
+ String packageName = "com.android.test.pip";
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+
+ mPipAppOpsListener.onActivityPinned(packageName);
+
+ verify(mMockAppOpsManager).startWatchingMode(
+ eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName),
+ any(AppOpsManager.OnOpChangedListener.class));
+ }
+
+ @Test
+ public void onActivityUnpinned_unregisterAppOpsListener() {
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+
+ mPipAppOpsListener.onActivityUnpinned();
+
+ verify(mMockAppOpsManager).stopWatchingMode(any(AppOpsManager.OnOpChangedListener.class));
+ }
+
+ @Test
+ public void disablePipAppOps_dismissPip() throws PackageManager.NameNotFoundException {
+ String packageName = "com.android.test.pip";
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ // Set up the top pip activity info as mTopPipActivity
+ mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
+ mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
+ // Set up the application info as mApplicationInfo
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ // Mock the mode to be **not** allowed
+ when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), eq(packageName)))
+ .thenReturn(AppOpsManager.MODE_DEFAULT);
+ // Set up the initial state
+ mPipAppOpsListener.onActivityPinned(packageName);
+ verify(mMockAppOpsManager).startWatchingMode(
+ eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName),
+ mOnOpChangedListenerCaptor.capture());
+ AppOpsManager.OnOpChangedListener opChangedListener = mOnOpChangedListenerCaptor.getValue();
+
+ opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
+ packageName);
+
+ verify(mMockExecutor).execute(mRunnableArgumentCaptor.capture());
+ Runnable runnable = mRunnableArgumentCaptor.getValue();
+ runnable.run();
+ verify(mMockCallback).dismissPip();
+ }
+
+ @Test
+ public void disablePipAppOps_differentPackage_doNothing()
+ throws PackageManager.NameNotFoundException {
+ String packageName = "com.android.test.pip";
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ // Set up the top pip activity info as mTopPipActivity
+ mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
+ mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
+ // Set up the application info as mApplicationInfo
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName + ".modified";
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ // Mock the mode to be **not** allowed
+ when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), eq(packageName)))
+ .thenReturn(AppOpsManager.MODE_DEFAULT);
+ // Set up the initial state
+ mPipAppOpsListener.onActivityPinned(packageName);
+ verify(mMockAppOpsManager).startWatchingMode(
+ eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName),
+ mOnOpChangedListenerCaptor.capture());
+ AppOpsManager.OnOpChangedListener opChangedListener = mOnOpChangedListenerCaptor.getValue();
+
+ opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
+ packageName);
+
+ verifyZeroInteractions(mMockExecutor);
+ }
+
+ @Test
+ public void disablePipAppOps_nameNotFound_unregisterAppOpsListener()
+ throws PackageManager.NameNotFoundException {
+ String packageName = "com.android.test.pip";
+ mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor);
+ // Set up the top pip activity info as mTopPipActivity
+ mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0);
+ mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity);
+ // Set up the application info as mApplicationInfo
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException.class);
+ // Mock the mode to be **not** allowed
+ when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), eq(packageName)))
+ .thenReturn(AppOpsManager.MODE_DEFAULT);
+ // Set up the initial state
+ mPipAppOpsListener.onActivityPinned(packageName);
+ verify(mMockAppOpsManager).startWatchingMode(
+ eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName),
+ mOnOpChangedListenerCaptor.capture());
+ AppOpsManager.OnOpChangedListener opChangedListener = mOnOpChangedListenerCaptor.getValue();
+
+ opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
+ packageName);
+
+ verify(mMockAppOpsManager).stopWatchingMode(any(AppOpsManager.OnOpChangedListener.class));
+ }
+
+ private Pair<ComponentName, Integer> getTopPipActivity(Context context) {
+ return mTopPipActivity;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
index ecaf970ae389..803e5d4442a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
@@ -43,38 +43,30 @@ class AppCompatUtilsTest : ShellTestCase() {
.apply {
isTopActivityTransparent = true
numActivities = 1
- }))
- assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
- createFreeformTask(/* displayId */ 0)
- .apply {
- isTopActivityTransparent = true
- numActivities = 0
+ isTopActivityNoDisplay = false
}))
}
@Test
- fun testIsTopActivityExemptFromDesktopWindowing_singleTopActivity() {
- assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
- createFreeformTask(/* displayId */ 0)
- .apply {
- isTopActivityTransparent = true
- numActivities = 1
- }))
+ fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent_multipleActivities() {
assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
- .apply {
- isTopActivityTransparent = false
- numActivities = 1
- }))
+ .apply {
+ isTopActivityTransparent = true
+ numActivities = 2
+ isTopActivityNoDisplay = false
+ }))
}
@Test
- fun testIsTopActivityExemptFromDesktopWindowing__topActivityStyleFloating() {
+ fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent_notDisplayed() {
assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
- .apply {
- isTopActivityStyleFloating = true
- }))
+ .apply {
+ isTopActivityTransparent = true
+ numActivities = 1
+ isTopActivityNoDisplay = true
+ }))
}
@Test
@@ -85,6 +77,19 @@ class AppCompatUtilsTest : ShellTestCase() {
createFreeformTask(/* displayId */ 0)
.apply {
baseActivity = baseComponent
+ isTopActivityNoDisplay = false
}))
}
+
+ @Test
+ fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask_notDisplayed() {
+ val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = true
+ }))
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
new file mode 100644
index 000000000000..6df8d6fd7717
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WindowingMode
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.TransitionInfo
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.ShellExecutor
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() {
+
+ private val testExecutor = mock<ShellExecutor>()
+ private val closingTaskLeash = mock<SurfaceControl>()
+ private val displayController = mock<DisplayController>()
+
+ private lateinit var handler: DesktopBackNavigationTransitionHandler
+
+ @Before
+ fun setUp() {
+ handler =
+ DesktopBackNavigationTransitionHandler(
+ testExecutor,
+ testExecutor,
+ displayController
+ )
+ whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
+ }
+
+ @Test
+ fun handleRequest_returnsNull() {
+ assertNull(handler.handleRequest(mock(), mock()))
+ }
+
+ @Test
+ fun startAnimation_openTransition_returnsFalse() {
+ val animates =
+ handler.startAnimation(
+ transition = mock(),
+ info =
+ createTransitionInfo(
+ type = WindowManager.TRANSIT_OPEN,
+ task = createTask(WINDOWING_MODE_FREEFORM)
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ assertFalse("Should not animate open transition", animates)
+ }
+
+ @Test
+ fun startAnimation_toBackTransitionFullscreenTask_returnsFalse() {
+ val animates =
+ handler.startAnimation(
+ transition = mock(),
+ info = createTransitionInfo(task = createTask(WINDOWING_MODE_FULLSCREEN)),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ assertFalse("Should not animate fullscreen task to back transition", animates)
+ }
+
+ @Test
+ fun startAnimation_toBackTransitionOpeningFreeformTask_returnsFalse() {
+ val animates =
+ handler.startAnimation(
+ transition = mock(),
+ info =
+ createTransitionInfo(
+ changeMode = WindowManager.TRANSIT_OPEN,
+ task = createTask(WINDOWING_MODE_FREEFORM)
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ assertFalse("Should not animate opening freeform task to back transition", animates)
+ }
+
+ @Test
+ fun startAnimation_toBackTransitionToBackFreeformTask_returnsTrue() {
+ val animates =
+ handler.startAnimation(
+ transition = mock(),
+ info = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ assertTrue("Should animate going to back freeform task close transition", animates)
+ }
+
+ @Test
+ fun startAnimation_closeTransitionClosingFreeformTask_returnsTrue() {
+ val animates =
+ handler.startAnimation(
+ transition = mock(),
+ info = createTransitionInfo(
+ type = TRANSIT_CLOSE,
+ changeMode = TRANSIT_CLOSE,
+ task = createTask(WINDOWING_MODE_FREEFORM)
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ assertTrue("Should animate going to back freeform task close transition", animates)
+ }
+ private fun createTransitionInfo(
+ type: Int = WindowManager.TRANSIT_TO_BACK,
+ changeMode: Int = WindowManager.TRANSIT_TO_BACK,
+ task: RunningTaskInfo
+ ): TransitionInfo =
+ TransitionInfo(type, 0 /* flags */).apply {
+ addChange(
+ TransitionInfo.Change(mock(), closingTaskLeash).apply {
+ mode = changeMode
+ parent = null
+ taskInfo = task
+ }
+ )
+ }
+
+ private fun createTask(@WindowingMode windowingMode: Int): RunningTaskInfo =
+ TestRunningTaskInfoBuilder()
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(windowingMode)
+ .build()
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
new file mode 100644
index 000000000000..fea82365c1a0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.content.ContentResolver
+import android.os.Binder
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for [DesktopDisplayEventHandler]
+ *
+ * Usage: atest WMShellUnitTests:DesktopDisplayEventHandlerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopDisplayEventHandlerTest : ShellTestCase() {
+
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock lateinit var transitions: Transitions
+ @Mock lateinit var displayController: DisplayController
+ @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockWindowManager: IWindowManager
+
+ private lateinit var shellInit: ShellInit
+ private lateinit var handler: DesktopDisplayEventHandler
+
+ @Before
+ fun setUp() {
+ shellInit = spy(ShellInit(testExecutor))
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+ val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+ handler =
+ DesktopDisplayEventHandler(
+ context,
+ shellInit,
+ transitions,
+ displayController,
+ rootTaskDisplayAreaOrganizer,
+ mockWindowManager,
+ )
+ shellInit.init()
+ }
+
+ private fun testDisplayWindowingModeSwitch(
+ defaultWindowingMode: Int,
+ extendedDisplayEnabled: Boolean,
+ expectTransition: Boolean
+ ) {
+ val externalDisplayId = 100
+ val captor = ArgumentCaptor.forClass(OnDisplaysChangedListener::class.java)
+ verify(displayController).addDisplayWindowListener(captor.capture())
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
+ val settingsSession = ExtendedDisplaySettingsSession(
+ context.contentResolver, if (extendedDisplayEnabled) 1 else 0)
+
+ settingsSession.use {
+ // The external display connected.
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
+ captor.value.onDisplayAdded(externalDisplayId)
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ // The external display disconnected.
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ captor.value.onDisplayRemoved(externalDisplayId)
+
+ if (expectTransition) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
+ .isEqualTo(defaultWindowingMode)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
+ }
+ }
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = false,
+ expectTransition = false
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = true,
+ expectTransition = true
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = true,
+ expectTransition = false
+ )
+ }
+
+ private class ExtendedDisplaySettingsSession(
+ private val contentResolver: ContentResolver,
+ private val overrideValue: Int
+ ) : AutoCloseable {
+ private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+ private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
+
+ init { Settings.Global.putInt(contentResolver, settingName, overrideValue) }
+
+ override fun close() {
+ Settings.Global.putInt(contentResolver, settingName, initialValue)
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index e05a0b54fcf4..a4f4d05d2079 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.desktopmode
+import android.animation.AnimatorTestRule
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS
import android.graphics.Rect
@@ -24,6 +25,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.view.Display.DEFAULT_DISPLAY
import android.view.Surface
import android.view.SurfaceControl
@@ -43,6 +45,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.util.StubTransaction
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -64,17 +67,19 @@ import org.mockito.kotlin.whenever
* Usage: atest WMShellUnitTests:DesktopImmersiveControllerTest
*/
@SmallTest
+@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
class DesktopImmersiveControllerTest : ShellTestCase() {
@JvmField @Rule val setFlagsRule = SetFlagsRule()
+ @JvmField @Rule val animatorTestRule = AnimatorTestRule(this)
@Mock private lateinit var mockTransitions: Transitions
private lateinit var desktopRepository: DesktopRepository
@Mock private lateinit var mockDisplayController: DisplayController
@Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
@Mock private lateinit var mockDisplayLayout: DisplayLayout
- private val transactionSupplier = { SurfaceControl.Transaction() }
+ private val transactionSupplier = { StubTransaction() }
private lateinit var controller: DesktopImmersiveController
@@ -89,10 +94,12 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
(invocation.getArgument(0) as Rect).set(STABLE_BOUNDS)
}
controller = DesktopImmersiveController(
+ shellInit = mock(),
transitions = mockTransitions,
desktopRepository = desktopRepository,
displayController = mockDisplayController,
shellTaskOrganizer = mockShellTaskOrganizer,
+ shellCommandHandler = mock(),
transactionSupplier = transactionSupplier,
)
}
@@ -672,6 +679,60 @@ class DesktopImmersiveControllerTest : ShellTestCase() {
assertThat(controller.isImmersiveChange(transition, change)).isTrue()
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun externalAnimateResizeChange_doesNotCleanUpPendingTransitionState() {
+ val task = createFreeformTask()
+ val mockBinder = mock(IBinder::class.java)
+ whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)))
+ .thenReturn(mockBinder)
+ desktopRepository.setTaskInFullImmersiveState(
+ displayId = task.displayId,
+ taskId = task.taskId,
+ immersive = true
+ )
+
+ controller.moveTaskToNonImmersive(task)
+
+ controller.animateResizeChange(
+ change = TransitionInfo.Change(task.token, SurfaceControl()).apply {
+ taskInfo = task
+ },
+ startTransaction = StubTransaction(),
+ finishTransaction = StubTransaction(),
+ finishCallback = { }
+ )
+ animatorTestRule.advanceTimeBy(DesktopImmersiveController.FULL_IMMERSIVE_ANIM_DURATION_MS)
+
+ assertThat(controller.state).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun startAnimation_missingChange_clearsState() {
+ val task = createFreeformTask()
+ val mockBinder = mock(IBinder::class.java)
+ whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)))
+ .thenReturn(mockBinder)
+ desktopRepository.setTaskInFullImmersiveState(
+ displayId = task.displayId,
+ taskId = task.taskId,
+ immersive = false
+ )
+
+ controller.moveTaskToImmersive(task)
+
+ controller.startAnimation(
+ transition = mockBinder,
+ info = createTransitionInfo(changes = emptyList()),
+ startTransaction = StubTransaction(),
+ finishTransaction = StubTransaction(),
+ finishCallback = {}
+ )
+
+ assertThat(controller.state).isNull()
+ }
+
private fun createTransitionInfo(
@TransitionType type: Int = TRANSIT_CHANGE,
@TransitionFlags flags: Int = 0,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index df061e368071..f21f26443748 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -32,6 +32,7 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TransitionType
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
@@ -77,16 +78,28 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@JvmField @Rule val setFlagsRule = SetFlagsRule()
- @Mock lateinit var transitions: Transitions
- @Mock lateinit var desktopRepository: DesktopRepository
- @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
- @Mock lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
- @Mock lateinit var desktopImmersiveController: DesktopImmersiveController
- @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock lateinit var mockHandler: Handler
- @Mock lateinit var closingTaskLeash: SurfaceControl
- @Mock lateinit var shellInit: ShellInit
- @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock
+ lateinit var transitions: Transitions
+ @Mock
+ lateinit var desktopRepository: DesktopRepository
+ @Mock
+ lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
+ @Mock
+ lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler
+ @Mock
+ lateinit var desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler
+ @Mock
+ lateinit var desktopImmersiveController: DesktopImmersiveController
+ @Mock
+ lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock
+ lateinit var mockHandler: Handler
+ @Mock
+ lateinit var closingTaskLeash: SurfaceControl
+ @Mock
+ lateinit var shellInit: ShellInit
+ @Mock
+ lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
private lateinit var mixedHandler: DesktopMixedTransitionHandler
@@ -100,6 +113,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
freeformTaskTransitionHandler,
closeDesktopTaskTransitionHandler,
desktopImmersiveController,
+ desktopBackNavigationTransitionHandler,
interactionJankMonitor,
mockHandler,
shellInit,
@@ -447,6 +461,37 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
+ val wct = WindowContainerTransaction()
+ val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val nonLaunchTaskChange = createChange(createTask(WINDOWING_MODE_FREEFORM))
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+ .thenReturn(transition)
+ mixedHandler.addPendingMixedTransition(
+ PendingMixedTransition.Launch(
+ transition = transition,
+ launchingTask = launchingTask.taskId,
+ minimizingTask = null,
+ exitingImmersiveTask = null,
+ )
+ )
+
+ val started = mixedHandler.startAnimation(
+ transition,
+ createTransitionInfo(
+ TRANSIT_OPEN,
+ listOf(nonLaunchTaskChange)
+ ),
+ SurfaceControl.Transaction(),
+ SurfaceControl.Transaction(),
+ ) { }
+
+ assertFalse("Should not start animation without launching desktop task", started)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -564,6 +609,87 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
assertThat(mixedHandler.pendingMixedTransitions).isEmpty()
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun startAnimation_withMinimizingDesktopTask_callsBackNavigationHandler() {
+ val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val transition = Binder()
+ whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
+ whenever(
+ desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any())
+ )
+ .thenReturn(true)
+ mixedHandler.addPendingMixedTransition(
+ PendingMixedTransition.Minimize(
+ transition = transition,
+ minimizingTask = minimizingTask.taskId,
+ isLastTask = false,
+ )
+ )
+
+ val minimizingTaskChange = createChange(minimizingTask)
+ val started = mixedHandler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ TRANSIT_TO_BACK,
+ listOf(minimizingTaskChange)
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ assertTrue("Should delegate animation to back navigation transition handler", started)
+ verify(desktopBackNavigationTransitionHandler)
+ .startAnimation(
+ eq(transition),
+ argThat { info -> info.changes.contains(minimizingTaskChange) },
+ any(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun startAnimation_withMinimizingLastDesktopTask_dispatchesTransition() {
+ val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+ val transition = Binder()
+ whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2)
+ whenever(
+ desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any())
+ )
+ .thenReturn(true)
+ mixedHandler.addPendingMixedTransition(
+ PendingMixedTransition.Minimize(
+ transition = transition,
+ minimizingTask = minimizingTask.taskId,
+ isLastTask = true,
+ )
+ )
+
+ val minimizingTaskChange = createChange(minimizingTask)
+ mixedHandler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ TRANSIT_TO_BACK,
+ listOf(minimizingTaskChange)
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ verify(transitions)
+ .dispatchTransition(
+ eq(transition),
+ argThat { info -> info.changes.contains(minimizingTaskChange) },
+ any(),
+ any(),
+ any(),
+ eq(mixedHandler)
+ )
+ }
+
private fun createTransitionInfo(
type: Int = WindowManager.TRANSIT_CLOSE,
changeMode: Int = WindowManager.TRANSIT_CLOSE,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 414c1a658b95..7f790d574a7e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -936,6 +936,28 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
+ fun saveBoundsBeforeMinimize_boundsSavedByTaskId() {
+ val taskId = 1
+ val bounds = Rect(0, 0, 200, 200)
+
+ repo.saveBoundsBeforeMinimize(taskId, bounds)
+
+ assertThat(repo.removeBoundsBeforeMinimize(taskId)).isEqualTo(bounds)
+ }
+
+ @Test
+ fun removeBoundsBeforeMinimize_returnsNullAfterBoundsRemoved() {
+ val taskId = 1
+ val bounds = Rect(0, 0, 200, 200)
+ repo.saveBoundsBeforeMinimize(taskId, bounds)
+ repo.removeBoundsBeforeMinimize(taskId)
+
+ val boundsBeforeMinimize = repo.removeBoundsBeforeMinimize(taskId)
+
+ assertThat(boundsBeforeMinimize).isNull()
+ }
+
+ @Test
fun getExpandedTasksOrdered_returnsFreeformTasksInCorrectOrder() {
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true)
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index b157d557c1d8..ad266ead774e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -1123,11 +1123,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_topActivityTranslucentWithStyleFloating_taskIsMovedToDesktop() {
+ fun moveRunningTaskToDesktop_topActivityTranslucentWithoutDisplay_taskIsMovedToDesktop() {
val task =
setUpFullscreenTask().apply {
isTopActivityTransparent = true
- isTopActivityStyleFloating = true
+ isTopActivityNoDisplay = true
numActivities = 1
}
@@ -1139,11 +1139,11 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_topActivityTranslucentWithoutStyleFloating_doesNothing() {
+ fun moveRunningTaskToDesktop_topActivityTranslucentWithDisplay_doesNothing() {
val task =
setUpFullscreenTask().apply {
isTopActivityTransparent = true
- isTopActivityStyleFloating = false
+ isTopActivityNoDisplay = false
numActivities = 1
}
@@ -1153,20 +1153,41 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun moveRunningTaskToDesktop_systemUIActivity_doesNothing() {
- val task = setUpFullscreenTask()
-
+ fun moveRunningTaskToDesktop_systemUIActivityWithDisplay_doesNothing() {
// Set task as systemUI package
val systemUIPackageName = context.resources.getString(
com.android.internal.R.string.config_systemUi)
val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- task.baseActivity = baseComponent
+ val task =
+ setUpFullscreenTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
verifyEnterDesktopWCTNotExecuted()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveRunningTaskToDesktop_systemUIActivityWithoutDisplay_doesNothing() {
+ // Set task as systemUI package
+ val systemUIPackageName = context.resources.getString(
+ com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val task =
+ setUpFullscreenTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = true
+ }
+
+ controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
fun moveRunningTaskToDesktop_deviceSupported_taskIsMovedToDesktop() {
val task = setUpFullscreenTask()
@@ -2223,14 +2244,14 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_topActivityTransparentWithStyleFloating_returnSwitchToFreeformWCT() {
+ fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
val freeformTask = setUpFreeformTask()
markTaskVisible(freeformTask)
val task =
setUpFullscreenTask().apply {
isTopActivityTransparent = true
- isTopActivityStyleFloating = true
+ isTopActivityNoDisplay = true
numActivities = 1
}
@@ -2241,11 +2262,14 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_topActivityTransparentWithoutStyleFloating_returnSwitchToFullscreenWCT() {
+ fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
val task =
setUpFreeformTask().apply {
isTopActivityTransparent = true
- isTopActivityStyleFloating = false
+ isTopActivityNoDisplay = false
numActivities = 1
}
@@ -2256,14 +2280,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun handleRequest_systemUIActivity_returnSwitchToFullscreenWCT() {
- val task = setUpFreeformTask()
+ fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
// Set task as systemUI package
val systemUIPackageName = context.resources.getString(
com.android.internal.R.string.config_systemUi)
val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- task.baseActivity = baseComponent
+ val task =
+ setUpFreeformTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
val result = controller.handleRequest(Binder(), createTransition(task))
assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
@@ -2271,6 +2300,27 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ // Set task as systemUI package
+ val systemUIPackageName = context.resources.getString(
+ com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ val task =
+ setUpFullscreenTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = true
+ }
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
val task = setUpFreeformTask()
@@ -2988,6 +3038,21 @@ class DesktopTasksControllerTest : ShellTestCase() {
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
+ // Assert event is properly logged
+ verify(desktopModeEventLogger, times(1)).logTaskResizingStarted(
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+ motionEvent,
+ task,
+ displayController
+ )
+ verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
+ ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
+ motionEvent,
+ task,
+ STABLE_BOUNDS.height(),
+ STABLE_BOUNDS.width(),
+ displayController
+ )
}
@Test
@@ -3032,6 +3097,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
eq(STABLE_BOUNDS),
anyOrNull(),
)
+ // Assert no event is logged
+ verify(desktopModeEventLogger, never()).logTaskResizingStarted(
+ any(), any(), any(), any(), any()
+ )
+ verify(desktopModeEventLogger, never()).logTaskResizingEnded(
+ any(), any(), any(), any(), any(), any(), any()
+ )
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 01b69aed8465..456b50da095b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.Rect
import android.os.Binder
import android.os.Handler
import android.platform.test.annotations.DisableFlags
@@ -24,8 +25,10 @@ import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
+import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
@@ -63,6 +66,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
import org.mockito.kotlin.eq
@@ -235,6 +239,30 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
+ fun onTransitionReady_pendingTransition_changeTaskToBack_boundsSaved() {
+ val bounds = Rect(0, 0, 200, 200)
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ desktopTasksLimiter.addPendingMinimizeChange(
+ transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+ val change = TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
+ mode = TRANSIT_TO_BACK
+ taskInfo = task
+ setStartAbsBounds(bounds)
+ }
+ desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+ transition,
+ TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
+ StubTransaction() /* startTransaction */,
+ StubTransaction() /* finishTransaction */)
+
+ assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
+ assertThat(desktopTaskRepo.removeBoundsBeforeMinimize(taskId = task.taskId)).isEqualTo(
+ bounds)
+ }
+
+ @Test
fun onTransitionReady_transitionMergedFromPending_taskIsMinimized() {
val mergedTransition = Binder()
val newTransition = Binder()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 737439ce3cfe..7f1c1db3207a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -76,6 +76,7 @@ class DesktopTasksTransitionObserverTest {
private val context = mock<Context>()
private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
private val taskRepository = mock<DesktopRepository>()
+ private val mixedHandler = mock<DesktopMixedTransitionHandler>()
private lateinit var transitionObserver: DesktopTasksTransitionObserver
private lateinit var shellInit: ShellInit
@@ -87,7 +88,7 @@ class DesktopTasksTransitionObserverTest {
transitionObserver =
DesktopTasksTransitionObserver(
- context, taskRepository, transitions, shellTaskOrganizer, shellInit
+ context, taskRepository, transitions, shellTaskOrganizer, mixedHandler, shellInit
)
}
@@ -106,6 +107,7 @@ class DesktopTasksTransitionObserverTest {
)
verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+ verify(mixedHandler).addPendingMixedTransition(any())
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index 840126421c08..e40bbad7adda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -20,6 +20,8 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DragEvent.ACTION_DRAG_STARTED;
+import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData;
+
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@@ -30,9 +32,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.Context;
-import android.content.Intent;
import android.os.RemoteException;
import android.view.Display;
import android.view.DragEvent;
@@ -128,7 +128,7 @@ public class DragAndDropControllerTest extends ShellTestCase {
doReturn(display).when(dragLayout).getDisplay();
doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
- final ClipData clipData = createClipData();
+ final ClipData clipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT);
final DragEvent event = mock(DragEvent.class);
doReturn(ACTION_DRAG_STARTED).when(event).getAction();
doReturn(clipData).when(event).getClipData();
@@ -150,15 +150,4 @@ public class DragAndDropControllerTest extends ShellTestCase {
mController.onDrag(dragLayout, event);
verify(mDragAndDropListener, never()).onDragStarted();
}
-
- private ClipData createClipData() {
- ClipDescription clipDescription = new ClipDescription(MIMETYPE_APPLICATION_SHORTCUT,
- new String[] { MIMETYPE_APPLICATION_SHORTCUT });
- Intent i = new Intent();
- i.putExtra(Intent.EXTRA_PACKAGE_NAME, "pkg");
- i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcutId");
- i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
- ClipData.Item item = new ClipData.Item(i);
- return new ClipData(clipDescription, item);
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
new file mode 100644
index 000000000000..3d59342f62d8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.draganddrop
+
+import android.app.ActivityTaskManager
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.content.ClipDescription
+import android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID
+import android.os.PersistableBundle
+import android.os.RemoteException
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo
+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
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for DragSession.
+ *
+ * Usage: atest WMShellUnitTests:DragSessionTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DragSessionTest : ShellTestCase() {
+ @Mock
+ private lateinit var activityTaskManager: ActivityTaskManager
+
+ @Mock
+ private lateinit var displayLayout: DisplayLayout
+
+ @Before
+ @Throws(RemoteException::class)
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testGetRunningTask() {
+ // Set up running tasks
+ val runningTasks = listOf(
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
+ )
+ whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks)
+
+ // Simulate dragging an app
+ val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT)
+
+ // Start a new drag session
+ val session = DragSession(activityTaskManager, displayLayout, data, 0)
+ session.updateRunningTask()
+
+ assertThat(session.runningTaskInfo).isEqualTo(runningTasks.first())
+ assertThat(session.runningTaskWinMode).isEqualTo(runningTasks.first().windowingMode)
+ assertThat(session.runningTaskActType).isEqualTo(runningTasks.first().activityType)
+ }
+
+ @Test
+ fun testGetRunningTaskWithFloatingTasks() {
+ // Set up running tasks
+ val runningTasks = listOf(
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
+ createTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD),
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, alwaysOnTop=true),
+ )
+ whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks)
+
+ // Simulate dragging an app
+ val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT)
+
+ // Start a new drag session
+ val session = DragSession(activityTaskManager, displayLayout, data, 0)
+ session.updateRunningTask()
+
+ // Ensure that we find the first non-floating task
+ assertThat(session.runningTaskInfo).isEqualTo(runningTasks.first())
+ assertThat(session.runningTaskWinMode).isEqualTo(runningTasks.first().windowingMode)
+ assertThat(session.runningTaskActType).isEqualTo(runningTasks.first().activityType)
+ }
+
+ @Test
+ fun testHideDragSource_readDragFlag() {
+ // Set up running tasks
+ val runningTasks = listOf(
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
+ )
+ whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks)
+
+ // Simulate dragging an app with hide-drag-source set for the second (top most) app
+ val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT)
+ data.description.extras =
+ PersistableBundle().apply {
+ putInt(
+ EXTRA_HIDE_DRAG_SOURCE_TASK_ID,
+ runningTasks.last().taskId
+ )
+ }
+
+ // Start a new drag session
+ val session = DragSession(activityTaskManager, displayLayout, data, 0)
+ session.updateRunningTask()
+
+ assertThat(session.hideDragSourceTaskId).isEqualTo(runningTasks.last().taskId)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt
new file mode 100644
index 000000000000..1680d9b9a86c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.draganddrop
+
+import android.app.ActivityManager
+import android.app.PendingIntent
+import android.content.ClipData
+import android.content.ClipDescription
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.os.Process
+import java.util.Random
+import org.mockito.Mockito
+
+/**
+ * Convenience methods for drag tests.
+ */
+object DragTestUtils {
+ /**
+ * Creates an app-based clip data that is by default resizeable.
+ */
+ @JvmStatic
+ fun createAppClipData(mimeType: String): ClipData {
+ val clipDescription = ClipDescription(mimeType, arrayOf(mimeType))
+ val i = Intent()
+ when (mimeType) {
+ ClipDescription.MIMETYPE_APPLICATION_SHORTCUT -> {
+ i.putExtra(Intent.EXTRA_PACKAGE_NAME, "package")
+ i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcut_id")
+ }
+
+ ClipDescription.MIMETYPE_APPLICATION_TASK -> i.putExtra(Intent.EXTRA_TASK_ID, 12345)
+ ClipDescription.MIMETYPE_APPLICATION_ACTIVITY -> {
+ val pi = Mockito.mock(PendingIntent::class.java)
+ Mockito.doReturn(Process.myUserHandle()).`when`(pi).creatorUserHandle
+ i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi)
+ }
+ }
+ i.putExtra(Intent.EXTRA_USER, Process.myUserHandle())
+ val item = ClipData.Item(i)
+ item.activityInfo = ActivityInfo()
+ item.activityInfo.applicationInfo = ApplicationInfo()
+ val data = ClipData(clipDescription, item)
+ setClipDataResizeable(data, true)
+ return data
+ }
+
+ /**
+ * Creates an intent-based clip data that is by default resizeable.
+ */
+ @JvmStatic
+ fun createIntentClipData(intent: PendingIntent): ClipData {
+ val clipDescription = ClipDescription(
+ "Intent",
+ arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT)
+ )
+ val item = ClipData.Item.Builder()
+ .setIntentSender(intent.intentSender)
+ .build()
+ item.activityInfo = ActivityInfo()
+ item.activityInfo.applicationInfo = ApplicationInfo()
+ val data = ClipData(clipDescription, item)
+ setClipDataResizeable(data, true)
+ return data
+ }
+
+ /**
+ * Sets the given clip data to be resizeable.
+ */
+ @JvmStatic
+ fun setClipDataResizeable(data: ClipData, resizeable: Boolean) {
+ data.getItemAt(0).activityInfo.resizeMode = if (resizeable)
+ ActivityInfo.RESIZE_MODE_RESIZEABLE
+ else
+ ActivityInfo.RESIZE_MODE_UNRESIZEABLE
+ }
+
+ /**
+ * Creates a task info with the given params.
+ */
+ @JvmStatic
+ fun createTaskInfo(winMode: Int, actType: Int): ActivityManager.RunningTaskInfo {
+ return createTaskInfo(winMode, actType, false)
+ }
+
+ /**
+ * Creates a task info with the given params.
+ */
+ @JvmStatic
+ fun createTaskInfo(winMode: Int, actType: Int, alwaysOnTop: Boolean = false):
+ ActivityManager.RunningTaskInfo {
+ val info = ActivityManager.RunningTaskInfo()
+ info.taskId = Random().nextInt()
+ info.configuration.windowConfiguration.activityType = actType
+ info.configuration.windowConfiguration.windowingMode = winMode
+ info.configuration.windowConfiguration.isAlwaysOnTop = alwaysOnTop
+ info.isVisible = true
+ info.isResizeable = true
+ info.baseActivity = ComponentName(
+ "com.android.wm.shell",
+ ".ActivityWithMode$winMode"
+ )
+ info.baseIntent = Intent()
+ info.baseIntent.setComponent(info.baseActivity)
+ val activityInfo = ActivityInfo()
+ activityInfo.packageName = info.baseActivity!!.packageName
+ activityInfo.name = info.baseActivity!!.className
+ info.topActivityInfo = activityInfo
+ return info
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
index eb74218b866a..2cfce6933e1b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
@@ -22,11 +22,12 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
-import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData;
+import static com.android.wm.shell.draganddrop.DragTestUtils.createIntentClipData;
+import static com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo;
+import static com.android.wm.shell.draganddrop.DragTestUtils.setClipDataResizeable;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -55,11 +56,7 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
@@ -176,74 +173,11 @@ public class SplitDragPolicyTest extends ShellTestCase {
mMockitoSession.finishMocking();
}
- /**
- * Creates an app-based clip data that is by default resizeable.
- */
- private ClipData createAppClipData(String mimeType) {
- ClipDescription clipDescription = new ClipDescription(mimeType, new String[] { mimeType });
- Intent i = new Intent();
- switch (mimeType) {
- case MIMETYPE_APPLICATION_SHORTCUT:
- i.putExtra(Intent.EXTRA_PACKAGE_NAME, "package");
- i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcut_id");
- break;
- case MIMETYPE_APPLICATION_TASK:
- i.putExtra(Intent.EXTRA_TASK_ID, 12345);
- break;
- case MIMETYPE_APPLICATION_ACTIVITY:
- final PendingIntent pi = mock(PendingIntent.class);
- doReturn(android.os.Process.myUserHandle()).when(pi).getCreatorUserHandle();
- i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi);
- break;
- }
- i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
- ClipData.Item item = new ClipData.Item(i);
- item.setActivityInfo(new ActivityInfo());
- ClipData data = new ClipData(clipDescription, item);
- setClipDataResizeable(data, true);
- return data;
- }
-
- /**
- * Creates an intent-based clip data that is by default resizeable.
- */
- private ClipData createIntentClipData(PendingIntent intent) {
- ClipDescription clipDescription = new ClipDescription("Intent",
- new String[] { MIMETYPE_TEXT_INTENT });
- ClipData.Item item = new ClipData.Item.Builder()
- .setIntentSender(intent.getIntentSender())
- .build();
- ClipData data = new ClipData(clipDescription, item);
- return data;
- }
-
- private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType) {
- ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
- info.configuration.windowConfiguration.setActivityType(actType);
- info.configuration.windowConfiguration.setWindowingMode(winMode);
- info.isResizeable = true;
- info.baseActivity = new ComponentName(getInstrumentation().getContext(),
- ".ActivityWithMode" + winMode);
- info.baseIntent = new Intent();
- info.baseIntent.setComponent(info.baseActivity);
- ActivityInfo activityInfo = new ActivityInfo();
- activityInfo.packageName = info.baseActivity.getPackageName();
- activityInfo.name = info.baseActivity.getClassName();
- info.topActivityInfo = activityInfo;
- return info;
- }
-
private void setRunningTask(ActivityManager.RunningTaskInfo task) {
doReturn(Collections.singletonList(task)).when(mActivityTaskManager)
.getTasks(anyInt(), anyBoolean());
}
- private void setClipDataResizeable(ClipData data, boolean resizeable) {
- data.getItemAt(0).getActivityInfo().resizeMode = resizeable
- ? ActivityInfo.RESIZE_MODE_RESIZEABLE
- : ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- }
-
@Test
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
dragOverFullscreenHome_expectOnlyFullscreenTarget(mActivityClipData);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 72950a8dc139..6d37ed766aef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -28,8 +28,11 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import android.app.AppCompatTaskInfo;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -75,6 +78,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
.setContainerLayer()
.setName("FakeLeash")
.build();
+ mTaskInfo.appCompatTaskInfo = mock(AppCompatTaskInfo.class);
}
@Test
@@ -93,7 +97,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue1 = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -107,14 +112,16 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
oldAnimator.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
- TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -145,7 +152,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
// Fullscreen to PiP.
PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
- TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90);
+ TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90,
+ false /* alwaysAnimateTaskBounds */);
// Apply fraction 1 to compute the end value.
animator.applySurfaceControlTransaction(mLeash, tx, 1);
final Rect rotatedEndBounds = new Rect(endBounds);
@@ -157,7 +165,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
startBounds.set(0, 0, 1000, 500);
endBounds.set(200, 100, 400, 500);
animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds,
- endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270);
+ endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270,
+ false /* alwaysAnimateTaskBounds */);
animator.applySurfaceControlTransaction(mLeash, tx, 1);
rotatedEndBounds.set(endBounds);
rotateBounds(rotatedEndBounds, startBounds, ROTATION_270);
@@ -166,6 +175,37 @@ public class PipAnimationControllerTest extends ShellTestCase {
}
@Test
+ public void pipTransitionAnimator_rotatedEndValue_overrideMainWindowFrame() {
+ final SurfaceControl.Transaction tx = createMockSurfaceControlTransaction();
+ final Rect startBounds = new Rect(200, 700, 400, 800);
+ final Rect endBounds = new Rect(0, 0, 500, 1000);
+ mTaskInfo.topActivityMainWindowFrame = new Rect(0, 250, 1000, 500);
+
+ // Fullscreen task to PiP.
+ PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
+ TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90,
+ false /* alwaysAnimateTaskBounds */);
+ // Apply fraction 1 to compute the end value.
+ animator.applySurfaceControlTransaction(mLeash, tx, 1);
+
+ assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame,
+ animator.mCurrentValue);
+
+ // PiP to fullscreen.
+ mTaskInfo.topActivityMainWindowFrame = new Rect(0, 250, 1000, 500);
+ startBounds.set(0, 0, 1000, 500);
+ endBounds.set(200, 100, 400, 500);
+ animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds,
+ endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270,
+ false /* alwaysAnimateTaskBounds */);
+ animator.applySurfaceControlTransaction(mLeash, tx, 1);
+
+ assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame,
+ animator.mCurrentValue);
+ }
+
+ @Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() {
final Rect baseValue = new Rect(0, 0, 100, 100);
@@ -174,7 +214,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
animator.updateEndValue(endValue2);
@@ -188,7 +229,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
- TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
animator.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
@@ -207,4 +249,126 @@ public class PipAnimationControllerTest extends ShellTestCase {
verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo),
any(SurfaceControl.Transaction.class), eq(animator));
}
+
+ @Test
+ public void pipTransitionAnimator_overrideMainWindowFrame() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue = new Rect(100, 100, 200, 200);
+ mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100);
+ PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is overridden for in-PIP transition",
+ mTaskInfo.topActivityMainWindowFrame, animator.getBaseValue());
+ assertEquals("Expect start value is overridden for in-PIP transition",
+ mTaskInfo.topActivityMainWindowFrame, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for in-PIP transition",
+ endValue, animator.getEndValue());
+
+ animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue,
+ endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for leave-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for leave-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is overridden for leave-PIP transition",
+ mTaskInfo.topActivityMainWindowFrame, animator.getEndValue());
+ }
+
+ @Test
+ public void pipTransitionAnimator_animateTaskBounds() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue = new Rect(100, 100, 200, 200);
+ mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100);
+ PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ true /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for in-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for in-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for in-PIP transition",
+ endValue, animator.getEndValue());
+
+ animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue,
+ endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0,
+ true /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for leave-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for leave-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for leave-PIP transition",
+ endValue, animator.getEndValue());
+ }
+
+ @Test
+ public void pipTransitionAnimator_letterboxed_animateTaskBounds() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue = new Rect(100, 100, 200, 200);
+ mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100);
+ doReturn(true).when(mTaskInfo.appCompatTaskInfo).isTopActivityLetterboxed();
+ PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for in-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for in-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for in-PIP transition",
+ endValue, animator.getEndValue());
+
+ animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue,
+ endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for leave-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for leave-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for leave-PIP transition",
+ endValue, animator.getEndValue());
+ }
+
+ @Test
+ public void pipTransitionAnimator_sizeCompat_animateTaskBounds() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue = new Rect(100, 100, 200, 200);
+ mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100);
+ doReturn(true).when(mTaskInfo.appCompatTaskInfo).isTopActivityInSizeCompat();
+ PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for in-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for in-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for in-PIP transition",
+ endValue, animator.getEndValue());
+
+ animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue,
+ endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0,
+ false /* alwaysAnimateTaskBounds */);
+
+ assertEquals("Expect base value is not overridden for leave-PIP transition",
+ baseValue, animator.getBaseValue());
+ assertEquals("Expect start value is not overridden for leave-PIP transition",
+ startValue, animator.getStartValue());
+ assertEquals("Expect end value is not overridden for leave-PIP transition",
+ endValue, animator.getEndValue());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index bcb7461bfae7..5f58265b45f5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -47,6 +47,7 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.MockSurfaceControlHelper;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -61,6 +62,7 @@ import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
+import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -90,6 +92,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
+ @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository;
+ @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
@Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
private TestShellExecutor mMainExecutor;
@@ -120,8 +124,10 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
mMockPipParamsChangedForwarder, mMockOptionalSplitScreen,
- Optional.empty() /* pipPerfHintControllerOptional */, mMockDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor);
+ Optional.empty() /* pipPerfHintControllerOptional */,
+ mMockOptionalDesktopRepository, mRootTaskDisplayAreaOrganizer,
+ mMockDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer,
+ mMainExecutor);
mMainExecutor.flushAll();
preparePipTaskOrg();
preparePipSurfaceTransactionHelper();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
new file mode 100644
index 000000000000..a4008c1e6995
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.animation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip2.phone.PipAppIconOverlay;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test again {@link PipEnterAnimator}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipEnterAnimatorTest {
+
+ @Mock private Context mMockContext;
+
+ @Mock private Resources mMockResources;
+
+ @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+
+ @Mock private SurfaceControl.Transaction mMockAnimateTransaction;
+
+ @Mock private SurfaceControl.Transaction mMockStartTransaction;
+
+ @Mock private SurfaceControl.Transaction mMockFinishTransaction;
+
+ @Mock private Runnable mMockStartCallback;
+
+ @Mock private Runnable mMockEndCallback;
+
+ @Mock private PipAppIconOverlay mMockPipAppIconOverlay;
+
+ @Mock private SurfaceControl mMockAppIconOverlayLeash;
+
+ @Mock private ActivityInfo mMockActivityInfo;
+
+ @Surface.Rotation private int mRotation;
+ private SurfaceControl mTestLeash;
+ private Rect mEndBounds;
+ private PipEnterAnimator mPipEnterAnimator;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getInteger(anyInt())).thenReturn(0);
+ when(mMockFactory.getTransaction()).thenReturn(mMockAnimateTransaction);
+ when(mMockAnimateTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockAnimateTransaction);
+ when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockStartTransaction);
+ when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockPipAppIconOverlay.getLeash()).thenReturn(mMockAppIconOverlayLeash);
+
+ mTestLeash = new SurfaceControl.Builder()
+ .setContainerLayer()
+ .setName("PipExpandAnimatorTest")
+ .setCallsite("PipExpandAnimatorTest")
+ .build();
+ }
+
+ @Test
+ public void setAnimationStartCallback_enter_callbackStartCallback() {
+ mRotation = Surface.ROTATION_0;
+ mEndBounds = new Rect(100, 100, 500, 500);
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mEndBounds, mRotation);
+ mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ mPipEnterAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipEnterAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipEnterAnimator.start();
+ mPipEnterAnimator.pause();
+ });
+
+ verify(mMockStartCallback).run();
+ verifyZeroInteractions(mMockEndCallback);
+ }
+
+ @Test
+ public void setAnimationEndCallback_enter_callbackStartAndEndCallback() {
+ mRotation = Surface.ROTATION_0;
+ mEndBounds = new Rect(100, 100, 500, 500);
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mEndBounds, mRotation);
+ mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+ mPipEnterAnimator.setAnimationStartCallback(mMockStartCallback);
+ mPipEnterAnimator.setAnimationEndCallback(mMockEndCallback);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mPipEnterAnimator.start();
+ mPipEnterAnimator.end();
+ });
+
+ verify(mMockStartCallback).run();
+ verify(mMockEndCallback).run();
+ }
+
+ @Test
+ public void setAppIconContentOverlay_thenGetContentOverlayLeash_returnOverlayLeash() {
+ mRotation = Surface.ROTATION_0;
+ mEndBounds = new Rect(100, 100, 500, 500);
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mEndBounds, mRotation);
+ mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+ mPipEnterAnimator.setPipAppIconOverlaySupplier(
+ (context, appBounds, endBounds, icon, iconSize) -> mMockPipAppIconOverlay);
+
+ mPipEnterAnimator.setAppIconContentOverlay(mMockContext, mEndBounds, mEndBounds,
+ mMockActivityInfo, 64 /* iconSize */);
+
+ assertEquals(mPipEnterAnimator.getContentOverlayLeash(), mMockAppIconOverlayLeash);
+ }
+
+ @Test
+ public void setAppIconContentOverlay_thenClearAppIconOverlay_returnNullLeash() {
+ mRotation = Surface.ROTATION_0;
+ mEndBounds = new Rect(100, 100, 500, 500);
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mEndBounds, mRotation);
+ mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+ mPipEnterAnimator.setPipAppIconOverlaySupplier(
+ (context, appBounds, endBounds, icon, iconSize) -> mMockPipAppIconOverlay);
+
+ mPipEnterAnimator.setAppIconContentOverlay(mMockContext, mEndBounds, mEndBounds,
+ mMockActivityInfo, 64 /* iconSize */);
+ mPipEnterAnimator.clearAppIconOverlay();
+
+ assertNull(mPipEnterAnimator.getContentOverlayLeash());
+ }
+
+ @Test
+ public void onEnterAnimationUpdate_withContentOverlay_animateOverlay() {
+ mRotation = Surface.ROTATION_0;
+ mEndBounds = new Rect(100, 100, 500, 500);
+ mPipEnterAnimator = new PipEnterAnimator(mMockContext, mTestLeash,
+ mMockStartTransaction, mMockFinishTransaction,
+ mEndBounds, mRotation);
+ mPipEnterAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+ mPipEnterAnimator.setPipAppIconOverlaySupplier(
+ (context, appBounds, endBounds, icon, iconSize) -> mMockPipAppIconOverlay);
+
+ float fraction = 0.5f;
+ mPipEnterAnimator.setAppIconContentOverlay(mMockContext, mEndBounds, mEndBounds,
+ mMockActivityInfo, 64 /* iconSize */);
+ mPipEnterAnimator.onEnterAnimationUpdate(fraction, mMockAnimateTransaction);
+
+ verify(mMockPipAppIconOverlay).onAnimationUpdate(
+ eq(mMockAnimateTransaction), anyFloat(), eq(fraction), eq(mEndBounds));
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index e19a10a78417..b816f0ef041e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -93,6 +93,17 @@ public class PipExpandAnimatorTest {
.thenReturn(mMockTransaction);
when(mMockTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
.thenReturn(mMockTransaction);
+ // No-op on the mMockStartTransaction
+ when(mMockStartTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setCrop(any(SurfaceControl.class), any(Rect.class)))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(mMockFinishTransaction);
+ when(mMockStartTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(mMockFinishTransaction);
// Do the same for mMockFinishTransaction
when(mMockFinishTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
.thenReturn(mMockFinishTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
new file mode 100644
index 000000000000..cab625216236
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.MatchersKt.eq;
+import static org.mockito.kotlin.VerificationKt.times;
+import static org.mockito.kotlin.VerificationKt.verify;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test against {@link PipScheduler}
+ */
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipSchedulerTest {
+ private static final int TEST_RESIZE_DURATION = 1;
+ private static final Rect TEST_STARTING_BOUNDS = new Rect(0, 0, 10, 10);
+ private static final Rect TEST_BOUNDS = new Rect(0, 0, 20, 20);
+
+ @Mock private Context mMockContext;
+ @Mock private Resources mMockResources;
+ @Mock private PipBoundsState mMockPipBoundsState;
+ @Mock private ShellExecutor mMockMainExecutor;
+ @Mock private PipTransitionState mMockPipTransitionState;
+ @Mock private PipTransitionController mMockPipTransitionController;
+ @Mock private Runnable mMockUpdateMovementBoundsRunnable;
+ @Mock private WindowContainerToken mMockPipTaskToken;
+ @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+ @Mock private SurfaceControl.Transaction mMockTransaction;
+ @Mock private PipAlphaAnimator mMockAlphaAnimator;
+
+ @Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
+ @Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor;
+
+ private PipScheduler mPipScheduler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getInteger(anyInt())).thenReturn(0);
+ when(mMockPipBoundsState.getBounds()).thenReturn(TEST_STARTING_BOUNDS);
+ when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
+ when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(mMockTransaction);
+
+ mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
+ mMockPipTransitionState);
+ mPipScheduler.setPipTransitionController(mMockPipTransitionController);
+ mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
+ mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) ->
+ mMockAlphaAnimator);
+
+ SurfaceControl testLeash = new SurfaceControl.Builder()
+ .setContainerLayer()
+ .setName("PipSchedulerTest")
+ .setCallsite("PipSchedulerTest")
+ .build();
+ when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(testLeash);
+ }
+
+ @Test
+ public void scheduleExitPipViaExpand_nullTaskToken_noop() {
+ setNullPipTaskToken();
+
+ mPipScheduler.scheduleExitPipViaExpand();
+
+ verify(mMockMainExecutor, never()).execute(any());
+ }
+
+ @Test
+ public void scheduleExitPipViaExpand_exitTransitionCalled() {
+ setMockPipTaskToken();
+
+ mPipScheduler.scheduleExitPipViaExpand();
+
+ verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
+ assertNotNull(mRunnableArgumentCaptor.getValue());
+ mRunnableArgumentCaptor.getValue().run();
+
+ verify(mMockPipTransitionController, times(1))
+ .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull());
+ }
+
+ @Test
+ public void removePipAfterAnimation() {
+ //TODO: Update once this is changed to run animation as part of transition
+ setMockPipTaskToken();
+
+ mPipScheduler.removePipAfterAnimation();
+ verify(mMockAlphaAnimator, times(1))
+ .setAnimationEndCallback(mRunnableArgumentCaptor.capture());
+ assertNotNull(mRunnableArgumentCaptor.getValue());
+ verify(mMockAlphaAnimator, times(1)).start();
+
+ mRunnableArgumentCaptor.getValue().run();
+
+ verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
+ assertNotNull(mRunnableArgumentCaptor.getValue());
+
+ mRunnableArgumentCaptor.getValue().run();
+
+ verify(mMockPipTransitionController, times(1))
+ .startExitTransition(eq(TRANSIT_REMOVE_PIP), any(), isNull());
+ }
+
+ @Test
+ public void scheduleAnimateResizePip_bounds_nullTaskToken_noop() {
+ setNullPipTaskToken();
+
+ mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS);
+
+ verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+ }
+
+ @Test
+ public void scheduleAnimateResizePip_boundsConfig_nullTaskToken_noop() {
+ setNullPipTaskToken();
+
+ mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true);
+
+ verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+ }
+
+ @Test
+ public void scheduleAnimateResizePip_boundsConfig_setsConfigAtEnd() {
+ setMockPipTaskToken();
+ when(mMockPipTransitionState.isInPip()).thenReturn(true);
+
+ mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true);
+
+ verify(mMockPipTransitionController, times(1))
+ .startResizeTransition(mWctArgumentCaptor.capture(), anyInt());
+ assertNotNull(mWctArgumentCaptor.getValue());
+ assertNotNull(mWctArgumentCaptor.getValue().getChanges());
+ boolean hasConfigAtEndChange = false;
+ for (WindowContainerTransaction.Change change :
+ mWctArgumentCaptor.getValue().getChanges().values()) {
+ if (change.getConfigAtTransitionEnd()) {
+ hasConfigAtEndChange = true;
+ break;
+ }
+ }
+ assertTrue(hasConfigAtEndChange);
+ }
+
+ @Test
+ public void scheduleAnimateResizePip_boundsConfigDuration_nullTaskToken_noop() {
+ setNullPipTaskToken();
+
+ mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
+
+ verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+ }
+
+ @Test
+ public void scheduleAnimateResizePip_notInPip_noop() {
+ setMockPipTaskToken();
+ when(mMockPipTransitionState.isInPip()).thenReturn(false);
+
+ mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
+
+ verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt());
+ }
+
+ @Test
+ public void scheduleAnimateResizePip_resizeTransition() {
+ setMockPipTaskToken();
+ when(mMockPipTransitionState.isInPip()).thenReturn(true);
+
+ mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION);
+
+ verify(mMockPipTransitionController, times(1))
+ .startResizeTransition(any(), eq(TEST_RESIZE_DURATION));
+ }
+
+ @Test
+ public void scheduleUserResizePip_emptyBounds_noop() {
+ setMockPipTaskToken();
+
+ mPipScheduler.scheduleUserResizePip(new Rect());
+
+ verify(mMockTransaction, never()).apply();
+ }
+
+ @Test
+ public void scheduleUserResizePip_rotation_emptyBounds_noop() {
+ setMockPipTaskToken();
+
+ mPipScheduler.scheduleUserResizePip(new Rect(), 90);
+
+ verify(mMockTransaction, never()).apply();
+ }
+
+ @Test
+ public void scheduleUserResizePip_applyTransaction() {
+ setMockPipTaskToken();
+
+ mPipScheduler.scheduleUserResizePip(TEST_BOUNDS, 90);
+
+ verify(mMockTransaction, times(1)).apply();
+ }
+
+ @Test
+ public void finishResize_movementBoundsRunnableCalled() {
+ mPipScheduler.setUpdateMovementBoundsRunnable(mMockUpdateMovementBoundsRunnable);
+ mPipScheduler.scheduleFinishResizePip(TEST_BOUNDS);
+
+ verify(mMockUpdateMovementBoundsRunnable, times(1)).run();
+ }
+
+ private void setNullPipTaskToken() {
+ when(mMockPipTransitionState.getPipTaskToken()).thenReturn(null);
+ }
+
+ private void setMockPipTaskToken() {
+ when(mMockPipTransitionState.getPipTaskToken()).thenReturn(mMockPipTaskToken);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
new file mode 100644
index 000000000000..89cb729d17b5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import static com.android.wm.shell.pip2.phone.PipTaskListener.ANIMATING_ASPECT_RATIO_CHANGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.MatchersKt.eq;
+import static org.mockito.kotlin.VerificationKt.clearInvocations;
+import static org.mockito.kotlin.VerificationKt.times;
+import static org.mockito.kotlin.VerificationKt.verify;
+import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.app.RemoteAction;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Rational;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.pip2.animation.PipResizeAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test against {@link PipTaskListener}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipTaskListenerTest {
+
+ @Mock private Context mMockContext;
+ @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock private PipTransitionState mMockPipTransitionState;
+ @Mock private SurfaceControl mMockLeash;
+ @Mock private PipScheduler mMockPipScheduler;
+ @Mock private PipBoundsState mMockPipBoundsState;
+ @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+ @Mock private ShellExecutor mMockShellExecutor;
+
+ @Mock private Icon mMockIcon;
+ @Mock private PendingIntent mMockPendingIntent;
+
+ @Mock private PipTaskListener.PipParamsChangedCallback mMockPipParamsChangedCallback;
+
+ @Mock private PipResizeAnimator mMockPipResizeAnimator;
+
+ private ArgumentCaptor<List<RemoteAction>> mRemoteActionListCaptor;
+
+ private PipTaskListener mPipTaskListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRemoteActionListCaptor = ArgumentCaptor.forClass(List.class);
+ when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(mMockLeash);
+ }
+
+ @Test
+ public void constructor_addPipTransitionStateChangedListener() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+
+ verify(mMockPipTransitionState).addPipTransitionStateChangedListener(eq(mPipTaskListener));
+ }
+
+ @Test
+ public void setPictureInPictureParams_updatePictureInPictureParams() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ PictureInPictureParams params = mPipTaskListener.getPictureInPictureParams();
+ assertEquals(aspectRatio, params.getAspectRatio());
+ assertTrue(params.hasSetActions());
+ assertEquals(1, params.getActions().size());
+ assertEquals(action1, params.getActions().get(0).getTitle());
+ }
+
+ @Test
+ public void setPictureInPictureParams_withActionsChanged_callbackActionsChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ action1 = "modified action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ verify(mMockPipParamsChangedCallback).onActionsChanged(
+ mRemoteActionListCaptor.capture(), any());
+ assertEquals(1, mRemoteActionListCaptor.getValue().size());
+ assertEquals(action1, mRemoteActionListCaptor.getValue().get(0).getTitle());
+ }
+
+ @Test
+ public void setPictureInPictureParams_withoutActionsChanged_doesNotCallbackActionsChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ String action1 = "action1";
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+ aspectRatio, action1));
+
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ }
+
+ @Test
+ public void onTaskInfoChanged_withActionsChanged_callbackActionsChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ clearInvocations(mMockPipBoundsState);
+ action1 = "modified action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ verify(mMockPipTransitionState, times(0))
+ .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ verify(mMockPipParamsChangedCallback).onActionsChanged(
+ mRemoteActionListCaptor.capture(), any());
+ assertEquals(1, mRemoteActionListCaptor.getValue().size());
+ assertEquals(action1, mRemoteActionListCaptor.getValue().get(0).getTitle());
+ }
+
+ @Test
+ public void onTaskInfoChanged_withAspectRatioChanged_callbackAspectRatioChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ clearInvocations(mMockPipBoundsState);
+ aspectRatio = new Rational(16, 9);
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ }
+
+ @Test
+ public void onTaskInfoChanged_withoutParamsChanged_doesNotCallbackAspectRatioChanged() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verify(mMockPipTransitionState, times(0))
+ .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_scheduledBoundsChangeWithAspectRatioChange_schedule() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, true);
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED, PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extras);
+
+ verify(mMockPipScheduler).scheduleAnimateResizePip(any(), anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_scheduledBoundsChangeWithoutAspectRatioChange_noop() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, false);
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED,
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ extras);
+
+ verifyZeroInteractions(mMockPipScheduler);
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_changingPipBoundsWaitAspectRatioChange_animate() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, true);
+ extras.putParcelable(PipTransition.PIP_DESTINATION_BOUNDS,
+ new Rect(0, 0, 100, 100));
+ when(mMockPipBoundsState.getBounds()).thenReturn(new Rect(0, 0, 200, 200));
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED,
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ extras);
+ mPipTaskListener.setPipResizeAnimatorSupplier(
+ (context, leash, startTx, finishTx, baseBounds, startBounds, endBounds,
+ duration, delta) -> mMockPipResizeAnimator);
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ PipTransitionState.CHANGING_PIP_BOUNDS,
+ extras);
+
+ verify(mMockPipResizeAnimator, times(1)).start();
+ }
+
+ @Test
+ public void onPipTransitionStateChanged_changingPipBoundsNotAspectRatioChange_noop() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ Bundle extras = new Bundle();
+ extras.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, false);
+ extras.putParcelable(PipTransition.PIP_DESTINATION_BOUNDS,
+ new Rect(0, 0, 100, 100));
+ when(mMockPipBoundsState.getBounds()).thenReturn(new Rect(0, 0, 200, 200));
+
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED,
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ extras);
+ mPipTaskListener.setPipResizeAnimatorSupplier(
+ (context, leash, startTx, finishTx, baseBounds, startBounds, endBounds,
+ duration, delta) -> mMockPipResizeAnimator);
+ mPipTaskListener.onPipTransitionStateChanged(
+ PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
+ PipTransitionState.CHANGING_PIP_BOUNDS,
+ extras);
+
+ verify(mMockPipResizeAnimator, times(0)).start();
+ }
+
+ private PictureInPictureParams getPictureInPictureParams(Rational aspectRatio,
+ String... actions) {
+ final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
+ builder.setAspectRatio(aspectRatio);
+ final List<RemoteAction> remoteActions = new ArrayList<>();
+ for (String action : actions) {
+ remoteActions.add(new RemoteAction(mMockIcon, action, action, mMockPendingIntent));
+ }
+ if (!remoteActions.isEmpty()) {
+ builder.setActions(remoteActions);
+ }
+ return builder.build();
+ }
+
+ private ActivityManager.RunningTaskInfo getTaskInfo(Rational aspectRatio,
+ String... actions) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.pictureInPictureParams = getPictureInPictureParams(aspectRatio, actions);
+ return taskInfo;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index 0c100fca2036..2b30bc360d06 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.recents
import android.app.ActivityManager
+import android.app.TaskInfo
import android.graphics.Rect
import android.os.Parcel
import android.testing.AndroidTestingRunner
@@ -24,11 +25,10 @@ import android.window.IWindowContainerToken
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.shared.GroupedRecentTaskInfo
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.CREATOR
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SINGLE
-import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SPLIT
+import com.android.wm.shell.shared.GroupedTaskInfo
+import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM
+import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN
+import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT
import com.android.wm.shell.shared.split.SplitBounds
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
import com.google.common.truth.Correspondence
@@ -39,15 +39,15 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.mock
/**
- * Tests for [GroupedRecentTaskInfo]
+ * Tests for [GroupedTaskInfo]
*/
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class GroupedRecentTaskInfoTest : ShellTestCase() {
+class GroupedTaskInfoTest : ShellTestCase() {
@Test
fun testSingleTask_hasCorrectType() {
- assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_SINGLE)
+ assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_FULLSCREEN)
}
@Test
@@ -117,8 +117,9 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
- assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SINGLE)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FULLSCREEN)
assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
assertThat(recentTaskInfoParcel.taskInfo2).isNull()
}
@@ -130,7 +131,8 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
@@ -146,11 +148,12 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
// Only compare task ids
- val taskIdComparator = Correspondence.transforming<ActivityManager.RecentTaskInfo, Int>(
+ val taskIdComparator = Correspondence.transforming<TaskInfo, Int>(
{ it?.taskId }, "has taskId of"
)
assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
@@ -167,7 +170,8 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
parcel.setDataPosition(0)
// Read the object back from the parcel
- val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ val recentTaskInfoParcel: GroupedTaskInfo =
+ GroupedTaskInfo.CREATOR.createFromParcel(parcel)
assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
assertThat(recentTaskInfoParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
}
@@ -177,24 +181,24 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
}
- private fun singleTaskGroupInfo(): GroupedRecentTaskInfo {
+ private fun singleTaskGroupInfo(): GroupedTaskInfo {
val task = createTaskInfo(id = 1)
- return GroupedRecentTaskInfo.forSingleTask(task)
+ return GroupedTaskInfo.forFullscreenTasks(task)
}
- private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
+ private fun splitTasksGroupInfo(): GroupedTaskInfo {
val task1 = createTaskInfo(id = 1)
val task2 = createTaskInfo(id = 2)
val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
- return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
+ return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
}
private fun freeformTasksGroupInfo(
freeformTaskIds: Array<Int>,
minimizedTaskIds: Array<Int> = emptyArray()
- ): GroupedRecentTaskInfo {
- return GroupedRecentTaskInfo.forFreeformTasks(
- freeformTaskIds.map { createTaskInfo(it) }.toTypedArray(),
+ ): GroupedTaskInfo {
+ return GroupedTaskInfo.forFreeformTasks(
+ freeformTaskIds.map { createTaskInfo(it) }.toList(),
minimizedTaskIds.toSet())
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 9b73d53e0639..dede583ca970 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -46,6 +46,7 @@ import static org.mockito.Mockito.when;
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityTaskManager;
import android.app.KeyguardManager;
import android.content.ComponentName;
@@ -71,7 +72,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
-import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.ShellSharedConstants;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.split.SplitBounds;
@@ -193,8 +194,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddRemoveSplitNotifyChange() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(SplitBounds.class));
@@ -207,8 +208,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testAddSameSplitBoundsInfoSkipNotifyChange() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
// Verify only one update if the split info is the same
@@ -223,13 +224,13 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
setRawList(t1, t2, t3);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
assertGroupedTasksListEquals(recentTasks,
t1.taskId, -1,
t2.taskId, -1,
@@ -238,12 +239,12 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_withPairs() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
- ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t6 = makeTaskInfo(6);
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
@@ -255,8 +256,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
assertGroupedTasksListEquals(recentTasks,
t1.taskId, -1,
t2.taskId, t4.taskId,
@@ -267,14 +268,14 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
@SuppressWarnings("unchecked")
- final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
- Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
- ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ final List<GroupedTaskInfo>[] recentTasks = new List[1];
+ Consumer<List<GroupedTaskInfo>> consumer = argument -> recentTasks[0] = argument;
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t6 = makeTaskInfo(6);
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
@@ -287,7 +288,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
mRecentTasksController.asRecentTasks()
- .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
+ .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run,
+ consumer);
mMainExecutor.flushAll();
assertGroupedTasksListEquals(recentTasks[0],
@@ -299,28 +301,28 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_groupFreeformTasks() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// 2 freeform tasks should be grouped into one, 3 total recents entries
assertEquals(3, recentTasks.size());
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
- GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
- GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+ GroupedTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedTaskInfo singleGroup2 = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
// Check freeform group entries
assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
@@ -333,11 +335,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_freeformTaskOrder() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
setRawList(t1, t2, t3, t4, t5);
SplitBounds pair1Bounds =
@@ -347,19 +349,19 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
when(mDesktopRepository.isActiveTask(5)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// 2 split screen tasks grouped, 2 freeform tasks grouped, 3 total recents entries
assertEquals(3, recentTasks.size());
- GroupedRecentTaskInfo splitGroup = recentTasks.get(0);
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(1);
- GroupedRecentTaskInfo singleGroup = recentTasks.get(2);
+ GroupedTaskInfo splitGroup = recentTasks.get(0);
+ GroupedTaskInfo freeformGroup = recentTasks.get(1);
+ GroupedTaskInfo singleGroup = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedRecentTaskInfo.TYPE_SPLIT, splitGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_SPLIT, splitGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup.getType());
// Check freeform group entries
assertEquals(t3, freeformGroup.getTaskInfoList().get(0));
@@ -378,24 +380,24 @@ public class RecentTasksControllerTest extends ShellTestCase {
ExtendedMockito.doReturn(false)
.when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// Expect no grouping of tasks
assertEquals(4, recentTasks.size());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(0).getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(1).getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(2).getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, recentTasks.get(3).getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(0).getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(1).getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(2).getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(3).getType());
assertEquals(t1, recentTasks.get(0).getTaskInfo1());
assertEquals(t2, recentTasks.get(1).getTaskInfo1());
@@ -405,11 +407,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testGetRecentTasks_proto2Enabled_includesMinimizedFreeformTasks() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
- ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
- ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t4 = makeTaskInfo(4);
+ RecentTaskInfo t5 = makeTaskInfo(5);
setRawList(t1, t2, t3, t4, t5);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
@@ -417,19 +419,19 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mDesktopRepository.isActiveTask(5)).thenReturn(true);
when(mDesktopRepository.isMinimizedTask(3)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
// 3 freeform tasks should be grouped into one, 2 single tasks, 3 total recents entries
assertEquals(3, recentTasks.size());
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
- GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
- GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+ GroupedTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedTaskInfo singleGroup2 = recentTasks.get(2);
// Check that groups have expected types
- assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
- assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
+ assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
// Check freeform group entries
assertEquals(3, freeformGroup.getTaskInfoList().size());
@@ -445,8 +447,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
public void testGetRecentTasks_hasDesktopTasks_persistenceEnabled_freeformTaskHaveBoundsSet() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
t1.lastNonFullscreenBounds = new Rect(100, 200, 300, 400);
t2.lastNonFullscreenBounds = new Rect(150, 250, 350, 450);
@@ -455,11 +457,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
when(mDesktopRepository.isActiveTask(2)).thenReturn(true);
- ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
- MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ ArrayList<GroupedTaskInfo> recentTasks =
+ mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
assertEquals(1, recentTasks.size());
- GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedTaskInfo freeformGroup = recentTasks.get(0);
// Check bounds
assertEquals(t1.lastNonFullscreenBounds, freeformGroup.getTaskInfoList().get(
@@ -478,9 +480,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testRemovedTaskRemovesSplit() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
- ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
- ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t2 = makeTaskInfo(2);
+ RecentTaskInfo t3 = makeTaskInfo(3);
setRawList(t1, t2, t3);
// Add a pair
@@ -500,7 +502,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Test
public void testTaskWindowingModeChangedNotifiesChange() {
- ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ RecentTaskInfo t1 = makeTaskInfo(1);
setRawList(t1);
// Remove one of the tasks and ensure the pair is removed
@@ -607,7 +609,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo);
- verify(mRecentTasksListener).onTaskMovedToFront(taskInfo);
+ GroupedTaskInfo runningTask = GroupedTaskInfo.forFullscreenTasks(taskInfo);
+ verify(mRecentTasksListener).onTaskMovedToFront(eq(new GroupedTaskInfo[] { runningTask }));
}
@Test
@@ -656,8 +659,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
/**
* Helper to create a task with a given task id.
*/
- private ActivityManager.RecentTaskInfo makeTaskInfo(int taskId) {
- ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
+ private RecentTaskInfo makeTaskInfo(int taskId) {
+ RecentTaskInfo info = new RecentTaskInfo();
info.taskId = taskId;
info.lastNonFullscreenBounds = new Rect();
return info;
@@ -676,10 +679,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
/**
* Helper to set the raw task list on the controller.
*/
- private ArrayList<ActivityManager.RecentTaskInfo> setRawList(
- ActivityManager.RecentTaskInfo... tasks) {
- ArrayList<ActivityManager.RecentTaskInfo> rawList = new ArrayList<>();
- for (ActivityManager.RecentTaskInfo task : tasks) {
+ private ArrayList<RecentTaskInfo> setRawList(
+ RecentTaskInfo... tasks) {
+ ArrayList<RecentTaskInfo> rawList = new ArrayList<>();
+ for (RecentTaskInfo task : tasks) {
rawList.add(task);
}
doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
@@ -693,11 +696,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
- private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
+ private void assertGroupedTasksListEquals(List<GroupedTaskInfo> recentTasks,
int... expectedTaskIds) {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
- GroupedRecentTaskInfo pair = recentTasks.get(i);
+ GroupedTaskInfo pair = recentTasks.get(i);
int taskId1 = pair.getTaskInfo1().taskId;
flattenedTaskIds[2 * i] = taskId1;
flattenedTaskIds[2 * i + 1] = pair.getTaskInfo2() != null
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index ef3af8e7bdac..966651f19711 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -217,7 +217,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
// Put the same component to the top running task
ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
@@ -238,7 +237,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
// Put the same component to the top running task
ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any());
// Put the same component into a task in the background
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 6cde0569796d..2442a55d78d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -363,6 +364,25 @@ public class ShellTransitionTests extends ShellTestCase {
}
@Test
+ public void testTransitionFilterWindowingMode() {
+ TransitionFilter filter = new TransitionFilter();
+ filter.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ filter.mRequirements[0].mWindowingMode = WINDOWING_MODE_FREEFORM;
+ filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+ final TransitionInfo fullscreenStd = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN, createTaskInfo(
+ 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build();
+ assertFalse(filter.matches(fullscreenStd));
+
+ final TransitionInfo freeformStd = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN, createTaskInfo(
+ 1, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD)).build();
+ assertTrue(filter.matches(freeformStd));
+ }
+
+ @Test
public void testTransitionFilterMultiRequirement() {
// filter that requires at-least one opening and one closing app
TransitionFilter filter = new TransitionFilter();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
index 5ebf5170bf86..59141ca39487 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
@@ -19,6 +19,7 @@ package com.android.wm.shell.windowdecor
import android.app.ActivityManager
import android.app.WindowConfiguration
import android.content.ComponentName
+import android.graphics.Region
import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.InsetsState
@@ -33,6 +34,9 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
class CaptionWindowDecorationTests : ShellTestCase() {
+
+ private val exclusionRegion = Region.obtain()
+
@Test
fun updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() {
val taskInfo = createTaskInfo()
@@ -50,7 +54,8 @@ class CaptionWindowDecorationTests : ShellTestCase() {
true /* isStatusBarVisible */,
false /* isKeyguardVisibleAndOccluded */,
InsetsState(),
- true /* hasGlobalFocus */
+ true /* hasGlobalFocus */,
+ exclusionRegion
)
Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isTrue()
@@ -72,7 +77,8 @@ class CaptionWindowDecorationTests : ShellTestCase() {
true /* isStatusBarVisible */,
false /* isKeyguardVisibleAndOccluded */,
InsetsState(),
- true /* hasGlobalFocus */
+ true /* hasGlobalFocus */,
+ exclusionRegion
)
Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isFalse()
@@ -90,7 +96,8 @@ class CaptionWindowDecorationTests : ShellTestCase() {
true /* isStatusBarVisible */,
false /* isKeyguardVisibleAndOccluded */,
InsetsState(),
- true /* hasGlobalFocus */
+ true /* hasGlobalFocus */,
+ exclusionRegion
)
Truth.assertThat(relayoutParams.mOccludingCaptionElements.size).isEqualTo(2)
Truth.assertThat(relayoutParams.mOccludingCaptionElements[0].mAlignment).isEqualTo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 03aab18d8d87..be664f86e9f5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -30,6 +30,7 @@ import android.content.Intent
import android.content.Intent.ACTION_MAIN
import android.content.pm.ActivityInfo
import android.graphics.Rect
+import android.graphics.Region
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.hardware.input.InputManager
@@ -48,6 +49,7 @@ import android.testing.TestableLooper.RunWithLooper
import android.util.SparseArray
import android.view.Choreographer
import android.view.Display.DEFAULT_DISPLAY
+import android.view.ISystemGestureExclusionListener
import android.view.IWindowManager
import android.view.InputChannel
import android.view.InputMonitor
@@ -84,7 +86,6 @@ import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiInstanceHelper
-import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
@@ -131,6 +132,7 @@ import org.mockito.Mockito.times
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.verify
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argThat
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doNothing
@@ -175,7 +177,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Mock private lateinit var mockInputMonitorFactory:
DesktopModeWindowDecorViewModel.InputMonitorFactory
@Mock private lateinit var mockShellController: ShellController
- @Mock private lateinit var mockShellExecutor: ShellExecutor
+ private val testShellExecutor = TestShellExecutor()
@Mock private lateinit var mockAppHeaderViewHolderFactory: AppHeaderViewHolder.Factory
@Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
@@ -230,13 +232,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
spyContext = spy(mContext)
doNothing().`when`(spyContext).startActivity(any())
- shellInit = ShellInit(mockShellExecutor)
+ shellInit = ShellInit(testShellExecutor)
windowDecorByTaskIdSpy.clear()
spyContext.addMockSystemService(InputManager::class.java, mockInputManager)
desktopModeEventLogger = mock<DesktopModeEventLogger>()
desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
spyContext,
- mockShellExecutor,
+ testShellExecutor,
mockMainHandler,
mockMainChoreographer,
bgExecutor,
@@ -478,25 +480,10 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
+ fun testDecorationIsNotCreatedForTopTranslucentActivities() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
isTopActivityTransparent = true
- isTopActivityStyleFloating = true
- numActivities = 1
- }
- doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
- setUpMockDecorationsForTasks(task)
-
- onTaskOpening(task)
- assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
- fun testDecorationIsNotCreatedForTopTranslucentActivitiesWithoutStyleFloating() {
- val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
- isTopActivityTransparent = true
- isTopActivityStyleFloating = false
+ isTopActivityNoDisplay = false
numActivities = 1
}
onTaskOpening(task)
@@ -507,13 +494,14 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun testDecorationIsNotCreatedForSystemUIActivities() {
- val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN)
-
// Set task as systemUI package
val systemUIPackageName = context.resources.getString(
com.android.internal.R.string.config_systemUi)
val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
- task.baseActivity = baseComponent
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
onTaskOpening(task)
@@ -1321,10 +1309,101 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
verify(decor).closeMaximizeMenu()
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
+ fun testOnTaskInfoChanged_enableShellTransitionsFlag() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+ assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
+
+ decoration.mHasGlobalFocus = true
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(eq(task), eq(true), anyOrNull())
+
+ decoration.mHasGlobalFocus = false
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(eq(task), eq(false), anyOrNull())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
+ fun testOnTaskInfoChanged_disableShellTransitionsFlag() {
+ val task = createTask(
+ windowingMode = WINDOWING_MODE_FREEFORM
+ )
+ val taskSurface = SurfaceControl()
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task, taskSurface)
+ assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
+
+ task.isFocused = true
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(eq(task), eq(true), anyOrNull())
+
+ task.isFocused = false
+ desktopModeWindowDecorViewModel.onTaskInfoChanged(task)
+ verify(decoration).relayout(eq(task), eq(false), anyOrNull())
+ }
+
+ @Test
+ fun testGestureExclusionChanged_updatesDecorations() {
+ val captor = argumentCaptor<ISystemGestureExclusionListener>()
+ verify(mockWindowManager)
+ .registerSystemGestureExclusionListener(captor.capture(), eq(DEFAULT_DISPLAY))
+ val task = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ displayId = DEFAULT_DISPLAY
+ )
+ val task2 = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ displayId = DEFAULT_DISPLAY
+ )
+ val newRegion = Region.obtain().apply {
+ set(Rect(0, 0, 1600, 80))
+ }
+
+ captor.firstValue.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, newRegion, newRegion)
+ testShellExecutor.flushAll()
+
+ verify(task).onExclusionRegionChanged(newRegion)
+ verify(task2).onExclusionRegionChanged(newRegion)
+ }
+
+ @Test
+ fun testGestureExclusionChanged_otherDisplay_skipsDecorationUpdate() {
+ val captor = argumentCaptor<ISystemGestureExclusionListener>()
+ verify(mockWindowManager)
+ .registerSystemGestureExclusionListener(captor.capture(), eq(DEFAULT_DISPLAY))
+ val task = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ displayId = DEFAULT_DISPLAY
+ )
+ val task2 = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ displayId = 2
+ )
+ val newRegion = Region.obtain().apply {
+ set(Rect(0, 0, 1600, 80))
+ }
+
+ captor.firstValue.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, newRegion, newRegion)
+ testShellExecutor.flushAll()
+
+ verify(task).onExclusionRegionChanged(newRegion)
+ verify(task2, never()).onExclusionRegionChanged(newRegion)
+ }
+
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
taskSurface: SurfaceControl = SurfaceControl(),
requestingImmersive: Boolean = false,
+ displayId: Int = DEFAULT_DISPLAY,
onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
onImmersiveOrRestoreListenerCaptor: KArgumentCaptor<() -> Unit> =
@@ -1348,6 +1427,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
): DesktopModeWindowDecoration {
val decor = setUpMockDecorationForTask(createTask(
windowingMode = windowingMode,
+ displayId = displayId,
requestingImmersive = requestingImmersive
))
onTaskOpening(decor.mTaskInfo, taskSurface)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 86ec6753a1bd..1d2d0f078817 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -64,6 +64,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemProperties;
@@ -224,6 +225,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private TestableContext mTestableContext;
private final ShellExecutor mBgExecutor = new TestShellExecutor();
private final AssistContent mAssistContent = new AssistContent();
+ private final Region mExclusionRegion = Region.obtain();
/** Set up run before test class. */
@BeforeClass
@@ -262,8 +264,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(), any(),
- anyBoolean(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt(),
- anyInt()))
+ anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), any(),
+ anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(),
@@ -283,7 +285,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration spyWindowDecor =
spy(createWindowDecoration(taskInfo));
- spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */, mExclusionRegion);
// Menus should close if open before the task being invisible causes relayout to return.
verify(spyWindowDecor).closeHandleMenu();
@@ -303,7 +305,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
}
@@ -324,7 +327,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
}
@@ -350,7 +354,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(customTaskDensity);
}
@@ -377,12 +382,14 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(systemDensity);
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS)
public void updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -400,12 +407,39 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.hasInputFeatureSpy()).isTrue();
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS)
+ public void updateRelayoutParams_freeformAndTransparentAppearance_limitedTouchRegion() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(
+ APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false,
+ /* isStatusBarVisible */ true,
+ /* isKeyguardVisibleAndOccluded */ false,
+ /* inFullImmersiveMode */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
+
+ assertThat(relayoutParams.mLimitTouchRegionToSystemAreas).isTrue();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS)
public void updateRelayoutParams_freeformButOpaqueAppearance_disallowsInputFallthrough() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -422,12 +456,38 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS)
+ public void updateRelayoutParams_freeformButOpaqueAppearance_unlimitedTouchRegion() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false,
+ /* isStatusBarVisible */ true,
+ /* isKeyguardVisibleAndOccluded */ false,
+ /* inFullImmersiveMode */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
+
+ assertThat(relayoutParams.mLimitTouchRegionToSystemAreas).isFalse();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS)
public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -443,12 +503,36 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS)
+ public void updateRelayoutParams_fullscreen_unlimitedTouchRegion() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false,
+ /* isStatusBarVisible */ true,
+ /* isKeyguardVisibleAndOccluded */ false,
+ /* inFullImmersiveMode */ false,
+ new InsetsState(),
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
+
+ assertThat(relayoutParams.mLimitTouchRegionToSystemAreas).isFalse();
+ }
+
+ @Test
public void updateRelayoutParams_freeform_inputChannelNeeded() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -464,7 +548,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(hasNoInputChannelFeature(relayoutParams)).isFalse();
}
@@ -486,7 +571,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
}
@@ -508,7 +594,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
}
@@ -531,7 +618,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
}
@@ -555,7 +643,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
}
@@ -577,7 +666,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(
(relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0)
@@ -601,7 +691,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(
(relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0)
@@ -631,7 +722,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ true,
insetsState,
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -654,7 +746,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ true,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -676,7 +769,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -698,7 +792,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -719,7 +814,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -740,7 +836,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ true,
/* inFullImmersiveMode */ false,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -762,7 +859,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ true,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -776,7 +874,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ false,
/* inFullImmersiveMode */ true,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -798,7 +897,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
/* isKeyguardVisibleAndOccluded */ true,
/* inFullImmersiveMode */ true,
new InsetsState(),
- /* hasGlobalFocus= */ true);
+ /* hasGlobalFocus= */ true,
+ mExclusionRegion);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -809,7 +909,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockTransaction).apply();
verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any());
@@ -824,7 +924,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
// Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
taskInfo.isResizeable = false;
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockTransaction, never()).apply();
verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
@@ -836,7 +936,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
}
@@ -848,7 +948,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
// Once for view host, the other for the AppHandle input layer.
verify(mMockHandler, times(2)).post(runnableArgument.capture());
@@ -865,7 +965,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
// Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
taskInfo.isResizeable = false;
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
verify(mMockHandler, never()).post(any());
@@ -877,11 +977,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
// Once for view host, the other for the AppHandle input layer.
verify(mMockHandler, times(2)).post(runnableArgument.capture());
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
}
@@ -892,7 +992,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
// Once for view host, the other for the AppHandle input layer.
verify(mMockHandler, times(2)).post(runnableArgument.capture());
@@ -1132,7 +1232,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
runnableArgument.getValue().run();
// Relayout decor with same captured link
- decor.relayout(taskInfo, true /* hasGlobalFocus */);
+ decor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
// Verify handle menu's browser link not set to captured link since link is expired
createHandleMenu(decor);
@@ -1178,6 +1278,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
openInBrowserCaptor.capture(),
any(),
any(),
@@ -1208,6 +1309,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
openInBrowserCaptor.capture(),
any(),
any(),
@@ -1263,6 +1365,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
closeClickListener.capture(),
any(),
anyBoolean()
@@ -1294,6 +1397,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
/* forceShowSystemBars= */ eq(true)
);
}
@@ -1309,7 +1413,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockCaptionHandleRepository, never()).notifyCaptionChanged(any());
}
@@ -1326,7 +1430,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
CaptionState.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged(
captionStateArgumentCaptor.capture());
@@ -1353,7 +1457,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
CaptionState.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
verify(mMockAppHeaderViewHolder, atLeastOnce()).runOnAppChipGlobalLayout(
runnableArgumentCaptor.capture());
runnableArgumentCaptor.getValue().invoke();
@@ -1376,7 +1480,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
CaptionState.class);
- spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */, mExclusionRegion);
verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged(
captionStateArgumentCaptor.capture());
@@ -1396,7 +1500,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
CaptionState.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
createHandleMenu(spyWindowDecor);
verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged(
@@ -1421,7 +1525,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
CaptionState.class);
- spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
createHandleMenu(spyWindowDecor);
spyWindowDecor.closeHandleMenu();
@@ -1436,9 +1540,30 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+ public void browserApp_webUriUsedForBrowserApp() {
+ // Make {@link AppToWebUtils#isBrowserApp} return true
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.handleAllWebDataURI = true;
+ resolveInfo.activityInfo = createActivityInfo();
+ when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(List.of(resolveInfo));
+
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ final DesktopModeWindowDecoration decor = createWindowDecoration(
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
+
+ // Verify web uri used for browser applications
+ createHandleMenu(decor);
+ verifyHandleMenuCreated(TEST_URI2);
+ }
+
+
private void verifyHandleMenuCreated(@Nullable Uri uri) {
verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(),
- any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
argThat(intent -> (uri == null && intent == null) || intent.getData().equals(uri)),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -1518,7 +1643,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener);
windowDecor.mDecorWindowContext = mContext;
if (relayout) {
- windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion);
}
return windowDecor;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 9544fa823b5a..7ec2cbf9460e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -242,7 +242,7 @@ class HandleMenuTest : ShellTestCase() {
private fun createAndShowHandleMenu(
splitPosition: Int? = null,
- forceShowSystemBars: Boolean = false,
+ forceShowSystemBars: Boolean = false
): HandleMenu {
val layoutId = if (mockDesktopWindowDecoration.mTaskInfo.isFreeform) {
R.layout.desktop_mode_app_header
@@ -266,7 +266,9 @@ class HandleMenuTest : ShellTestCase() {
WindowManagerWrapper(mockWindowManager),
layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true,
shouldShowNewWindowButton = true, shouldShowManageWindowsButton = false,
- null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50,
+ shouldShowChangeAspectRatioButton = false, isBrowserApp = false,
+ null /* openInAppOrBrowserIntent */, captionWidth = HANDLE_WIDTH,
+ captionHeight = 50,
captionX = captionX,
captionY = 0,
)
@@ -276,7 +278,8 @@ class HandleMenuTest : ShellTestCase() {
onToSplitScreenClickListener = mock(),
onNewWindowClickListener = mock(),
onManageWindowsClickListener = mock(),
- openInBrowserClickListener = mock(),
+ onChangeAspectRatioClickListener = mock(),
+ openInAppOrBrowserClickListener = mock(),
onOpenByDefaultClickListener = mock(),
onCloseMenuClickListener = mock(),
onOutsideTouchListener = mock(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 8e0434cb28f7..534803db5fe0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -60,6 +60,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
@@ -508,7 +509,7 @@ public class WindowDecorationTests extends ShellTestCase {
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */,
- true /* hasGlobalFocus */);
+ true /* hasGlobalFocus */, Region.obtain());
verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
}
@@ -525,7 +526,7 @@ public class WindowDecorationTests extends ShellTestCase {
mRelayoutParams.mCaptionTopPadding = 50;
windowDecor.relayout(taskInfo, false /* applyStartTransactionOnDraw */,
- true /* hasGlobalFocus */);
+ true /* hasGlobalFocus */, Region.obtain());
assertEquals(50, mRelayoutResult.mCaptionTopPadding);
}
@@ -944,7 +945,7 @@ public class WindowDecorationTests extends ShellTestCase {
decor.onInsetsStateChanged(createInsetsState(statusBars(), false /* visible */));
- verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */);
+ verify(decor, times(2)).relayout(any(), any(), any(), any(), any(), any());
}
@Test
@@ -958,7 +959,7 @@ public class WindowDecorationTests extends ShellTestCase {
decor.onInsetsStateChanged(createInsetsState(statusBars(), true /* visible */));
- verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */);
+ verify(decor, times(1)).relayout(any(), any(), any(), any(), any(), any());
}
@Test
@@ -973,7 +974,7 @@ public class WindowDecorationTests extends ShellTestCase {
decor.onKeyguardStateChanged(true /* visible */, true /* occluding */);
assertTrue(decor.mIsKeyguardVisibleAndOccluded);
- verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */);
+ verify(decor, times(2)).relayout(any(), any(), any(), any(), any(), any());
}
@Test
@@ -987,7 +988,7 @@ public class WindowDecorationTests extends ShellTestCase {
decor.onKeyguardStateChanged(false /* visible */, true /* occluding */);
- verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */);
+ verify(decor, times(1)).relayout(any(), any(), any(), any(), any(), any());
}
private ActivityManager.RunningTaskInfo createTaskInfo() {
@@ -1061,9 +1062,16 @@ public class WindowDecorationTests extends ShellTestCase {
surfaceControlViewHostFactory, desktopModeEventLogger);
}
- @Override
void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
- relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus);
+ relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus,
+ Region.obtain());
+ }
+
+ @Override
+ void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
+ relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus,
+ displayExclusionRegion);
}
@Override
@@ -1085,11 +1093,13 @@ public class WindowDecorationTests extends ShellTestCase {
}
void relayout(ActivityManager.RunningTaskInfo taskInfo,
- boolean applyStartTransactionOnDraw, boolean hasGlobalFocus) {
+ boolean applyStartTransactionOnDraw, boolean hasGlobalFocus,
+ @NonNull Region displayExclusionRegion) {
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
mRelayoutParams.mLayoutResId = R.layout.caption_layout;
mRelayoutParams.mHasGlobalFocus = hasGlobalFocus;
+ mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion);
relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 80ad1df44a1b..d8c1a11de452 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -24,6 +24,7 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -52,6 +53,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val transitionsMock: Transitions = mock()
private val shellTaskOrganizerMock: ShellTaskOrganizer = mock()
private val desktopRepository: DesktopRepository = mock()
+ private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val toggleResizeDesktopTaskTransitionHandlerMock:
ToggleResizeDesktopTaskTransitionHandler =
mock()
@@ -74,6 +76,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
toggleResizeDesktopTaskTransitionHandlerMock,
returnToDragStartAnimatorMock,
desktopRepository,
+ desktopModeEventLogger,
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
}
@@ -127,7 +130,8 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
)
desktopTilingDecorViewModel.moveTaskToFrontIfTiled(task1)
- verify(desktopTilingDecoration, times(1)).moveTiledPairToFront(any())
+ verify(desktopTilingDecoration, times(1))
+ .moveTiledPairToFront(any(), isTaskFocused = eq(true))
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index f371f5223419..d7b971de94ac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -21,6 +21,7 @@ import android.content.res.Resources
import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
+import android.view.MotionEvent
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_TO_FRONT
@@ -33,6 +34,8 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -91,7 +94,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
private val info: TransitionInfo = mock()
private val finishCallback: Transitions.TransitionFinishCallback = mock()
private val desktopRepository: DesktopRepository = mock()
+ private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager = mock()
+ private val motionEvent: MotionEvent = mock()
private lateinit var tilingDecoration: DesktopTilingWindowDecoration
private val split_divider_width = 10
@@ -112,6 +117,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
desktopRepository,
+ desktopModeEventLogger,
)
whenever(context.createContextAsUser(any(), any())).thenReturn(context)
}
@@ -322,6 +328,37 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
}
@Test
+ fun taskTiled_broughtToFront_taskInfoNotUpdated_bringToFront() {
+ val task1 = createFreeformTask()
+ val task2 = createFreeformTask()
+ val task3 = createFreeformTask()
+ val stableBounds = STABLE_BOUNDS_MOCK
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+ whenever(desktopWindowDecoration.getLeash()).thenReturn(surfaceControlMock)
+ whenever(desktopRepository.isVisibleTask(any())).thenReturn(true)
+ tilingDecoration.onAppTiled(
+ task1,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.RIGHT,
+ BOUNDS,
+ )
+ tilingDecoration.onAppTiled(
+ task2,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+
+ assertThat(tilingDecoration.moveTiledPairToFront(task3, isTaskFocused = true)).isFalse()
+ assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
+ }
+
+ @Test
fun taskTiledTasks_NotResized_BeforeTouchEndArrival() {
// Setup
val task1 = createFreeformTask()
@@ -371,13 +408,13 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
// End moving, no startTransition because bounds did not change.
tiledTaskHelper.newBounds.set(BOUNDS)
- tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction)
+ tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction, motionEvent)
verify(tiledTaskHelper, times(2)).hideVeil()
verify(transitions, never()).startTransition(any(), any(), any())
// Move then end again with bounds changing to ensure startTransition is called.
tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
- tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction)
+ tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction, motionEvent)
verify(transitions, times(1))
.startTransition(eq(TRANSIT_CHANGE), any(), eq(tilingDecoration))
// No hide veil until start animation is called.
@@ -389,6 +426,64 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
}
@Test
+ fun tiledTasksResizedUsingDividerHandle_shouldLogResizingEvents() {
+ // Setup
+ val task1 = createFreeformTask()
+ val task2 = createFreeformTask()
+ val stableBounds = STABLE_BOUNDS_MOCK
+ whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+ desktopWindowDecoration.mTaskInfo = task1
+ task1.minWidth = 0
+ task1.minHeight = 0
+ initTiledTaskHelperMock(task1)
+ desktopWindowDecoration.mDecorWindowContext = context
+ whenever(resources.getBoolean(any())).thenReturn(true)
+
+ // Act
+ tilingDecoration.onAppTiled(
+ task1,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.RIGHT,
+ BOUNDS,
+ )
+ tilingDecoration.onAppTiled(
+ task2,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+ tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.rightTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.onDividerHandleDragStart(motionEvent)
+ // Log start event for task1 and task2, but the tasks are the same in
+ // this test, so we verify the same log twice.
+ verify(desktopModeEventLogger, times(2)).logTaskResizingStarted(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ task1,
+ displayController,
+ )
+
+ tilingDecoration.onDividerHandleMoved(BOUNDS, transaction)
+ tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction, motionEvent)
+ // Log end event for task1 and task2, but the tasks are the same in
+ // this test, so we verify the same log twice.
+ verify(desktopModeEventLogger, times(2)).logTaskResizingEnded(
+ ResizeTrigger.TILING_DIVIDER,
+ motionEvent,
+ task1,
+ BOUNDS.height(),
+ BOUNDS.width(),
+ displayController,
+ )
+ }
+
+ @Test
fun taskTiled_shouldBeRemoved_whenTileBroken() {
val task1 = createFreeformTask()
val stableBounds = STABLE_BOUNDS_MOCK
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
index c96ce955f217..734815cdd915 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt
@@ -68,7 +68,7 @@ class TilingDividerViewTest : ShellTestCase() {
val downMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, downMotionEvent)
- verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any())
+ verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any(), any())
whenever(dividerMoveCallbackMock.onDividerMove(any())).thenReturn(true)
val motionEvent =
@@ -79,7 +79,7 @@ class TilingDividerViewTest : ShellTestCase() {
val upMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_UP, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, upMotionEvent)
- verify(dividerMoveCallbackMock, times(1)).onDividerMovedEnd(any())
+ verify(dividerMoveCallbackMock, times(1)).onDividerMovedEnd(any(), any())
}
@Test
@@ -92,12 +92,12 @@ class TilingDividerViewTest : ShellTestCase() {
val downMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_DOWN, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, downMotionEvent)
- verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any())
+ verify(dividerMoveCallbackMock, times(1)).onDividerMoveStart(any(), any())
val upMotionEvent =
getMotionEvent(downTime, MotionEvent.ACTION_UP, x.toFloat(), y.toFloat())
tilingDividerView.handleMotionEvent(viewMock, upMotionEvent)
- verify(dividerMoveCallbackMock, never()).onDividerMovedEnd(any())
+ verify(dividerMoveCallbackMock, never()).onDividerMovedEnd(any(), any())
}
private fun getMotionEvent(eventTime: Long, action: Int, x: Float, y: Float): MotionEvent {
diff --git a/libs/appfunctions/Android.bp b/libs/appfunctions/Android.bp
index c6cee07d1946..5ab5a7a59c2a 100644
--- a/libs/appfunctions/Android.bp
+++ b/libs/appfunctions/Android.bp
@@ -18,10 +18,10 @@ package {
}
java_sdk_library {
- name: "com.google.android.appfunctions.sidecar",
+ name: "com.android.extensions.appfunctions",
owner: "google",
srcs: ["java/**/*.java"],
- api_packages: ["com.google.android.appfunctions.sidecar"],
+ api_packages: ["com.android.extensions.appfunctions"],
dex_preopt: {
enabled: false,
},
@@ -31,9 +31,9 @@ java_sdk_library {
}
prebuilt_etc {
- name: "appfunctions.sidecar.xml",
+ name: "appfunctions.extension.xml",
system_ext_specific: true,
sub_dir: "permissions",
- src: "appfunctions.sidecar.xml",
+ src: "appfunctions.extension.xml",
filename_from_src: true,
}
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index faf84a8ab5ac..de402095e195 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -1,9 +1,29 @@
// Signature format: 2.0
-package com.google.android.appfunctions.sidecar {
+package com.android.extensions.appfunctions {
+
+ public final class AppFunctionException extends java.lang.Exception {
+ ctor public AppFunctionException(int, @Nullable String);
+ ctor public AppFunctionException(int, @Nullable String, @NonNull android.os.Bundle);
+ method public int getErrorCategory();
+ method public int getErrorCode();
+ method @Nullable public String getErrorMessage();
+ method @NonNull public android.os.Bundle getExtras();
+ field public static final int ERROR_APP_UNKNOWN_ERROR = 3000; // 0xbb8
+ field public static final int ERROR_CANCELLED = 2001; // 0x7d1
+ field public static final int ERROR_CATEGORY_APP = 3; // 0x3
+ field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
+ field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
+ field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
+ field public static final int ERROR_DENIED = 1000; // 0x3e8
+ field public static final int ERROR_DISABLED = 1002; // 0x3ea
+ field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb
+ field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9
+ field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0
+ }
public final class AppFunctionManager {
ctor public AppFunctionManager(android.content.Context);
- method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+ method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<com.android.extensions.appfunctions.ExecuteAppFunctionResponse,com.android.extensions.appfunctions.AppFunctionException>);
method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
@@ -15,7 +35,7 @@ package com.google.android.appfunctions.sidecar {
public abstract class AppFunctionService extends android.app.Service {
ctor public AppFunctionService();
method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+ method @MainThread public abstract void onExecuteFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<com.android.extensions.appfunctions.ExecuteAppFunctionResponse,com.android.extensions.appfunctions.AppFunctionException>);
field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
}
@@ -29,33 +49,17 @@ package com.google.android.appfunctions.sidecar {
public static final class ExecuteAppFunctionRequest.Builder {
ctor public ExecuteAppFunctionRequest.Builder(@NonNull String, @NonNull String);
- method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest build();
- method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle);
- method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument);
+ method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest build();
+ method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument);
}
public final class ExecuteAppFunctionResponse {
- method public int getErrorCategory();
- method @Nullable public String getErrorMessage();
+ ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument);
+ ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle);
method @NonNull public android.os.Bundle getExtras();
- method public int getResultCode();
method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
- method public boolean isSuccess();
- method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
- method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
- field public static final int ERROR_CATEGORY_APP = 3; // 0x3
- field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
- field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
- field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
- field public static final String PROPERTY_RETURN_VALUE = "returnValue";
- field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
- field public static final int RESULT_CANCELLED = 2001; // 0x7d1
- field public static final int RESULT_DENIED = 1000; // 0x3e8
- field public static final int RESULT_DISABLED = 1002; // 0x3ea
- field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
- field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
- field public static final int RESULT_OK = 0; // 0x0
- field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
+ field public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue";
}
}
diff --git a/libs/appfunctions/appfunctions.sidecar.xml b/libs/appfunctions/appfunctions.extension.xml
index bef8b6ec7ce6..dd09cc39d12f 100644
--- a/libs/appfunctions/appfunctions.sidecar.xml
+++ b/libs/appfunctions/appfunctions.extension.xml
@@ -16,6 +16,6 @@
-->
<permissions>
<library
- name="com.google.android.appfunctions.sidecar"
- file="/system_ext/framework/com.google.android.appfunctions.sidecar.jar"/>
+ name="com.android.extensions.appfunctions"
+ file="/system_ext/framework/com.android.extensions.appfunctions.jar"/>
</permissions> \ No newline at end of file
diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
new file mode 100644
index 000000000000..2540236f2ce5
--- /dev/null
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.extensions.appfunctions;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Represents an app function related errors. */
+public final class AppFunctionException extends Exception {
+ /**
+ * The caller does not have the permission to execute an app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_DENIED = 1000;
+
+ /**
+ * The caller supplied invalid arguments to the execution request.
+ *
+ * <p>This error may be considered similar to {@link IllegalArgumentException}.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_INVALID_ARGUMENT = 1001;
+
+ /**
+ * The caller tried to execute a disabled app function.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_DISABLED = 1002;
+
+ /**
+ * The caller tried to execute a function that does not exist.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+ */
+ public static final int ERROR_FUNCTION_NOT_FOUND = 1003;
+
+ /**
+ * An internal unexpected error coming from the system.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_SYSTEM_ERROR = 2000;
+
+ /**
+ * The operation was cancelled. Use this error code to report that a cancellation is done after
+ * receiving a cancellation signal.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+ */
+ public static final int ERROR_CANCELLED = 2001;
+
+ /**
+ * An unknown error occurred while processing the call in the AppFunctionService.
+ *
+ * <p>This error is thrown when the service is connected in the remote application but an
+ * unexpected error is thrown from the bound application.
+ *
+ * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
+ */
+ public static final int ERROR_APP_UNKNOWN_ERROR = 3000;
+
+ /**
+ * The error category is unknown.
+ *
+ * <p>This is the default value for {@link #getErrorCategory}.
+ */
+ public static final int ERROR_CATEGORY_UNKNOWN = 0;
+
+ /**
+ * The error is caused by the app requesting a function execution.
+ *
+ * <p>For example, the caller provided invalid parameters in the execution request e.g. an
+ * invalid function ID.
+ *
+ * <p>Errors in the category fall in the range 1000-1999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
+
+ /**
+ * The error is caused by an issue in the system.
+ *
+ * <p>For example, the AppFunctionService implementation is not found by the system.
+ *
+ * <p>Errors in the category fall in the range 2000-2999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_SYSTEM = 2;
+
+ /**
+ * The error is caused by the app providing the function.
+ *
+ * <p>For example, the app crashed when the system is executing the request.
+ *
+ * <p>Errors in the category fall in the range 3000-3999 inclusive.
+ */
+ public static final int ERROR_CATEGORY_APP = 3;
+
+ private final int mErrorCode;
+ @Nullable private final String mErrorMessage;
+ @NonNull private final Bundle mExtras;
+
+ public AppFunctionException(int errorCode, @Nullable String errorMessage) {
+ this(errorCode, errorMessage, Bundle.EMPTY);
+ }
+
+ public AppFunctionException(
+ int errorCode, @Nullable String errorMessage, @NonNull Bundle extras) {
+ super(errorMessage);
+ mErrorCode = errorCode;
+ mErrorMessage = errorMessage;
+ mExtras = extras;
+ }
+
+ /** Returns one of the {@code ERROR} constants. */
+ @ErrorCode
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /** Returns the error message. */
+ @Nullable
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ /**
+ * Returns the error category.
+ *
+ * <p>This method categorizes errors based on their underlying cause, allowing developers to
+ * implement targeted error handling and provide more informative error messages to users. It
+ * maps ranges of error codes to specific error categories.
+ *
+ * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the error code does not belong to
+ * any error category.
+ *
+ * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
+ * error code ranges.
+ */
+ @ErrorCategory
+ public int getErrorCategory() {
+ if (mErrorCode >= 1000 && mErrorCode < 2000) {
+ return ERROR_CATEGORY_REQUEST_ERROR;
+ }
+ if (mErrorCode >= 2000 && mErrorCode < 3000) {
+ return ERROR_CATEGORY_SYSTEM;
+ }
+ if (mErrorCode >= 3000 && mErrorCode < 4000) {
+ return ERROR_CATEGORY_APP;
+ }
+ return ERROR_CATEGORY_UNKNOWN;
+ }
+
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Error codes.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"ERROR_"},
+ value = {
+ ERROR_DENIED,
+ ERROR_APP_UNKNOWN_ERROR,
+ ERROR_FUNCTION_NOT_FOUND,
+ ERROR_SYSTEM_ERROR,
+ ERROR_INVALID_ARGUMENT,
+ ERROR_DISABLED,
+ ERROR_CANCELLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCode {}
+
+ /**
+ * Error categories.
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = {"ERROR_CATEGORY_"},
+ value = {
+ ERROR_CATEGORY_UNKNOWN,
+ ERROR_CATEGORY_REQUEST_ERROR,
+ ERROR_CATEGORY_APP,
+ ERROR_CATEGORY_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCategory {}
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
index 2075104ff868..9eb66a33fedc 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -31,7 +31,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Provides app functions related functionalities.
@@ -115,7 +114,9 @@ public final class AppFunctionManager {
@NonNull ExecuteAppFunctionRequest sidecarRequest,
@NonNull @CallbackExecutor Executor executor,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+ @NonNull
+ OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
+ callback) {
Objects.requireNonNull(sidecarRequest);
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -126,10 +127,20 @@ public final class AppFunctionManager {
platformRequest,
executor,
cancellationSignal,
- (platformResponse) -> {
- callback.accept(
- SidecarConverter.getSidecarExecuteAppFunctionResponse(
- platformResponse));
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(
+ android.app.appfunctions.ExecuteAppFunctionResponse result) {
+ callback.onResult(
+ SidecarConverter.getSidecarExecuteAppFunctionResponse(result));
+ }
+
+ @Override
+ public void onError(
+ android.app.appfunctions.AppFunctionException exception) {
+ callback.onError(
+ SidecarConverter.getSidecarAppFunctionException(exception));
+ }
});
}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
index 0dc87e45b7e3..55f579138218 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
-import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
+import static com.android.extensions.appfunctions.SidecarConverter.getPlatformAppFunctionException;
+import static com.android.extensions.appfunctions.SidecarConverter.getPlatformExecuteAppFunctionResponse;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -26,9 +27,7 @@ import android.content.Intent;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
-import android.util.Log;
-
-import java.util.function.Consumer;
+import android.os.OutcomeReceiver;
/**
* Abstract base class to provide app functions to the system.
@@ -80,10 +79,18 @@ public abstract class AppFunctionService extends Service {
platformRequest),
callingPackage,
cancellationSignal,
- (sidecarResponse) -> {
- callback.accept(
- SidecarConverter.getPlatformExecuteAppFunctionResponse(
- sidecarResponse));
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(ExecuteAppFunctionResponse result) {
+ callback.onResult(
+ getPlatformExecuteAppFunctionResponse(result));
+ }
+
+ @Override
+ public void onError(AppFunctionException exception) {
+ callback.onError(
+ getPlatformAppFunctionException(exception));
+ }
});
});
@@ -116,12 +123,14 @@ public abstract class AppFunctionService extends Service {
* @param request The function execution request.
* @param callingPackage The package name of the app that is requesting the execution.
* @param cancellationSignal A signal to cancel the execution.
- * @param callback A callback to report back the result.
+ * @param callback A callback to report back the result or error.
*/
@MainThread
public abstract void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull String callingPackage,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+ @NonNull
+ OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
+ callback);
}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java
index 593c5213dd52..baddc245f0f1 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
import android.annotation.NonNull;
import android.app.appsearch.GenericDocument;
@@ -91,8 +91,8 @@ public final class ExecuteAppFunctionRequest {
* Returns the function parameters. The key is the parameter name, and the value is the
* parameter value.
*
- * <p>The bundle may have missing parameters. Developers are advised to implement defensive
- * handling measures.
+ * <p>The {@link GenericDocument} may have missing parameters. Developers are advised to
+ * implement defensive handling measures.
*
* <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be
* obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This
diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java
new file mode 100644
index 000000000000..0826f04a50dd
--- /dev/null
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.extensions.appfunctions;
+
+import android.annotation.NonNull;
+import android.app.appfunctions.AppFunctionManager;
+import android.app.appsearch.GenericDocument;
+import android.os.Bundle;
+
+import java.util.Objects;
+
+/** The response to an app function execution. */
+public final class ExecuteAppFunctionResponse {
+ /**
+ * The name of the property that stores the function return value within the {@code
+ * resultDocument}.
+ *
+ * <p>See {@link GenericDocument#getProperty(String)} for more information.
+ *
+ * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will
+ * be empty {@link GenericDocument}.
+ *
+ * <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will
+ * return {@code null}.
+ *
+ * <p>See {@link #getResultDocument} for more information on extracting the return value.
+ */
+ public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue";
+
+ /**
+ * Returns the return value of the executed function.
+ *
+ * <p>The return value is stored in a {@link GenericDocument} with the key {@link
+ * #PROPERTY_RETURN_VALUE}.
+ *
+ * <p>See {@link #getResultDocument} for more information on extracting the return value.
+ */
+ @NonNull private final GenericDocument mResultDocument;
+
+ /** Returns the additional metadata data relevant to this function execution response. */
+ @NonNull private final Bundle mExtras;
+
+ /**
+ * @param resultDocument The return value of the executed function.
+ */
+ public ExecuteAppFunctionResponse(@NonNull GenericDocument resultDocument) {
+ this(resultDocument, Bundle.EMPTY);
+ }
+
+ /**
+ * @param resultDocument The return value of the executed function.
+ * @param extras The additional metadata for this function execution response.
+ */
+ public ExecuteAppFunctionResponse(
+ @NonNull GenericDocument resultDocument, @NonNull Bundle extras) {
+ mResultDocument = Objects.requireNonNull(resultDocument);
+ mExtras = Objects.requireNonNull(extras);
+ }
+
+ /**
+ * Returns a generic document containing the return value of the executed function.
+ *
+ * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
+ *
+ * <p>Sample code for extracting the return value:
+ *
+ * <pre>
+ * GenericDocument resultDocument = response.getResultDocument();
+ * Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE);
+ * if (returnValue != null) {
+ * // Cast returnValue to expected type, or use {@link GenericDocument#getPropertyString},
+ * // {@link GenericDocument#getPropertyLong} etc.
+ * // Do something with the returnValue
+ * }
+ * </pre>
+ *
+ * @see AppFunctionManager on how to determine the expected function return.
+ */
+ @NonNull
+ public GenericDocument getResultDocument() {
+ return mResultDocument;
+ }
+
+ /** Returns the additional metadata for this function execution response. */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java b/libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java
index b1b05f79f33f..5e1fc7e684e2 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
import android.annotation.NonNull;
@@ -28,46 +28,50 @@ public final class SidecarConverter {
private SidecarConverter() {}
/**
- * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest}
- * into platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest}
+ * Converts sidecar's {@link ExecuteAppFunctionRequest} into platform's {@link
+ * android.app.appfunctions.ExecuteAppFunctionRequest}
*
* @hide
*/
@NonNull
public static android.app.appfunctions.ExecuteAppFunctionRequest
getPlatformExecuteAppFunctionRequest(@NonNull ExecuteAppFunctionRequest request) {
- return new
- android.app.appfunctions.ExecuteAppFunctionRequest.Builder(
- request.getTargetPackageName(),
- request.getFunctionIdentifier())
+ return new android.app.appfunctions.ExecuteAppFunctionRequest.Builder(
+ request.getTargetPackageName(), request.getFunctionIdentifier())
.setExtras(request.getExtras())
.setParameters(request.getParameters())
.build();
}
/**
- * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse}
- * into platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse}
+ * Converts sidecar's {@link ExecuteAppFunctionResponse} into platform's {@link
+ * android.app.appfunctions.ExecuteAppFunctionResponse}
*
* @hide
*/
@NonNull
public static android.app.appfunctions.ExecuteAppFunctionResponse
getPlatformExecuteAppFunctionResponse(@NonNull ExecuteAppFunctionResponse response) {
- if (response.isSuccess()) {
- return android.app.appfunctions.ExecuteAppFunctionResponse.newSuccess(
- response.getResultDocument(), response.getExtras());
- } else {
- return android.app.appfunctions.ExecuteAppFunctionResponse.newFailure(
- response.getResultCode(),
- response.getErrorMessage(),
- response.getExtras());
- }
+ return new android.app.appfunctions.ExecuteAppFunctionResponse(
+ response.getResultDocument(), response.getExtras());
}
/**
- * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest}
- * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest}
+ * Converts sidecar's {@link AppFunctionException} into platform's {@link
+ * android.app.appfunctions.AppFunctionException}
+ *
+ * @hide
+ */
+ @NonNull
+ public static android.app.appfunctions.AppFunctionException
+ getPlatformAppFunctionException(@NonNull AppFunctionException exception) {
+ return new android.app.appfunctions.AppFunctionException(
+ exception.getErrorCode(), exception.getErrorMessage(), exception.getExtras());
+ }
+
+ /**
+ * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest} into sidecar's
+ * {@link ExecuteAppFunctionRequest}
*
* @hide
*/
@@ -75,30 +79,34 @@ public final class SidecarConverter {
public static ExecuteAppFunctionRequest getSidecarExecuteAppFunctionRequest(
@NonNull android.app.appfunctions.ExecuteAppFunctionRequest request) {
return new ExecuteAppFunctionRequest.Builder(
- request.getTargetPackageName(),
- request.getFunctionIdentifier())
+ request.getTargetPackageName(), request.getFunctionIdentifier())
.setExtras(request.getExtras())
.setParameters(request.getParameters())
.build();
}
/**
- * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse}
- * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse}
+ * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse} into
+ * sidecar's {@link ExecuteAppFunctionResponse}
*
* @hide
*/
@NonNull
public static ExecuteAppFunctionResponse getSidecarExecuteAppFunctionResponse(
@NonNull android.app.appfunctions.ExecuteAppFunctionResponse response) {
- if (response.isSuccess()) {
- return ExecuteAppFunctionResponse.newSuccess(
- response.getResultDocument(), response.getExtras());
- } else {
- return ExecuteAppFunctionResponse.newFailure(
- response.getResultCode(),
- response.getErrorMessage(),
- response.getExtras());
- }
+ return new ExecuteAppFunctionResponse(response.getResultDocument(), response.getExtras());
+ }
+
+ /**
+ * Converts platform's {@link android.app.appfunctions.AppFunctionException} into
+ * sidecar's {@link AppFunctionException}
+ *
+ * @hide
+ */
+ @NonNull
+ public static AppFunctionException getSidecarAppFunctionException(
+ @NonNull android.app.appfunctions.AppFunctionException exception) {
+ return new AppFunctionException(
+ exception.getErrorCode(), exception.getErrorMessage(), exception.getExtras());
}
}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
deleted file mode 100644
index 4e88fb025a9d..000000000000
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.appfunctions.sidecar;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.appsearch.GenericDocument;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * The response to an app function execution.
- *
- * <p>This class copies {@link android.app.appfunctions.ExecuteAppFunctionResponse} without parcel
- * functionality and exposes it here as a sidecar library (avoiding direct dependency on the
- * platform API).
- */
-public final class ExecuteAppFunctionResponse {
- /**
- * The name of the property that stores the function return value within the {@code
- * resultDocument}.
- *
- * <p>See {@link GenericDocument#getProperty(String)} for more information.
- *
- * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will
- * be empty {@link GenericDocument}.
- *
- * <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will
- * return {@code null}.
- *
- * <p>See {@link #getResultDocument} for more information on extracting the return value.
- */
- public static final String PROPERTY_RETURN_VALUE = "returnValue";
-
- /**
- * The call was successful.
- *
- * <p>This result code does not belong in an error category.
- */
- public static final int RESULT_OK = 0;
-
- /**
- * The caller does not have the permission to execute an app function.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_DENIED = 1000;
-
- /**
- * The caller supplied invalid arguments to the execution request.
- *
- * <p>This error may be considered similar to {@link IllegalArgumentException}.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_INVALID_ARGUMENT = 1001;
-
- /**
- * The caller tried to execute a disabled app function.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_DISABLED = 1002;
-
- /**
- * The caller tried to execute a function that does not exist.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
- */
- public static final int RESULT_FUNCTION_NOT_FOUND = 1003;
-
- /**
- * An internal unexpected error coming from the system.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
- */
- public static final int RESULT_SYSTEM_ERROR = 2000;
-
- /**
- * The operation was cancelled. Use this error code to report that a cancellation is done after
- * receiving a cancellation signal.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
- */
- public static final int RESULT_CANCELLED = 2001;
-
- /**
- * An unknown error occurred while processing the call in the AppFunctionService.
- *
- * <p>This error is thrown when the service is connected in the remote application but an
- * unexpected error is thrown from the bound application.
- *
- * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
- */
- public static final int RESULT_APP_UNKNOWN_ERROR = 3000;
-
- /**
- * The error category is unknown.
- *
- * <p>This is the default value for {@link #getErrorCategory}.
- */
- public static final int ERROR_CATEGORY_UNKNOWN = 0;
-
- /**
- * The error is caused by the app requesting a function execution.
- *
- * <p>For example, the caller provided invalid parameters in the execution request e.g. an
- * invalid function ID.
- *
- * <p>Errors in the category fall in the range 1000-1999 inclusive.
- */
- public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
-
- /**
- * The error is caused by an issue in the system.
- *
- * <p>For example, the AppFunctionService implementation is not found by the system.
- *
- * <p>Errors in the category fall in the range 2000-2999 inclusive.
- */
- public static final int ERROR_CATEGORY_SYSTEM = 2;
-
- /**
- * The error is caused by the app providing the function.
- *
- * <p>For example, the app crashed when the system is executing the request.
- *
- * <p>Errors in the category fall in the range 3000-3999 inclusive.
- */
- public static final int ERROR_CATEGORY_APP = 3;
-
- /** The result code of the app function execution. */
- @ResultCode private final int mResultCode;
-
- /**
- * The error message associated with the result, if any. This is {@code null} if the result code
- * is {@link #RESULT_OK}.
- */
- @Nullable private final String mErrorMessage;
-
- /**
- * Returns the return value of the executed function.
- *
- * <p>The return value is stored in a {@link GenericDocument} with the key {@link
- * #PROPERTY_RETURN_VALUE}.
- *
- * <p>See {@link #getResultDocument} for more information on extracting the return value.
- */
- @NonNull private final GenericDocument mResultDocument;
-
- /** Returns the additional metadata data relevant to this function execution response. */
- @NonNull private final Bundle mExtras;
-
- private ExecuteAppFunctionResponse(
- @NonNull GenericDocument resultDocument,
- @NonNull Bundle extras,
- @ResultCode int resultCode,
- @Nullable String errorMessage) {
- mResultDocument = Objects.requireNonNull(resultDocument);
- mExtras = Objects.requireNonNull(extras);
- mResultCode = resultCode;
- mErrorMessage = errorMessage;
- }
-
- /**
- * Returns result codes from throwable.
- *
- * @hide
- */
- static @ResultCode int getResultCode(@NonNull Throwable t) {
- if (t instanceof IllegalArgumentException) {
- return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
- }
- return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
- }
-
- /**
- * Returns a successful response.
- *
- * @param resultDocument The return value of the executed function.
- * @param extras The additional metadata data relevant to this function execution response.
- */
- @NonNull
- public static ExecuteAppFunctionResponse newSuccess(
- @NonNull GenericDocument resultDocument, @Nullable Bundle extras) {
- Objects.requireNonNull(resultDocument);
- Bundle actualExtras = getActualExtras(extras);
-
- return new ExecuteAppFunctionResponse(
- resultDocument, actualExtras, RESULT_OK, /* errorMessage= */ null);
- }
-
- /**
- * Returns a failure response.
- *
- * @param resultCode The result code of the app function execution.
- * @param extras The additional metadata data relevant to this function execution response.
- * @param errorMessage The error message associated with the result, if any.
- */
- @NonNull
- public static ExecuteAppFunctionResponse newFailure(
- @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) {
- if (resultCode == RESULT_OK) {
- throw new IllegalArgumentException("resultCode must not be RESULT_OK");
- }
- Bundle actualExtras = getActualExtras(extras);
- GenericDocument emptyDocument = new GenericDocument.Builder<>("", "", "").build();
- return new ExecuteAppFunctionResponse(
- emptyDocument, actualExtras, resultCode, errorMessage);
- }
-
- private static Bundle getActualExtras(@Nullable Bundle extras) {
- if (extras == null) {
- return Bundle.EMPTY;
- }
- return extras;
- }
-
- /**
- * Returns the error category of the {@link ExecuteAppFunctionResponse}.
- *
- * <p>This method categorizes errors based on their underlying cause, allowing developers to
- * implement targeted error handling and provide more informative error messages to users. It
- * maps ranges of result codes to specific error categories.
- *
- * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to
- * ensure correct categorization of the failed response.
- *
- * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to
- * any error category, for example, in the case of a successful result with {@link #RESULT_OK}.
- *
- * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
- * result code ranges.
- */
- @ErrorCategory
- public int getErrorCategory() {
- if (mResultCode >= 1000 && mResultCode < 2000) {
- return ERROR_CATEGORY_REQUEST_ERROR;
- }
- if (mResultCode >= 2000 && mResultCode < 3000) {
- return ERROR_CATEGORY_SYSTEM;
- }
- if (mResultCode >= 3000 && mResultCode < 4000) {
- return ERROR_CATEGORY_APP;
- }
- return ERROR_CATEGORY_UNKNOWN;
- }
-
- /**
- * Returns a generic document containing the return value of the executed function.
- *
- * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
- *
- * <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed
- * function does not produce a return value.
- *
- * <p>Sample code for extracting the return value:
- *
- * <pre>
- * GenericDocument resultDocument = response.getResultDocument();
- * Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE);
- * if (returnValue != null) {
- * // Cast returnValue to expected type, or use {@link GenericDocument#getPropertyString},
- * // {@link GenericDocument#getPropertyLong} etc.
- * // Do something with the returnValue
- * }
- * </pre>
- */
- @NonNull
- public GenericDocument getResultDocument() {
- return mResultDocument;
- }
-
- /** Returns the extras of the app function execution. */
- @NonNull
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Returns {@code true} if {@link #getResultCode} equals {@link
- * ExecuteAppFunctionResponse#RESULT_OK}.
- */
- public boolean isSuccess() {
- return getResultCode() == RESULT_OK;
- }
-
- /**
- * Returns one of the {@code RESULT} constants defined in {@link ExecuteAppFunctionResponse}.
- */
- @ResultCode
- public int getResultCode() {
- return mResultCode;
- }
-
- /**
- * Returns the error message associated with this result.
- *
- * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}.
- */
- @Nullable
- public String getErrorMessage() {
- return mErrorMessage;
- }
-
- /**
- * Result codes.
- *
- * @hide
- */
- @IntDef(
- prefix = {"RESULT_"},
- value = {
- RESULT_OK,
- RESULT_DENIED,
- RESULT_APP_UNKNOWN_ERROR,
- RESULT_SYSTEM_ERROR,
- RESULT_FUNCTION_NOT_FOUND,
- RESULT_INVALID_ARGUMENT,
- RESULT_DISABLED,
- RESULT_CANCELLED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ResultCode {}
-
- /**
- * Error categories.
- *
- * @hide
- */
- @IntDef(
- prefix = {"ERROR_CATEGORY_"},
- value = {
- ERROR_CATEGORY_UNKNOWN,
- ERROR_CATEGORY_REQUEST_ERROR,
- ERROR_CATEGORY_APP,
- ERROR_CATEGORY_SYSTEM
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ErrorCategory {}
-}
diff --git a/libs/appfunctions/tests/Android.bp b/libs/appfunctions/tests/Android.bp
index 6f5eff305d8d..db79675ae9f7 100644
--- a/libs/appfunctions/tests/Android.bp
+++ b/libs/appfunctions/tests/Android.bp
@@ -25,7 +25,7 @@ android_test {
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.core_core-ktx",
- "com.google.android.appfunctions.sidecar.impl",
+ "com.android.extensions.appfunctions.impl",
"junit",
"kotlin-test",
"mockito-target-extended-minus-junit4",
diff --git a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt b/libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt
index 264f84209caf..11202d58e484 100644
--- a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
+++ b/libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package com.google.android.appfunctions.sidecar.tests
+package com.android.extensions.appfunctions.tests
+import android.app.appfunctions.AppFunctionException
import android.app.appfunctions.ExecuteAppFunctionRequest
import android.app.appfunctions.ExecuteAppFunctionResponse
import android.app.appsearch.GenericDocument
import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.google.android.appfunctions.sidecar.SidecarConverter
+import com.android.extensions.appfunctions.SidecarConverter
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,7 +61,7 @@ class SidecarConverterTest {
.setPropertyLong("testLong", 23)
.build()
val sidecarRequest =
- com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder(
+ com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder(
"targetPkg",
"targetFunctionId"
)
@@ -83,44 +84,38 @@ class SidecarConverterTest {
GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
.setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true)
.build()
- val platformResponse = ExecuteAppFunctionResponse.newSuccess(resultGd, null)
+ val platformResponse = ExecuteAppFunctionResponse(resultGd)
val sidecarResponse = SidecarConverter.getSidecarExecuteAppFunctionResponse(
platformResponse
)
- assertThat(sidecarResponse.isSuccess).isTrue()
assertThat(
sidecarResponse.resultDocument.getProperty(
ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE
)
)
.isEqualTo(booleanArrayOf(true))
- assertThat(sidecarResponse.resultCode).isEqualTo(ExecuteAppFunctionResponse.RESULT_OK)
- assertThat(sidecarResponse.errorMessage).isNull()
}
@Test
- fun getSidecarExecuteAppFunctionResponse_errorResponse_sameContents() {
- val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
- val platformResponse =
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
- null,
- null
+ fun getSidecarAppFunctionException_sameContents() {
+ val bundle = Bundle()
+ bundle.putString("key", "value")
+ val platformException =
+ AppFunctionException(
+ AppFunctionException.ERROR_SYSTEM_ERROR,
+ "error",
+ bundle
)
- val sidecarResponse = SidecarConverter.getSidecarExecuteAppFunctionResponse(
- platformResponse
+ val sidecarException = SidecarConverter.getSidecarAppFunctionException(
+ platformException
)
- assertThat(sidecarResponse.isSuccess).isFalse()
- assertThat(sidecarResponse.resultDocument.namespace).isEqualTo(emptyGd.namespace)
- assertThat(sidecarResponse.resultDocument.id).isEqualTo(emptyGd.id)
- assertThat(sidecarResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
- assertThat(sidecarResponse.resultCode)
- .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR)
- assertThat(sidecarResponse.errorMessage).isNull()
+ assertThat(sidecarException.errorCode).isEqualTo(AppFunctionException.ERROR_SYSTEM_ERROR)
+ assertThat(sidecarException.errorMessage).isEqualTo("error")
+ assertThat(sidecarException.extras.getString("key")).isEqualTo("value")
}
@Test
@@ -129,44 +124,39 @@ class SidecarConverterTest {
GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
.setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true)
.build()
- val sidecarResponse = com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse
- .newSuccess(resultGd, null)
+ val sidecarResponse =
+ com.android.extensions.appfunctions.ExecuteAppFunctionResponse(resultGd)
val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse(
sidecarResponse
)
- assertThat(platformResponse.isSuccess).isTrue()
assertThat(
platformResponse.resultDocument.getProperty(
ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE
)
)
.isEqualTo(booleanArrayOf(true))
- assertThat(platformResponse.resultCode).isEqualTo(ExecuteAppFunctionResponse.RESULT_OK)
- assertThat(platformResponse.errorMessage).isNull()
}
@Test
- fun getPlatformExecuteAppFunctionResponse_errorResponse_sameContents() {
- val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
- val sidecarResponse =
- com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
- null,
- null
+ fun getPlatformAppFunctionException_sameContents() {
+ val bundle = Bundle()
+ bundle.putString("key", "value")
+ val sidecarException =
+ com.android.extensions.appfunctions.AppFunctionException(
+ AppFunctionException.ERROR_SYSTEM_ERROR,
+ "error",
+ bundle
)
- val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse(
- sidecarResponse
+ val platformException = SidecarConverter.getPlatformAppFunctionException(
+ sidecarException
)
- assertThat(platformResponse.isSuccess).isFalse()
- assertThat(platformResponse.resultDocument.namespace).isEqualTo(emptyGd.namespace)
- assertThat(platformResponse.resultDocument.id).isEqualTo(emptyGd.id)
- assertThat(platformResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
- assertThat(platformResponse.resultCode)
- .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR)
- assertThat(platformResponse.errorMessage).isNull()
+ assertThat(platformException.errorCode)
+ .isEqualTo(AppFunctionException.ERROR_SYSTEM_ERROR)
+ assertThat(platformException.errorMessage).isEqualTo("error")
+ assertThat(platformException.extras.getString("key")).isEqualTo("value")
}
}
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index fddcf29b9197..5f84f47b725d 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -33,9 +33,9 @@ inline bool letter_spacing_justification() {
#endif // __ANDROID__
}
-inline bool typeface_redesign() {
+inline bool typeface_redesign_readonly() {
#ifdef __ANDROID__
- static bool flag = com_android_text_flags_typeface_redesign();
+ static bool flag = com_android_text_flags_typeface_redesign_readonly();
return flag;
#else
return true;
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index ae46a99f09c8..064cac2a6fc6 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -113,7 +113,6 @@ float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number
bool Properties::clipSurfaceViews = false;
bool Properties::hdr10bitPlus = false;
bool Properties::skipTelemetry = false;
-bool Properties::resampleGainmapRegions = false;
bool Properties::queryGlobalPriority = false;
int Properties::timeoutMultiplier = 1;
@@ -190,8 +189,6 @@ bool Properties::load() {
clipSurfaceViews =
base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews());
hdr10bitPlus = hwui_flags::hdr_10bit_plus();
- resampleGainmapRegions = base::GetBoolProperty("debug.hwui.resample_gainmap_regions",
- hwui_flags::resample_gainmap_regions());
queryGlobalPriority = hwui_flags::query_global_priority();
timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
@@ -288,5 +285,11 @@ bool Properties::initializeGlAlways() {
return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always());
}
+bool Properties::resampleGainmapRegions() {
+ static bool sResampleGainmapRegions = base::GetBoolProperty(
+ "debug.hwui.resample_gainmap_regions", hwui_flags::resample_gainmap_regions());
+ return sResampleGainmapRegions;
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6f84796fb11e..db930f3904de 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -345,7 +345,6 @@ public:
static bool clipSurfaceViews;
static bool hdr10bitPlus;
static bool skipTelemetry;
- static bool resampleGainmapRegions;
static bool queryGlobalPriority;
static int timeoutMultiplier;
@@ -381,6 +380,7 @@ public:
static void setDrawingEnabled(bool enable);
static bool initializeGlAlways();
+ static bool resampleGainmapRegions();
private:
static StretchEffectBehavior stretchEffectBehavior;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 5ad788c67816..fa27af671be6 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -154,3 +154,13 @@ flag {
description: "API's that enable animated image drawables to use nearest sampling when scaling."
bug: "370523334"
}
+
+flag {
+ name: "remove_vri_sketchy_destroy"
+ namespace: "core_graphics"
+ description: "Remove the eager yet thread-violating destroyHardwareResources in VRI#die"
+ bug: "377057106"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 9cd6e253140e..e5fb75575ac3 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -49,6 +49,7 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
minikinPaint.fontVariationSettings = paint->getFontVariationOverride();
+ minikinPaint.verticalText = paint->isVerticalText();
const std::optional<minikin::FamilyVariant>& familyVariant = paint->getFamilyVariant();
if (familyVariant.has_value()) {
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 1510ce1378d8..20acf981d9b9 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -73,7 +73,7 @@ public:
static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) {
float saveSkewX = paint->getSkFont().getSkewX();
bool savefakeBold = paint->getSkFont().isEmbolden();
- if (text_feature::typeface_redesign()) {
+ if (text_feature::typeface_redesign_readonly()) {
for (uint32_t runIdx = 0; runIdx < layout.getFontRunCount(); ++runIdx) {
uint32_t start = layout.getFontRunStart(runIdx);
uint32_t end = layout.getFontRunEnd(runIdx);
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 7eb849fe6e3d..594ea31387ad 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -158,6 +158,7 @@ public:
SkSamplingOptions sampling() const {
return SkSamplingOptions(this->filterMode());
}
+ bool isVerticalText() const { return mVerticalText; }
void setVariationOverride(minikin::VariationSettings&& varSettings) {
mFontVariationOverride = std::move(varSettings);
@@ -202,6 +203,7 @@ private:
bool mUnderline = false;
bool mDevKern = false;
minikin::RunFlag mRunFlag = minikin::RunFlag::NONE;
+ bool mVerticalText = false;
};
} // namespace android
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 6dfcedc3d918..fa5325d90218 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -49,7 +49,8 @@ Paint::Paint(const Paint& paint)
, mStrikeThru(paint.mStrikeThru)
, mUnderline(paint.mUnderline)
, mDevKern(paint.mDevKern)
- , mRunFlag(paint.mRunFlag) {}
+ , mRunFlag(paint.mRunFlag)
+ , mVerticalText(paint.mVerticalText) {}
Paint::~Paint() {}
@@ -71,6 +72,7 @@ Paint& Paint::operator=(const Paint& other) {
mUnderline = other.mUnderline;
mDevKern = other.mDevKern;
mRunFlag = other.mRunFlag;
+ mVerticalText = other.mVerticalText;
return *this;
}
@@ -83,7 +85,8 @@ bool operator==(const Paint& a, const Paint& b) {
a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
a.mFilterBitmap == b.mFilterBitmap && a.mStrikeThru == b.mStrikeThru &&
- a.mUnderline == b.mUnderline && a.mDevKern == b.mDevKern && a.mRunFlag == b.mRunFlag;
+ a.mUnderline == b.mUnderline && a.mDevKern == b.mDevKern && a.mRunFlag == b.mRunFlag &&
+ a.mVerticalText == b.mVerticalText;
}
void Paint::reset() {
@@ -97,6 +100,7 @@ void Paint::reset() {
mStrikeThru = false;
mUnderline = false;
mDevKern = false;
+ mVerticalText = false;
mRunFlag = minikin::RunFlag::NONE;
}
@@ -135,6 +139,7 @@ static const uint32_t sForceAutoHinting = 0x800;
// flags related to minikin::Paint
static const uint32_t sUnderlineFlag = 0x08;
static const uint32_t sStrikeThruFlag = 0x10;
+static const uint32_t sVerticalTextFlag = 0x1000;
static const uint32_t sTextRunLeftEdge = 0x2000;
static const uint32_t sTextRunRightEdge = 0x4000;
// flags no longer supported on native side (but mirrored for compatibility)
@@ -190,6 +195,7 @@ uint32_t Paint::getJavaFlags() const {
flags |= -(int)mUnderline & sUnderlineFlag;
flags |= -(int)mDevKern & sDevKernFlag;
flags |= -(int)mFilterBitmap & sFilterBitmapFlag;
+ flags |= -(int)mVerticalText & sVerticalTextFlag;
if (mRunFlag & minikin::RunFlag::LEFT_EDGE) {
flags |= sTextRunLeftEdge;
}
@@ -206,6 +212,7 @@ void Paint::setJavaFlags(uint32_t flags) {
mUnderline = (flags & sUnderlineFlag) != 0;
mDevKern = (flags & sDevKernFlag) != 0;
mFilterBitmap = (flags & sFilterBitmapFlag) != 0;
+ mVerticalText = (flags & sVerticalTextFlag) != 0;
std::underlying_type<minikin::RunFlag>::type rawFlag = minikin::RunFlag::NONE;
if (flags & sTextRunLeftEdge) {
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index 5ffd5b9016d8..491066b05952 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -112,9 +112,7 @@ public:
return false;
}
- // Round out the subset so that we decode a slightly larger region, in
- // case the subset has fractional components.
- SkIRect roundedSubset = desiredSubset.roundOut();
+ sampleSize = std::max(sampleSize, 1);
// Map the desired subset to the space of the decoded gainmap. The
// subset is repositioned relative to the resulting bitmap, and then
@@ -123,10 +121,51 @@ public:
// for existing gainmap formats.
SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()),
-std::floorf(desiredSubset.top()));
- logicalSubset.fLeft /= sampleSize;
- logicalSubset.fTop /= sampleSize;
- logicalSubset.fRight /= sampleSize;
- logicalSubset.fBottom /= sampleSize;
+ logicalSubset = scale(logicalSubset, 1.0f / sampleSize);
+
+ // Round out the subset so that we decode a slightly larger region, in
+ // case the subset has fractional components. When we round, we need to
+ // round the downsampled subset to avoid possibly rounding down by accident.
+ // Consider this concrete example if we round the desired subset directly:
+ //
+ // * We are decoding a 18x18 corner of an image
+ //
+ // * Gainmap is 1/4 resolution, which is logically a 4.5x4.5 gainmap
+ // that we would want
+ //
+ // * The app wants to downsample by a factor of 2x
+ //
+ // * The desired gainmap dimensions are computed to be 3x3 to fit the
+ // downsampled gainmap, since we need to fill a 2.25x2.25 region that's
+ // later upscaled to 3x3
+ //
+ // * But, if we round out the desired gainmap region _first_, then we
+ // request to decode a 5x5 region, downsampled by 2, which actually
+ // decodes a 2x2 region since skia rounds down internally. But then we transfer
+ // the result to a 3x3 bitmap using a clipping allocator, which leaves an inset.
+ // Not only did we get a smaller region than we expected (so, our desired subset is
+ // not valid), but because the API allows for decoding regions using a recycled
+ // bitmap, we can't really safely fill in the inset since then we might
+ // extend the gainmap beyond intended the image bounds. Oops.
+ //
+ // * If we instead round out as if we downsampled, then we downsample
+ // the desired region to 2.25x2.25, round out to 3x3, then upsample back
+ // into the source gainmap space to get 6x6. Then we decode a 6x6 region
+ // downsampled into a 3x3 region, and everything's now correct.
+ //
+ // Note that we don't always run into this problem, because
+ // decoders actually round *up* for subsampling when decoding a subset
+ // that matches the dimensions of the image. E.g., if the original image
+ // size in the above example was a 20x20 image, so that the gainmap was
+ // 5x5, then we still manage to downsample into a 3x3 bitmap even with
+ // the "wrong" math. but that's what we wanted!
+ //
+ // Note also that if we overshoot the gainmap bounds with the requested
+ // subset it isn't a problem either, since now the decoded bitmap is too
+ // large, rather than too small, so now we can use the desired subset to
+ // avoid sampling "invalid" colors.
+ SkRect scaledSubset = scale(desiredSubset, 1.0f / sampleSize);
+ SkIRect roundedSubset = scale(scaledSubset.roundOut(), static_cast<float>(sampleSize));
RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset);
if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType,
@@ -154,7 +193,7 @@ public:
const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width();
const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height();
- if (uirenderer::Properties::resampleGainmapRegions) {
+ if (uirenderer::Properties::resampleGainmapRegions()) {
const auto srcRect = SkRect::MakeLTRB(
mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY);
@@ -186,6 +225,22 @@ private:
, mGainmapBRD(std::move(gainmapBRD))
, mGainmapInfo(info) {}
+ SkRect scale(SkRect rect, float scale) const {
+ rect.fLeft *= scale;
+ rect.fTop *= scale;
+ rect.fRight *= scale;
+ rect.fBottom *= scale;
+ return rect;
+ }
+
+ SkIRect scale(SkIRect rect, float scale) const {
+ rect.fLeft *= scale;
+ rect.fTop *= scale;
+ rect.fRight *= scale;
+ rect.fBottom *= scale;
+ return rect;
+ }
+
std::unique_ptr<skia::BitmapRegionDecoder> mMainImageBRD;
std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD;
SkGainmapInfo mGainmapInfo;
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 258bf91f2124..a210ddf54b2e 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -750,7 +750,7 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() {
std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample(
std::optional<SkRect> subset) {
- if (!uirenderer::Properties::resampleGainmapRegions || !subset || subset->isEmpty()) {
+ if (!uirenderer::Properties::resampleGainmapRegions() || !subset || subset->isEmpty()) {
return std::nullopt;
}
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 70e6beda6cb9..5f693462af91 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -86,7 +86,7 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou
overallDescent = std::max(overallDescent, extent.descent);
}
- if (text_feature::typeface_redesign()) {
+ if (text_feature::typeface_redesign_readonly()) {
uint32_t runCount = layout.getFontRunCount();
std::unordered_map<minikin::FakedFont, uint32_t, FakedFontKey> fakedToFontIds;
@@ -229,7 +229,7 @@ float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin:
// CriticalNative
static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
- if (text_feature::typeface_redesign()) {
+ if (text_feature::typeface_redesign_readonly()) {
float value =
findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght);
return std::isnan(value) ? NO_OVERRIDE : value;
@@ -241,7 +241,7 @@ static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlon
// CriticalNative
static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
- if (text_feature::typeface_redesign()) {
+ if (text_feature::typeface_redesign_readonly()) {
float value =
findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital);
return std::isnan(value) ? NO_OVERRIDE : value;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 1db719828c76..5b9054797bed 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -16,11 +16,15 @@
package android.media;
+import static android.media.audio.Flags.FLAG_SPEAKER_CLEANUP_USAGE;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+// TODO switch from HIDL imports to AIDL
import android.audio.policy.configuration.V7_0.AudioUsage;
import android.compat.annotation.UnsupportedAppUsage;
import android.media.audiopolicy.AudioProductStrategy;
@@ -247,6 +251,16 @@ public final class AudioAttributes implements Parcelable {
public static final int USAGE_ANNOUNCEMENT = SYSTEM_USAGE_OFFSET + 3;
/**
+ * @hide
+ * Usage value to use when a system application plays a signal intended to clean up the
+ * speaker transducers and free them of deposits of dust or water.
+ */
+ @FlaggedApi(FLAG_SPEAKER_CLEANUP_USAGE)
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int USAGE_SPEAKER_CLEANUP = SYSTEM_USAGE_OFFSET + 4;
+
+ /**
* IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
* if applicable, as well as audioattributes.proto.
* Also consider adding them to <aaudio/AAudio.h> for the NDK.
@@ -1521,6 +1535,8 @@ public final class AudioAttributes implements Parcelable {
return "USAGE_VEHICLE_STATUS";
case USAGE_ANNOUNCEMENT:
return "USAGE_ANNOUNCEMENT";
+ case USAGE_SPEAKER_CLEANUP:
+ return "USAGE_SPEAKER_CLEANUP";
default:
return "unknown usage " + usage;
}
@@ -1661,12 +1677,8 @@ public final class AudioAttributes implements Parcelable {
}
/**
- * @param usage one of {@link AttributeSystemUsage},
- * {@link AttributeSystemUsage#USAGE_CALL_ASSISTANT},
- * {@link AttributeSystemUsage#USAGE_EMERGENCY},
- * {@link AttributeSystemUsage#USAGE_SAFETY},
- * {@link AttributeSystemUsage#USAGE_VEHICLE_STATUS},
- * {@link AttributeSystemUsage#USAGE_ANNOUNCEMENT}
+ * Returns whether the given usage can only be used by system-privileged components
+ * @param usage one of {@link AttributeSystemUsage}.
* @return boolean indicating if the usage is a system usage or not
* @hide
*/
@@ -1676,7 +1688,8 @@ public final class AudioAttributes implements Parcelable {
|| usage == USAGE_EMERGENCY
|| usage == USAGE_SAFETY
|| usage == USAGE_VEHICLE_STATUS
- || usage == USAGE_ANNOUNCEMENT);
+ || usage == USAGE_ANNOUNCEMENT
+ || usage == USAGE_SPEAKER_CLEANUP);
}
/**
@@ -1790,6 +1803,7 @@ public final class AudioAttributes implements Parcelable {
case USAGE_SAFETY:
case USAGE_VEHICLE_STATUS:
case USAGE_ANNOUNCEMENT:
+ case USAGE_SPEAKER_CLEANUP:
case USAGE_UNKNOWN:
return AudioSystem.STREAM_MUSIC;
default:
@@ -1829,7 +1843,8 @@ public final class AudioAttributes implements Parcelable {
USAGE_EMERGENCY,
USAGE_SAFETY,
USAGE_VEHICLE_STATUS,
- USAGE_ANNOUNCEMENT
+ USAGE_ANNOUNCEMENT,
+ USAGE_SPEAKER_CLEANUP
})
@Retention(RetentionPolicy.SOURCE)
public @interface AttributeSystemUsage {}
@@ -1879,6 +1894,7 @@ public final class AudioAttributes implements Parcelable {
USAGE_SAFETY,
USAGE_VEHICLE_STATUS,
USAGE_ANNOUNCEMENT,
+ USAGE_SPEAKER_CLEANUP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AttributeUsage {}
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 55c8ed5debf8..530d48d3e60b 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -40,6 +40,8 @@ import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import dalvik.system.VMRuntime;
import java.io.IOException;
@@ -1208,6 +1210,11 @@ public class ImageReader implements AutoCloseable {
default:
width = nativeGetWidth();
}
+ if (Flags.cameraHeifGainmap()) {
+ if (getFormat() == ImageFormat.HEIC_ULTRAHDR){
+ width = ImageReader.this.getWidth();
+ }
+ }
return width;
}
@@ -1227,6 +1234,11 @@ public class ImageReader implements AutoCloseable {
default:
height = nativeGetHeight();
}
+ if (Flags.cameraHeifGainmap()) {
+ if (getFormat() == ImageFormat.HEIC_ULTRAHDR){
+ height = ImageReader.this.getHeight();
+ }
+ }
return height;
}
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index f4caad727407..c7678067f0b5 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -23,6 +23,8 @@ import android.media.Image.Plane;
import android.util.Log;
import android.util.Size;
+import com.android.internal.camera.flags.Flags;
+
import libcore.io.Memory;
import java.nio.ByteBuffer;
@@ -44,6 +46,11 @@ class ImageUtils {
* are used.
*/
public static int getNumPlanesForFormat(int format) {
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ return 1;
+ }
+ }
switch (format) {
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
@@ -229,6 +236,11 @@ class ImageUtils {
public static int getEstimatedNativeAllocBytes(int width, int height, int format,
int numImages) {
double estimatedBytePerPixel;
+ if (Flags.cameraHeifGainmap()) {
+ if (format == ImageFormat.HEIC_ULTRAHDR) {
+ estimatedBytePerPixel = 0.3;
+ }
+ }
switch (format) {
// 10x compression from RGB_888
case ImageFormat.JPEG:
@@ -283,6 +295,11 @@ class ImageUtils {
}
private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
+ if (Flags.cameraHeifGainmap()) {
+ if (image.getFormat() == ImageFormat.HEIC_ULTRAHDR){
+ return new Size(image.getWidth(), image.getHeight());
+ }
+ }
switch (image.getFormat()) {
case ImageFormat.YCBCR_P010:
case ImageFormat.YV12:
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 88efed55c11f..3f9126aa9456 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -1000,7 +1000,10 @@ public final class MediaCas implements AutoCloseable {
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public boolean updateResourcePriority(int priority, int niceValue) {
- return mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
+ if (mTunerResourceManager != null) {
+ return mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
+ }
+ return false;
}
/**
@@ -1017,7 +1020,9 @@ public final class MediaCas implements AutoCloseable {
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public void setResourceHolderRetain(boolean resourceHolderRetain) {
- mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain);
+ if (mTunerResourceManager != null) {
+ mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain);
+ }
}
IHwBinder getBinder() {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index e575daeb8d29..2ae89d3300c1 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -18,6 +18,7 @@ package android.media;
import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_SUBSESSION_METRICS;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -890,7 +891,7 @@ import java.util.function.Supplier;
any start codes), and submit it as a <strong>regular</strong> input buffer.
<p>
You will receive an {@link #INFO_OUTPUT_FORMAT_CHANGED} return value from {@link
- #dequeueOutputBuffer dequeueOutputBuffer} or a {@link Callback#onOutputBufferAvailable
+ #dequeueOutputBuffer dequeueOutputBuffer} or a {@link Callback#onOutputFormatChanged
onOutputFormatChanged} callback just after the picture-size change takes place and before any
frames with the new size have been returned.
<p class=note>
@@ -1835,6 +1836,13 @@ final public class MediaCodec {
private static final int CB_CRYPTO_ERROR = 6;
private static final int CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7;
+ /**
+ * Callback ID for when the metrics for this codec have been flushed due to
+ * the start of a new subsession. The associated Java Message object will
+ * contain the flushed metrics as a PersistentBundle in the obj field.
+ */
+ private static final int CB_METRICS_FLUSHED = 8;
+
private class EventHandler extends Handler {
private MediaCodec mCodec;
@@ -2007,6 +2015,15 @@ final public class MediaCodec {
break;
}
+ case CB_METRICS_FLUSHED:
+ {
+
+ if (GetFlag(() -> android.media.codec.Flags.subsessionMetrics())) {
+ mCallback.onMetricsFlushed(mCodec, (PersistableBundle)msg.obj);
+ }
+ break;
+ }
+
default:
{
break;
@@ -4958,14 +4975,24 @@ final public class MediaCodec {
public native final String getCanonicalName();
/**
- * Return Metrics data about the current codec instance.
+ * Return Metrics data about the current codec instance.
+ * <p>
+ * Call this method after configuration, during execution, or after
+ * the codec has been already stopped.
+ * <p>
+ * Beginning with {@link android.os.Build.VERSION_CODES#B}
+ * this method can be used to get the Metrics data prior to an error.
+ * (e.g. in {@link Callback#onError} or after a method throws
+ * {@link MediaCodec.CodecException}.) Before that, the Metrics data was
+ * cleared on error, resulting in a null return value.
*
* @return a {@link PersistableBundle} containing the set of attributes and values
* available for the media being handled by this instance of MediaCodec
* The attributes are descibed in {@link MetricsConstants}.
*
* Additional vendor-specific fields may also be present in
- * the return value.
+ * the return value. Returns null if there is no Metrics data.
+ *
*/
public PersistableBundle getMetrics() {
PersistableBundle bundle = native_getMetrics();
@@ -5692,6 +5719,27 @@ final public class MediaCodec {
*/
public abstract void onOutputFormatChanged(
@NonNull MediaCodec codec, @NonNull MediaFormat format);
+
+ /**
+ * Called when the metrics for this codec have been flushed due to the
+ * start of a new subsession.
+ * <p>
+ * This can happen when the codec is reconfigured after stop(), or
+ * mid-stream e.g. if the video size changes. When this happens, the
+ * metrics for the previous subsession are flushed, and
+ * {@link MediaCodec#getMetrics} will return the metrics for the
+ * new subsession. This happens just before the {@link Callback#onOutputFormatChanged}
+ * event, so this <b>optional</b> callback is provided to be able to
+ * capture the final metrics for the previous subsession.
+ *
+ * @param codec The MediaCodec object.
+ * @param metrics The flushed metrics for this codec.
+ */
+ @FlaggedApi(FLAG_SUBSESSION_METRICS)
+ public void onMetricsFlushed(
+ @NonNull MediaCodec codec, @NonNull PersistableBundle metrics) {
+ // default implementation ignores this callback.
+ }
}
private void postEventFromNative(
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b08a86ee8f46..bd65b2ecb76a 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -17,6 +17,7 @@
package android.media;
import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
+import static android.media.codec.Flags.FLAG_NUM_INPUT_SLOTS;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
import static android.media.codec.Flags.FLAG_APV_SUPPORT;
@@ -1777,6 +1778,17 @@ public final class MediaFormat {
public static final String KEY_SECURITY_MODEL = "security-model";
/**
+ * A key describing the number of slots used in the codec. When present in input format,
+ * the associated value indicates the number of input slots. The entry is set by the codec
+ * if configured with (@link MediaCodec#CONFIGURE_FLAG_BLOCK_MODEL), and will be ignored if set
+ * by the application.
+ * <p>
+ * The associated value is an integer.
+ */
+ @FlaggedApi(FLAG_NUM_INPUT_SLOTS)
+ public static final String KEY_NUM_SLOTS = "num-slots";
+
+ /**
* QpOffsetRect constitutes the metadata required for encoding a region of interest in an
* image or a video frame. The region of interest is represented by a rectangle. The four
* integer coordinates of the rectangle are stored in fields left, top, right, bottom.
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 816729da5ad4..09022782e6c3 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -22,6 +22,7 @@ import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE;
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -150,6 +151,9 @@ public final class MediaRoute2Info implements Parcelable {
TYPE_HDMI,
TYPE_HDMI_ARC,
TYPE_HDMI_EARC,
+ TYPE_LINE_DIGITAL,
+ TYPE_LINE_ANALOG,
+ TYPE_AUX_LINE,
TYPE_USB_DEVICE,
TYPE_USB_ACCESSORY,
TYPE_DOCK,
@@ -231,6 +235,24 @@ public final class MediaRoute2Info implements Parcelable {
public static final int TYPE_HDMI_EARC = AudioDeviceInfo.TYPE_HDMI_EARC;
/**
+ * Indicates the route is a digital line connection (for example S/PDIF).
+ */
+ @FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
+ public static final int TYPE_LINE_DIGITAL = AudioDeviceInfo.TYPE_LINE_DIGITAL;
+
+ /**
+ * Indicates the route is an analog line-level connection.
+ */
+ @FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
+ public static final int TYPE_LINE_ANALOG = AudioDeviceInfo.TYPE_LINE_ANALOG;
+
+ /**
+ * Indicates the route is using the auxiliary line-level connectors.
+ */
+ @FlaggedApi(FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES)
+ public static final int TYPE_AUX_LINE = AudioDeviceInfo.TYPE_AUX_LINE;
+
+ /**
* Indicates the route is a USB audio device.
*
* @see #getType
@@ -839,6 +861,7 @@ public final class MediaRoute2Info implements Parcelable {
public boolean isSystemRouteType() {
return switch (mType) {
case TYPE_BUILTIN_SPEAKER,
+ TYPE_AUX_LINE,
TYPE_BLUETOOTH_A2DP,
TYPE_DOCK,
TYPE_BLE_HEADSET,
@@ -846,6 +869,8 @@ public final class MediaRoute2Info implements Parcelable {
TYPE_HDMI,
TYPE_HDMI_ARC,
TYPE_HDMI_EARC,
+ TYPE_LINE_DIGITAL,
+ TYPE_LINE_ANALOG,
TYPE_USB_ACCESSORY,
TYPE_USB_DEVICE,
TYPE_USB_HEADSET,
@@ -1074,6 +1099,12 @@ public final class MediaRoute2Info implements Parcelable {
return "HDMI_ARC";
case TYPE_HDMI_EARC:
return "HDMI_EARC";
+ case TYPE_LINE_DIGITAL:
+ return "LINE_DIGITAL";
+ case TYPE_LINE_ANALOG:
+ return "LINE_ANALOG";
+ case TYPE_AUX_LINE:
+ return "AUX_LINE";
case TYPE_DOCK:
return "DOCK";
case TYPE_USB_DEVICE:
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 3499c438086d..20108e7369d7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -1771,10 +1771,12 @@ public final class MediaRouter2 {
}
/**
- * A class to control media routing session in media route provider. For example,
- * selecting/deselecting/transferring to routes of a session can be done through this. Instances
- * are created when {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is
- * called, which is invoked after {@link #transferTo(MediaRoute2Info)} is called.
+ * Controls a media routing session.
+ *
+ * <p>Routing controllers wrap a {@link RoutingSessionInfo}, taking care of mapping route ids to
+ * {@link MediaRoute2Info} instances. You can still access the underlying session using {@link
+ * #getRoutingSessionInfo()}, but keep in mind it can be changed by other threads. Changes to
+ * the routing session are notified via {@link ControllerCallback}.
*/
public class RoutingController {
private final Object mControllerLock = new Object();
@@ -1836,7 +1838,9 @@ public final class MediaRouter2 {
}
/**
- * @return the unmodifiable list of currently selected routes
+ * Returns the unmodifiable list of currently selected routes
+ *
+ * @see RoutingSessionInfo#getSelectedRoutes()
*/
@NonNull
public List<MediaRoute2Info> getSelectedRoutes() {
@@ -1848,7 +1852,9 @@ public final class MediaRouter2 {
}
/**
- * @return the unmodifiable list of selectable routes for the session.
+ * Returns the unmodifiable list of selectable routes for the session.
+ *
+ * @see RoutingSessionInfo#getSelectableRoutes()
*/
@NonNull
public List<MediaRoute2Info> getSelectableRoutes() {
@@ -1860,7 +1866,9 @@ public final class MediaRouter2 {
}
/**
- * @return the unmodifiable list of deselectable routes for the session.
+ * Returns the unmodifiable list of deselectable routes for the session.
+ *
+ * @see RoutingSessionInfo#getDeselectableRoutes()
*/
@NonNull
public List<MediaRoute2Info> getDeselectableRoutes() {
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 83a4dd5a682a..3b8cf3fb2909 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -262,7 +262,8 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
- * Gets the provider id of the session.
+ * Gets the provider ID of the session.
+ *
* @hide
*/
@Nullable
@@ -271,7 +272,15 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
- * Gets the list of IDs of selected routes for the session. It shouldn't be empty.
+ * Gets the list of IDs of selected routes for the session.
+ *
+ * <p>Selected routes are the routes that this session is actively routing media to.
+ *
+ * <p>The behavior of a routing session with multiple selected routes is ultimately defined by
+ * the {@link MediaRoute2ProviderService} implementation. However, typically, it's expected that
+ * all the selected routes of a routing session are playing the same media in sync.
+ *
+ * @return A non-empty list of selected route ids.
*/
@NonNull
public List<String> getSelectedRoutes() {
@@ -280,6 +289,16 @@ public final class RoutingSessionInfo implements Parcelable {
/**
* Gets the list of IDs of selectable routes for the session.
+ *
+ * <p>Selectable routes can be added to a routing session (via {@link
+ * MediaRouter2.RoutingController#selectRoute}) in order to add them to the {@link
+ * #getSelectedRoutes() selected routes}, so that media plays on the newly selected route along
+ * with the other selected routes.
+ *
+ * <p>Not to be confused with {@link #getTransferableRoutes() transferable routes}. Transferring
+ * to a route makes it the sole selected route.
+ *
+ * @return A possibly empty list of selectable route ids.
*/
@NonNull
public List<String> getSelectableRoutes() {
@@ -288,6 +307,17 @@ public final class RoutingSessionInfo implements Parcelable {
/**
* Gets the list of IDs of deselectable routes for the session.
+ *
+ * <p>Deselectable routes can be removed from the {@link #getSelectedRoutes() selected routes},
+ * so that the routing session stops routing to the newly deselected route, but continues on any
+ * remaining selected routes.
+ *
+ * <p>Deselectable routes should be a subset of the {@link #getSelectedRoutes() selected
+ * routes}, meaning not all of the selected routes might be deselectable. For example, one of
+ * the selected routes may be a leader device coordinating group playback, which must always
+ * remain selected while the session is active.
+ *
+ * @return A possibly empty list of deselectable route ids.
*/
@NonNull
public List<String> getDeselectableRoutes() {
@@ -296,6 +326,24 @@ public final class RoutingSessionInfo implements Parcelable {
/**
* Gets the list of IDs of transferable routes for the session.
+ *
+ * <p>Transferring to a route (for example, using {@link MediaRouter2#transferTo}) replaces the
+ * list of {@link #getSelectedRoutes() selected routes} with the target route, causing playback
+ * to move from one route to another.
+ *
+ * <p>Note that this is different from {@link #getSelectableRoutes() selectable routes}, because
+ * selecting a route makes it part of the selected routes, while transferring to a route makes
+ * it the selected route. A route can be both transferable and selectable.
+ *
+ * <p>Note that playback may transfer across routes without the target route being in the list
+ * of transferable routes. This can happen by creating a new routing session to the target
+ * route, and releasing the routing session being transferred from. The difference is that a
+ * transfer to a route in the transferable list can happen with no intervention from the app,
+ * with the route provider taking care of the entire operation. A transfer to a route that is
+ * not in the list of transferable routes (by creating a new session) requires the app to move
+ * the playback state from one device to the other.
+ *
+ * @return A possibly empty list of transferable route ids.
*/
@NonNull
public List<String> getTransferableRoutes() {
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index c1d73f9033cf..8521d1c472a8 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -705,6 +705,10 @@ public class AidlConversion {
aidl.type = AudioDeviceType.OUT_BROADCAST;
aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE;
break;
+ case AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP:
+ aidl.type = AudioDeviceType.OUT_MULTICHANNEL_GROUP;
+ aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
+ break;
case AudioSystem.DEVICE_IN_BUILTIN_MIC:
aidl.type = AudioDeviceType.IN_MICROPHONE;
break;
diff --git a/media/java/android/media/flags/editing.aconfig b/media/java/android/media/flags/editing.aconfig
index 185f579df4b9..0adc4783445a 100644
--- a/media/java/android/media/flags/editing.aconfig
+++ b/media/java/android/media/flags/editing.aconfig
@@ -15,3 +15,10 @@ flag {
description: "Enable B frames for Stagefright recorder."
bug: "341121900"
}
+
+flag {
+ name: "muxer_mp4_enable_apv"
+ namespace: "media_solutions"
+ description: "Enable APV support in mp4 writer."
+ bug: "370061501"
+}
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 1ef98f2c2644..7895eb27b372 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -47,6 +47,14 @@ flag {
}
flag {
+ name: "enable_new_wired_media_route_2_info_types"
+ is_exported: true
+ namespace: "media_tv"
+ description: "Enables the following type constant in MediaRoute2Info: LINE_ANALOG, LINE_DIGITAL, AUX_LINE"
+ bug: "375691732"
+}
+
+flag {
name: "enable_privileged_routing_for_media_routing_control"
is_exported: true
namespace: "media_solutions"
@@ -71,7 +79,7 @@ flag {
flag {
name: "update_client_profile_priority"
- namespace: "media"
+ namespace: "media_solutions"
description : "Feature flag to add updateResourcePriority api to MediaCas"
bug: "300565729"
}
@@ -158,3 +166,10 @@ flag {
description: "Allows audio input devices routing and volume control via system settings."
bug: "355684672"
}
+
+flag {
+ name: "enable_mirroring_in_media_router_2"
+ namespace: "media_better_together"
+ description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
+ bug: "362507305"
+}
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.aidl b/media/java/android/media/quality/AmbientBacklightEvent.aidl
new file mode 100644
index 000000000000..174cd461e846
--- /dev/null
+++ b/media/java/android/media/quality/AmbientBacklightEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+parcelable AmbientBacklightEvent;
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.java b/media/java/android/media/quality/AmbientBacklightEvent.java
new file mode 100644
index 000000000000..5c11def43209
--- /dev/null
+++ b/media/java/android/media/quality/AmbientBacklightEvent.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.tv.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Ambient backlight event
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class AmbientBacklightEvent implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({AMBIENT_BACKLIGHT_EVENT_ENABLED, AMBIENT_BACKLIGHT_EVENT_DISABLED,
+ AMBIENT_BACKLIGHT_EVENT_METADATA,
+ AMBIENT_BACKLIGHT_EVENT_INTERRUPTED})
+ public @interface AmbientBacklightEventTypes {}
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight is enabled.
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_ENABLED = 1;
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight is disabled.
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_DISABLED = 2;
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight metadata is
+ * available.
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_METADATA = 3;
+
+ /**
+ * Event type for ambient backlight events. The ambient backlight event is preempted by another
+ * application.
+ */
+ public static final int AMBIENT_BACKLIGHT_EVENT_INTERRUPTED = 4;
+
+ private final int mEventType;
+ @Nullable
+ private final AmbientBacklightMetadata mMetadata;
+
+ /**
+ * Constructor of AmbientBacklightEvent.
+ */
+ public AmbientBacklightEvent(int eventType,
+ @Nullable AmbientBacklightMetadata metadata) {
+ mEventType = eventType;
+ mMetadata = metadata;
+ }
+
+ private AmbientBacklightEvent(Parcel in) {
+ mEventType = in.readInt();
+ mMetadata = in.readParcelable(AmbientBacklightMetadata.class.getClassLoader());
+ }
+
+ /**
+ * Gets event type.
+ */
+ public int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * Gets ambient backlight metadata.
+ *
+ * @return the metadata of the event. It's non-null only for
+ * {@link #AMBIENT_BACKLIGHT_EVENT_METADATA}.
+ */
+ @Nullable
+ public AmbientBacklightMetadata getMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mEventType);
+ dest.writeParcelable(mMetadata, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AmbientBacklightEvent> CREATOR =
+ new Parcelable.Creator<AmbientBacklightEvent>() {
+ public AmbientBacklightEvent createFromParcel(Parcel in) {
+ return new AmbientBacklightEvent(in);
+ }
+
+ public AmbientBacklightEvent[] newArray(int size) {
+ return new AmbientBacklightEvent[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof AmbientBacklightEvent)) {
+ return false;
+ }
+
+ AmbientBacklightEvent other = (AmbientBacklightEvent) obj;
+ return mEventType == other.mEventType
+ && Objects.equals(mMetadata, other.mMetadata);
+ }
+
+ @Override
+ public int hashCode() {
+ return mEventType * 31 + (mMetadata != null ? mMetadata.hashCode() : 0);
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientBacklightEvent{"
+ + "mEventType=" + mEventType
+ + ", mMetadata=" + mMetadata
+ + '}';
+ }
+}
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.aidl b/media/java/android/media/quality/AmbientBacklightMetadata.aidl
new file mode 100644
index 000000000000..b95a474fbf90
--- /dev/null
+++ b/media/java/android/media/quality/AmbientBacklightMetadata.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+parcelable AmbientBacklightMetadata; \ No newline at end of file
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.java b/media/java/android/media/quality/AmbientBacklightMetadata.java
new file mode 100644
index 000000000000..9c11f9a3e560
--- /dev/null
+++ b/media/java/android/media/quality/AmbientBacklightMetadata.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.graphics.PixelFormat;
+import android.media.tv.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+
+/**
+ * Metadata of ambient backlight.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class AmbientBacklightMetadata implements Parcelable {
+ @NonNull
+ private final String mPackageName;
+ private final int mCompressAlgorithm;
+ private final int mSource;
+ private final int mColorFormat;
+ private final int mHorizontalZonesNumber;
+ private final int mVerticalZonesNumber;
+ @NonNull
+ private final int[] mZonesColors;
+
+ /**
+ * Constructor of AmbientBacklightMetadata.
+ */
+ public AmbientBacklightMetadata(@NonNull String packageName, int compressAlgorithm,
+ int source, int colorFormat, int horizontalZonesNumber, int verticalZonesNumber,
+ @NonNull int[] zonesColors) {
+ mPackageName = packageName;
+ mCompressAlgorithm = compressAlgorithm;
+ mSource = source;
+ mColorFormat = colorFormat;
+ mHorizontalZonesNumber = horizontalZonesNumber;
+ mVerticalZonesNumber = verticalZonesNumber;
+ mZonesColors = zonesColors;
+ }
+
+ private AmbientBacklightMetadata(Parcel in) {
+ mPackageName = in.readString();
+ mCompressAlgorithm = in.readInt();
+ mSource = in.readInt();
+ mColorFormat = in.readInt();
+ mHorizontalZonesNumber = in.readInt();
+ mVerticalZonesNumber = in.readInt();
+ mZonesColors = in.createIntArray();
+ }
+
+ /**
+ * Gets package name.
+ * @hide
+ */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Gets compress algorithm.
+ */
+ @AmbientBacklightSettings.CompressAlgorithm
+ public int getCompressAlgorithm() {
+ return mCompressAlgorithm;
+ }
+
+ /**
+ * Gets source of ambient backlight detection.
+ */
+ @AmbientBacklightSettings.Source
+ public int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Gets color format.
+ */
+ @PixelFormat.Format
+ public int getColorFormat() {
+ return mColorFormat;
+ }
+
+ /**
+ * Gets the number of lights in each horizontal zone.
+ */
+ @IntRange(from = 0)
+ public int getHorizontalZonesNumber() {
+ return mHorizontalZonesNumber;
+ }
+
+ /**
+ * Gets the number of lights in each vertical zone.
+ */
+ @IntRange(from = 0)
+ public int getVerticalZonesNumber() {
+ return mVerticalZonesNumber;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public int[] getZonesColors() {
+ return mZonesColors;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeInt(mCompressAlgorithm);
+ dest.writeInt(mSource);
+ dest.writeInt(mColorFormat);
+ dest.writeInt(mHorizontalZonesNumber);
+ dest.writeInt(mVerticalZonesNumber);
+ dest.writeIntArray(mZonesColors);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<AmbientBacklightMetadata> CREATOR =
+ new Parcelable.Creator<AmbientBacklightMetadata>() {
+ public AmbientBacklightMetadata createFromParcel(Parcel in) {
+ return new AmbientBacklightMetadata(in);
+ }
+
+ public AmbientBacklightMetadata[] newArray(int size) {
+ return new AmbientBacklightMetadata[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "AmbientBacklightMetadata{packageName=" + mPackageName
+ + ", compressAlgorithm=" + mCompressAlgorithm + ", source=" + mSource
+ + ", colorFormat=" + mColorFormat + ", horizontalZonesNumber="
+ + mHorizontalZonesNumber + ", verticalZonesNumber=" + mVerticalZonesNumber
+ + ", zonesColors=" + Arrays.toString(mZonesColors) + "}";
+ }
+}
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.aidl b/media/java/android/media/quality/AmbientBacklightSettings.aidl
new file mode 100644
index 000000000000..e2cdd03194cd
--- /dev/null
+++ b/media/java/android/media/quality/AmbientBacklightSettings.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+parcelable AmbientBacklightSettings;
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.java b/media/java/android/media/quality/AmbientBacklightSettings.java
new file mode 100644
index 000000000000..4ed7bc79fdca
--- /dev/null
+++ b/media/java/android/media/quality/AmbientBacklightSettings.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.graphics.PixelFormat;
+import android.media.tv.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Settings for ambient backlight.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class AmbientBacklightSettings implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SOURCE_NONE, SOURCE_AUDIO, SOURCE_VIDEO, SOURCE_AUDIO_VIDEO})
+ public @interface Source {}
+
+ /**
+ * The detection is disabled.
+ */
+ public static final int SOURCE_NONE = 0;
+
+ /**
+ * The detection is enabled for audio.
+ */
+ public static final int SOURCE_AUDIO = 1;
+
+ /**
+ * The detection is enabled for video.
+ */
+ public static final int SOURCE_VIDEO = 2;
+
+ /**
+ * The detection is enabled for audio and video.
+ */
+ public static final int SOURCE_AUDIO_VIDEO = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({COLOR_FORMAT_RGB888})
+ public @interface ColorFormat {}
+
+ /**
+ * The color format is RGB888.
+ * @hide
+ */
+ public static final int COLOR_FORMAT_RGB888 = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ALGORITHM_NONE, ALGORITHM_RLE})
+ public @interface CompressAlgorithm {}
+
+ /**
+ * The compress algorithm is disabled.
+ */
+ public static final int ALGORITHM_NONE = 0;
+
+ /**
+ * The compress algorithm is run length encoding (RLE).
+ */
+ public static final int ALGORITHM_RLE = 1;
+
+ /**
+ * The source of the ambient backlight.
+ */
+ private final int mSource;
+
+ /**
+ * The maximum framerate for the ambient backlight.
+ */
+ private final int mMaxFps;
+
+ /**
+ * The color format for the ambient backlight.
+ */
+ private final int mColorFormat;
+
+ /**
+ * The number of zones in horizontal direction.
+ */
+ private final int mHorizontalZonesNumber;
+
+ /**
+ * The number of zones in vertical direction.
+ */
+ private final int mVerticalZonesNumber;
+
+ /**
+ * The flag to indicate whether the letterbox is omitted.
+ */
+ private final boolean mIsLetterboxOmitted;
+
+ /**
+ * The color threshold for the ambient backlight.
+ */
+ private final int mThreshold;
+
+ /**
+ * Constructs AmbientBacklightSettings.
+ */
+ public AmbientBacklightSettings(int source, int maxFps, int colorFormat,
+ int horizontalZonesNumber, int verticalZonesNumber, boolean isLetterboxOmitted,
+ int threshold) {
+ mSource = source;
+ mMaxFps = maxFps;
+ mColorFormat = colorFormat;
+ mHorizontalZonesNumber = horizontalZonesNumber;
+ mVerticalZonesNumber = verticalZonesNumber;
+ mIsLetterboxOmitted = isLetterboxOmitted;
+ mThreshold = threshold;
+ }
+
+ private AmbientBacklightSettings(Parcel in) {
+ mSource = in.readInt();
+ mMaxFps = in.readInt();
+ mColorFormat = in.readInt();
+ mHorizontalZonesNumber = in.readInt();
+ mVerticalZonesNumber = in.readInt();
+ mIsLetterboxOmitted = in.readBoolean();
+ mThreshold = in.readInt();
+ }
+
+ /**
+ * Gets source of ambient backlight detection.
+ */
+ @Source
+ public int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Gets max frames per second.
+ */
+ @IntRange(from = 1)
+ public int getMaxFps() {
+ return mMaxFps;
+ }
+
+ /**
+ * Gets color format.
+ */
+ @PixelFormat.Format
+ public int getColorFormat() {
+ return mColorFormat;
+ }
+
+ /**
+ * Gets the number of lights in each horizontal zone.
+ */
+ @IntRange(from = 0)
+ public int getHorizontalZonesNumber() {
+ return mHorizontalZonesNumber;
+ }
+
+ /**
+ * Gets the number of lights in each vertical zone.
+ */
+ @IntRange(from = 0)
+ public int getVerticalZonesNumber() {
+ return mVerticalZonesNumber;
+ }
+
+ /**
+ * Returns {@code true} if letter box is omitted; {@code false} otherwise.
+ * @hide
+ */
+ public boolean isLetterboxOmitted() {
+ return mIsLetterboxOmitted;
+ }
+
+ /**
+ * @hide
+ */
+ public int getThreshold() {
+ return mThreshold;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSource);
+ dest.writeInt(mMaxFps);
+ dest.writeInt(mColorFormat);
+ dest.writeInt(mHorizontalZonesNumber);
+ dest.writeInt(mVerticalZonesNumber);
+ dest.writeBoolean(mIsLetterboxOmitted);
+ dest.writeInt(mThreshold);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<AmbientBacklightSettings> CREATOR =
+ new Parcelable.Creator<AmbientBacklightSettings>() {
+ public AmbientBacklightSettings createFromParcel(Parcel in) {
+ return new AmbientBacklightSettings(in);
+ }
+
+ public AmbientBacklightSettings[] newArray(int size) {
+ return new AmbientBacklightSettings[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "AmbientBacklightSettings{Source=" + mSource + ", MaxFps=" + mMaxFps
+ + ", ColorFormat=" + mColorFormat + ", HorizontalZonesNumber="
+ + mHorizontalZonesNumber + ", VerticalZonesNumber=" + mVerticalZonesNumber
+ + ", IsLetterboxOmitted=" + mIsLetterboxOmitted + ", Threshold=" + mThreshold + "}";
+ }
+}
diff --git a/media/java/android/media/quality/IAmbientBacklightCallback.aidl b/media/java/android/media/quality/IAmbientBacklightCallback.aidl
new file mode 100644
index 000000000000..159f5b7b5e71
--- /dev/null
+++ b/media/java/android/media/quality/IAmbientBacklightCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.media.quality.AmbientBacklightEvent;
+
+/** @hide */
+oneway interface IAmbientBacklightCallback {
+ void onAmbientBacklightEvent(in AmbientBacklightEvent event);
+}
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index e413a50389ca..250d59b7c2d7 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -16,7 +16,13 @@
package android.media.quality;
+import android.media.quality.AmbientBacklightSettings;
+import android.media.quality.IAmbientBacklightCallback;
+import android.media.quality.IPictureProfileCallback;
+import android.media.quality.ISoundProfileCallback;
+import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfile;
/**
* Interface for Media Quality Manager
@@ -24,8 +30,37 @@ import android.media.quality.PictureProfile;
*/
interface IMediaQualityManager {
PictureProfile createPictureProfile(in PictureProfile pp);
- PictureProfile getPictureProfileById(in long id);
+ void updatePictureProfile(in String id, in PictureProfile pp);
+ void removePictureProfile(in String id);
+ PictureProfile getPictureProfile(in int type, in String name);
List<PictureProfile> getPictureProfilesByPackage(in String packageName);
List<PictureProfile> getAvailablePictureProfiles();
- List<PictureProfile> getAvailableAllPictureProfiles();
-} \ No newline at end of file
+ List<String> getPictureProfilePackageNames();
+ List<String> getPictureProfileAllowList();
+ void setPictureProfileAllowList(in List<String> packages);
+
+ SoundProfile createSoundProfile(in SoundProfile pp);
+ void updateSoundProfile(in String id, in SoundProfile pp);
+ void removeSoundProfile(in String id);
+ SoundProfile getSoundProfileById(in String id);
+ List<SoundProfile> getSoundProfilesByPackage(in String packageName);
+ List<SoundProfile> getAvailableSoundProfiles();
+ List<String> getSoundProfilePackageNames();
+
+ void registerPictureProfileCallback(in IPictureProfileCallback cb);
+ void registerSoundProfileCallback(in ISoundProfileCallback cb);
+ void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
+
+ List<ParamCapability> getParamCapabilities(in List<String> names);
+
+ boolean isSupported();
+ void setAutoPictureQualityEnabled(in boolean enabled);
+ boolean isAutoPictureQualityEnabled();
+ void setSuperResolutionEnabled(in boolean enabled);
+ boolean isSuperResolutionEnabled();
+ void setAutoSoundQualityEnabled(in boolean enabled);
+ boolean isAutoSoundQualityEnabled();
+
+ void setAmbientBacklightSettings(in AmbientBacklightSettings settings);
+ void setAmbientBacklightEnabled(in boolean enabled);
+}
diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/IPictureProfileCallback.aidl
index 5deb029a47fd..34aa2b061caf 100644
--- a/media/java/android/media/quality/IPictureProfileCallback.aidl
+++ b/media/java/android/media/quality/IPictureProfileCallback.aidl
@@ -17,6 +17,7 @@
package android.media.quality;
+import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
/**
@@ -24,6 +25,9 @@ import android.media.quality.PictureProfile;
* @hide
*/
oneway interface IPictureProfileCallback {
- void onPictureProfileAdded(in long id, in PictureProfile p);
- void onPictureProfileUpdated(in long id, in PictureProfile p);
+ void onPictureProfileAdded(in String id, in PictureProfile p);
+ void onPictureProfileUpdated(in String id, in PictureProfile p);
+ void onPictureProfileRemoved(in String id, in PictureProfile p);
+ void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
+ void onError(in int err);
}
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
new file mode 100644
index 000000000000..72d1524198fd
--- /dev/null
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.quality;
+
+import android.media.quality.SoundProfile;
+
+/**
+ * Interface to receive callbacks from IMediaQuality.
+ * @hide
+ */
+oneway interface ISoundProfileCallback {
+ void onSoundProfileAdded(in long id, in SoundProfile p);
+ void onSoundProfileUpdated(in long id, in SoundProfile p);
+ void onSoundProfileRemoved(in long id, in SoundProfile p);
+}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 5cbc81d5f92d..f07ef873a0af 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -21,25 +21,68 @@ import android.annotation.FlaggedApi;
import android.media.tv.flags.Flags;
/**
+ * The contract between the media quality service and applications. Contains definitions for the
+ * commonly used parameter names.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public class MediaQualityContract {
+ /**
+ * @hide
+ */
public interface BaseParameters {
String PARAMETER_ID = "_id";
+ String PARAMETER_TYPE = "_type";
String PARAMETER_NAME = "_name";
String PARAMETER_PACKAGE = "_package";
String PARAMETER_INPUT_ID = "_input_id";
}
+
+ /**
+ * Parameters picture quality.
+ * @hide
+ */
public static final class PictureQuality implements BaseParameters {
+ /**
+ * The brightness.
+ *
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_BRIGHTNESS = "brightness";
+
+ /**
+ * The contrast.
+ *
+ * <p>The ratio between the luminance of the brightest white and the darkest black.
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_CONTRAST = "contrast";
+
+ /**
+ * The sharpness.
+ *
+ * <p>Sharpness indicates the clarity of detail.
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_SHARPNESS = "sharpness";
+
+ /**
+ * The saturation.
+ *
+ * <p>Saturation indicates the intensity of the color.
+ * <p>Type: INTEGER
+ */
public static final String PARAMETER_SATURATION = "saturation";
+
+ private PictureQuality() {
+ }
}
+ /**
+ * @hide
+ */
public static final class SoundQuality implements BaseParameters {
public static final String PARAMETER_BALANCE = "balance";
public static final String PARAMETER_BASS = "bass";
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 03dc24d0ad87..4d4526cf9925 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -19,6 +19,7 @@ package android.media.quality;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.content.Context;
import android.media.tv.flags.Flags;
@@ -34,12 +35,13 @@ import java.util.List;
import java.util.concurrent.Executor;
/**
- * Expose TV setting APIs for the application to use
+ * Central system API to the overall media quality, which arbitrates interaction between
+ * applications and media quality service.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@SystemService(Context.MEDIA_QUALITY_SERVICE)
-public class MediaQualityManager {
+public final class MediaQualityManager {
// TODO: unhide the APIs for api review
private static final String TAG = "MediaQualityManager";
@@ -48,6 +50,11 @@ public class MediaQualityManager {
private final Object mLock = new Object();
// @GuardedBy("mLock")
private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
+ // @GuardedBy("mLock")
+ private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
+ // @GuardedBy("mLock")
+ private final List<AmbientBacklightCallbackRecord> mAbCallbackRecords = new ArrayList<>();
+
/**
* @hide
@@ -55,9 +62,9 @@ public class MediaQualityManager {
public MediaQualityManager(Context context, IMediaQualityManager service) {
mContext = context;
mService = service;
- IPictureProfileCallback mqCallback = new IPictureProfileCallback.Stub() {
+ IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
@Override
- public void onPictureProfileAdded(long profileId, PictureProfile profile) {
+ public void onPictureProfileAdded(String profileId, PictureProfile profile) {
synchronized (mLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
@@ -66,7 +73,7 @@ public class MediaQualityManager {
}
}
@Override
- public void onPictureProfileUpdated(long profileId, PictureProfile profile) {
+ public void onPictureProfileUpdated(String profileId, PictureProfile profile) {
synchronized (mLock) {
for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
// TODO: filter callback record
@@ -74,14 +81,90 @@ public class MediaQualityManager {
}
}
}
+ @Override
+ public void onPictureProfileRemoved(String profileId, PictureProfile profile) {
+ synchronized (mLock) {
+ for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
+ // TODO: filter callback record
+ record.postPictureProfileRemoved(profileId, profile);
+ }
+ }
+ }
+ @Override
+ public void onParamCapabilitiesChanged(String profileId, List<ParamCapability> caps) {
+ synchronized (mLock) {
+ for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
+ // TODO: filter callback record
+ record.postParamCapabilitiesChanged(profileId, caps);
+ }
+ }
+ }
+ @Override
+ public void onError(int err) {
+ synchronized (mLock) {
+ for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
+ // TODO: filter callback record
+ record.postError(err);
+ }
+ }
+ }
};
+ ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
+ @Override
+ public void onSoundProfileAdded(long profileId, SoundProfile profile) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postSoundProfileAdded(profileId, profile);
+ }
+ }
+ }
+ @Override
+ public void onSoundProfileUpdated(long profileId, SoundProfile profile) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postSoundProfileUpdated(profileId, profile);
+ }
+ }
+ }
+ @Override
+ public void onSoundProfileRemoved(long profileId, SoundProfile profile) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postSoundProfileRemoved(profileId, profile);
+ }
+ }
+ }
+ };
+ IAmbientBacklightCallback abCallback = new IAmbientBacklightCallback.Stub() {
+ @Override
+ public void onAmbientBacklightEvent(AmbientBacklightEvent event) {
+ synchronized (mLock) {
+ for (AmbientBacklightCallbackRecord record : mAbCallbackRecords) {
+ record.postAmbientBacklightEvent(event);
+ }
+ }
+ }
+ };
+
+ try {
+ if (mService != null) {
+ mService.registerPictureProfileCallback(ppCallback);
+ mService.registerSoundProfileCallback(spCallback);
+ mService.registerAmbientBacklightCallback(abCallback);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* Registers a {@link PictureProfileCallback}.
* @hide
*/
- public void registerCallback(
+ public void registerPictureProfileCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
@@ -95,7 +178,7 @@ public class MediaQualityManager {
* Unregisters the existing {@link PictureProfileCallback}.
* @hide
*/
- public void unregisterCallback(@NonNull final PictureProfileCallback callback) {
+ public void unregisterPictureProfileCallback(@NonNull final PictureProfileCallback callback) {
Preconditions.checkNotNull(callback);
synchronized (mLock) {
for (Iterator<PictureProfileCallbackRecord> it = mPpCallbackRecords.iterator();
@@ -111,22 +194,31 @@ public class MediaQualityManager {
/**
- * Gets picture profile by given profile ID.
- * @return the corresponding picture profile if available; {@code null} if the ID doesn't
- * exist or the profile is not accessible to the caller.
+ * Gets picture profile by given profile type and name.
+ *
+ * @return the corresponding picture profile if available; {@code null} if the name doesn't
+ * exist.
+ * @hide
*/
- public PictureProfile getPictureProfileById(long profileId) {
+ @Nullable
+ public PictureProfile getPictureProfile(
+ @PictureProfile.ProfileType int type, @NonNull String name) {
try {
- return mService.getPictureProfileById(profileId);
+ return mService.getPictureProfile(type, name);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /** @SystemApi gets profiles that available to the given package */
+ /**
+ * Gets profiles that available to the given package.
+ *
+ * @hide @SystemApi
+ */
+ @NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
- public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
+ public List<PictureProfile> getPictureProfilesByPackage(@NonNull String packageName) {
try {
return mService.getPictureProfilesByPackage(packageName);
} catch (RemoteException e) {
@@ -134,7 +226,10 @@ public class MediaQualityManager {
}
}
- /** Gets profiles that available to the caller package */
+ /**
+ * Gets profiles that available to the caller.
+ */
+ @NonNull
public List<PictureProfile> getAvailablePictureProfiles() {
try {
return mService.getAvailablePictureProfiles();
@@ -143,11 +238,17 @@ public class MediaQualityManager {
}
}
- /** @SystemApi all stored picture profiles */
+ /**
+ * Gets all package names whose picture profiles are available.
+ *
+ * @see #getPictureProfilesByPackage(String)
+ * @hide @SystemApi
+ */
+ @NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
- public List<PictureProfile> getAvailableAllPictureProfiles() {
+ public List<String> getPictureProfilePackageNames() {
try {
- return mService.getAvailableAllPictureProfiles();
+ return mService.getPictureProfilePackageNames();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -157,9 +258,12 @@ public class MediaQualityManager {
/**
* Creates a picture profile and store it in the system.
*
- * @return the stored profile with an assigned profile ID.
+ * @return the stored profile with an assigned profile ID. {@code null} if it's not created
+ * successfully.
+ * @hide
*/
- public PictureProfile createPictureProfile(PictureProfile pp) {
+ @Nullable
+ public PictureProfile createPictureProfile(@NonNull PictureProfile pp) {
try {
return mService.createPictureProfile(pp);
} catch (RemoteException e) {
@@ -167,6 +271,360 @@ public class MediaQualityManager {
}
}
+
+ /**
+ * Updates an existing picture profile and store it in the system.
+ * @hide
+ */
+ public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) {
+ try {
+ mService.updatePictureProfile(profileId, pp);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Removes a picture profile from the system.
+ * @hide
+ */
+ public void removePictureProfile(@NonNull String profileId) {
+ try {
+ mService.removePictureProfile(profileId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a {@link SoundProfileCallback}.
+ * @hide
+ */
+ public void registerSoundProfileCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull SoundProfileCallback callback) {
+ Preconditions.checkNotNull(callback);
+ Preconditions.checkNotNull(executor);
+ synchronized (mLock) {
+ mSpCallbackRecords.add(new SoundProfileCallbackRecord(callback, executor));
+ }
+ }
+
+ /**
+ * Unregisters the existing {@link SoundProfileCallback}.
+ * @hide
+ */
+ public void unregisterSoundProfileCallback(@NonNull final SoundProfileCallback callback) {
+ Preconditions.checkNotNull(callback);
+ synchronized (mLock) {
+ for (Iterator<SoundProfileCallbackRecord> it = mSpCallbackRecords.iterator();
+ it.hasNext(); ) {
+ SoundProfileCallbackRecord record = it.next();
+ if (record.getCallback() == callback) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Gets sound profile by given profile ID.
+ * @return the corresponding sound profile if available; {@code null} if the ID doesn't
+ * exist or the profile is not accessible to the caller.
+ * @hide
+ */
+ public SoundProfile getSoundProfileById(String profileId) {
+ try {
+ return mService.getSoundProfileById(profileId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * @SystemApi gets profiles that available to the given package
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+ try {
+ return mService.getSoundProfilesByPackage(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets profiles that available to the caller package
+ * @hide
+ */
+ public List<SoundProfile> getAvailableSoundProfiles() {
+ try {
+ return mService.getAvailableSoundProfiles();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @SystemApi all stored sound profiles of all packages
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ public List<String> getSoundProfilePackageNames() {
+ try {
+ return mService.getSoundProfilePackageNames();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Creates a sound profile and store it in the system.
+ *
+ * @return the stored profile with an assigned profile ID.
+ * @hide
+ */
+ public SoundProfile createSoundProfile(SoundProfile sp) {
+ try {
+ return mService.createSoundProfile(sp);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Updates an existing sound profile and store it in the system.
+ * @hide
+ */
+ public void updateSoundProfile(String profileId, SoundProfile sp) {
+ try {
+ mService.updateSoundProfile(profileId, sp);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Removes a sound profile from the system.
+ * @hide
+ */
+ public void removeSoundProfile(String profileId) {
+ try {
+ mService.removeSoundProfile(profileId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets capability information of the given parameters.
+ * @hide
+ */
+ @NonNull
+ public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) {
+ try {
+ return mService.getParamCapabilities(names);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the allowlist of packages that can create and removed picture profiles
+ *
+ * @see #createPictureProfile(PictureProfile)
+ * @see #removePictureProfile(String)
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ @NonNull
+ public List<String> getPictureProfileAllowList() {
+ try {
+ return mService.getPictureProfileAllowList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the allowlist of packages that can create and removed picture profiles
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ public void setPictureProfileAllowList(@NonNull List<String> packageNames) {
+ try {
+ mService.setPictureProfileAllowList(packageNames);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns {@code true} if media quality HAL is implemented; {@code false} otherwise.
+ * @hide
+ */
+ public boolean isSupported() {
+ try {
+ return mService.isSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables auto picture quality.
+ * <p>If enabled, picture quality parameters can be adjusted dynamically by hardware based on
+ * different use cases.
+ *
+ * @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ public void setAutoPictureQualityEnabled(boolean enabled) {
+ try {
+ mService.setAutoPictureQualityEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns {@code true} if auto picture quality is enabled; {@code false} otherwise.
+ * @hide
+ */
+ public boolean isAutoPictureQualityEnabled() {
+ try {
+ return mService.isAutoPictureQualityEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables super resolution.
+ * <p>Super resolution is a feature to improve resolution.
+ *
+ * @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ public void setSuperResolutionEnabled(boolean enabled) {
+ try {
+ mService.setSuperResolutionEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns {@code true} if super resolution is enabled; {@code false} otherwise.
+ * @hide
+ */
+ public boolean isSuperResolutionEnabled() {
+ try {
+ return mService.isSuperResolutionEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables auto sound quality.
+ * <p>If enabled, sound quality parameters can be adjusted dynamically by hardware based on
+ * different use cases.
+ *
+ * @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ public void setAutoSoundQualityEnabled(boolean enabled) {
+ try {
+ mService.setAutoSoundQualityEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns {@code true} if auto sound quality is enabled; {@code false} otherwise.
+ * @hide
+ */
+ public boolean isAutoSoundQualityEnabled() {
+ try {
+ return mService.isAutoSoundQualityEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a {@link AmbientBacklightCallback}.
+ */
+ public void registerAmbientBacklightCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AmbientBacklightCallback callback) {
+ Preconditions.checkNotNull(callback);
+ Preconditions.checkNotNull(executor);
+ synchronized (mLock) {
+ mAbCallbackRecords.add(new AmbientBacklightCallbackRecord(callback, executor));
+ }
+ }
+
+ /**
+ * Unregisters the existing {@link AmbientBacklightCallback}.
+ */
+ public void unregisterAmbientBacklightCallback(
+ @NonNull final AmbientBacklightCallback callback) {
+ Preconditions.checkNotNull(callback);
+ synchronized (mLock) {
+ for (Iterator<AmbientBacklightCallbackRecord> it = mAbCallbackRecords.iterator();
+ it.hasNext(); ) {
+ AmbientBacklightCallbackRecord record = it.next();
+ if (record.getCallback() == callback) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the ambient backlight settings.
+ *
+ * @param settings The settings to use for the backlight detector.
+ */
+ public void setAmbientBacklightSettings(
+ @NonNull AmbientBacklightSettings settings) {
+ Preconditions.checkNotNull(settings);
+ try {
+ mService.setAmbientBacklightSettings(settings);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables the ambient backlight detection.
+ *
+ * @param enabled {@code true} to enable, {@code false} to disable.
+ */
+ public void setAmbientBacklightEnabled(boolean enabled) {
+ try {
+ mService.setAmbientBacklightEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
private static final class PictureProfileCallbackRecord {
private final PictureProfileCallback mCallback;
private final Executor mExecutor;
@@ -180,7 +638,7 @@ public class MediaQualityManager {
return mCallback;
}
- public void postPictureProfileAdded(final long id, PictureProfile profile) {
+ public void postPictureProfileAdded(final String id, PictureProfile profile) {
mExecutor.execute(new Runnable() {
@Override
@@ -190,7 +648,7 @@ public class MediaQualityManager {
});
}
- public void postPictureProfileUpdated(final long id, PictureProfile profile) {
+ public void postPictureProfileUpdated(final String id, PictureProfile profile) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
@@ -198,6 +656,98 @@ public class MediaQualityManager {
}
});
}
+
+ public void postPictureProfileRemoved(final String id, PictureProfile profile) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onPictureProfileRemoved(id, profile);
+ }
+ });
+ }
+
+ public void postParamCapabilitiesChanged(final String id, List<ParamCapability> caps) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onParamCapabilitiesChanged(id, caps);
+ }
+ });
+ }
+
+ public void postError(int error) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onError(error);
+ }
+ });
+ }
+ }
+
+ private static final class SoundProfileCallbackRecord {
+ private final SoundProfileCallback mCallback;
+ private final Executor mExecutor;
+
+ SoundProfileCallbackRecord(SoundProfileCallback callback, Executor executor) {
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ public SoundProfileCallback getCallback() {
+ return mCallback;
+ }
+
+ public void postSoundProfileAdded(final long id, SoundProfile profile) {
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onSoundProfileAdded(id, profile);
+ }
+ });
+ }
+
+ public void postSoundProfileUpdated(final long id, SoundProfile profile) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onSoundProfileUpdated(id, profile);
+ }
+ });
+ }
+
+ public void postSoundProfileRemoved(final long id, SoundProfile profile) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onSoundProfileRemoved(id, profile);
+ }
+ });
+ }
+ }
+
+ private static final class AmbientBacklightCallbackRecord {
+ private final AmbientBacklightCallback mCallback;
+ private final Executor mExecutor;
+
+ AmbientBacklightCallbackRecord(AmbientBacklightCallback callback, Executor executor) {
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ public AmbientBacklightCallback getCallback() {
+ return mCallback;
+ }
+
+ public void postAmbientBacklightEvent(AmbientBacklightEvent event) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onAmbientBacklightEvent(event);
+ }
+ });
+ }
}
/**
@@ -206,14 +756,95 @@ public class MediaQualityManager {
*/
public abstract static class PictureProfileCallback {
/**
+ * This is invoked when a picture profile has been added.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the newly added profile.
+ * @hide
+ */
+ public void onPictureProfileAdded(
+ @NonNull String profileId, @NonNull PictureProfile profile) {
+ }
+
+ /**
+ * This is invoked when a picture profile has been updated.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the profile with updated info.
+ * @hide
+ */
+ public void onPictureProfileUpdated(
+ @NonNull String profileId, @NonNull PictureProfile profile) {
+ }
+
+ /**
+ * This is invoked when a picture profile has been removed.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the removed profile.
+ * @hide
+ */
+ public void onPictureProfileRemoved(
+ @NonNull String profileId, @NonNull PictureProfile profile) {
+ }
+
+ /**
+ * This is invoked when an issue has occurred.
+ *
+ * @param errorCode the error code
+ * @hide
+ */
+ public void onError(@PictureProfile.ErrorCode int errorCode) {
+ }
+
+ /**
+ * This is invoked when parameter capabilities has been changed due to status changes of the
+ * content.
+ *
+ * @param profileId the ID of the profile used by the media content.
+ * @param updatedCaps the updated capabilities.
+ * @hide
+ */
+ public void onParamCapabilitiesChanged(
+ @NonNull String profileId, @NonNull List<ParamCapability> updatedCaps) {
+ }
+ }
+
+ /**
+ * Callback used to monitor status of sound profiles.
+ * @hide
+ */
+ public abstract static class SoundProfileCallback {
+ /**
* @hide
*/
- public void onPictureProfileAdded(long id, PictureProfile profile) {
+ public void onSoundProfileAdded(long id, SoundProfile profile) {
}
/**
* @hide
*/
- public void onPictureProfileUpdated(long id, PictureProfile profile) {
+ public void onSoundProfileUpdated(long id, SoundProfile profile) {
+ }
+ /**
+ * @hide
+ */
+ public void onSoundProfileRemoved(long id, SoundProfile profile) {
+ }
+ /**
+ * @hide
+ */
+ public void onError(int errorCode) {
+ }
+ }
+
+ /**
+ * Callback used to monitor status of ambient backlight.
+ */
+ public abstract static class AmbientBacklightCallback {
+ /**
+ * Called when new ambient backlight event is emitted.
+ */
+ public void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event) {
}
}
}
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/media/java/android/media/quality/ParamCapability.aidl
new file mode 100644
index 000000000000..b43409d039f2
--- /dev/null
+++ b/media/java/android/media/quality/ParamCapability.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+parcelable ParamCapability;
diff --git a/media/java/android/media/quality/ParamCapability.java b/media/java/android/media/quality/ParamCapability.java
new file mode 100644
index 000000000000..0b698a9c1ad2
--- /dev/null
+++ b/media/java/android/media/quality/ParamCapability.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.StringDef;
+import android.media.tv.flags.Flags;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Capability info of media quality parameters
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class ParamCapability implements Parcelable {
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_INT,
+ TYPE_LONG,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ParamType {}
+
+ /**
+ * Integer parameter type
+ */
+ public static final int TYPE_INT = 1;
+
+ /**
+ * Long integer parameter type
+ */
+ public static final int TYPE_LONG = 2;
+
+ /**
+ * Double parameter type
+ */
+ public static final int TYPE_DOUBLE = 3;
+
+ /**
+ * String parameter type
+ */
+ public static final int TYPE_STRING = 4;
+
+ /** @hide */
+ @StringDef(prefix = { "CAPABILITY_" }, value = {
+ CAPABILITY_MAX,
+ CAPABILITY_MIN,
+ CAPABILITY_DEFAULT,
+ CAPABILITY_ENUM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Capability {}
+
+ /**
+ * The key for the max possible value of this parameter.
+ */
+ public static final String CAPABILITY_MAX = "max";
+
+ /**
+ * The key for the min possible value of this parameter.
+ */
+ public static final String CAPABILITY_MIN = "min";
+
+ /**
+ * The key for the default value of this parameter.
+ */
+ public static final String CAPABILITY_DEFAULT = "default";
+
+ /**
+ * The key for the enumeration of this parameter.
+ */
+ public static final String CAPABILITY_ENUM = "enum";
+
+ @NonNull
+ private final String mName;
+ private final boolean mIsSupported;
+ @ParamType
+ private final int mType;
+ @NonNull
+ private final Bundle mCaps;
+
+ /** @hide */
+ protected ParamCapability(Parcel in) {
+ mName = in.readString();
+ mIsSupported = in.readBoolean();
+ mType = in.readInt();
+ mCaps = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeBoolean(mIsSupported);
+ dest.writeInt(mType);
+ dest.writeBundle(mCaps);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<ParamCapability> CREATOR = new Creator<ParamCapability>() {
+ @Override
+ public ParamCapability createFromParcel(Parcel in) {
+ return new ParamCapability(in);
+ }
+
+ @Override
+ public ParamCapability[] newArray(int size) {
+ return new ParamCapability[size];
+ }
+ };
+
+
+ /**
+ * Creates a new ParamCapability.
+ *
+ * @hide
+ */
+ public ParamCapability(
+ @NonNull String name,
+ boolean isSupported,
+ int type,
+ @NonNull Bundle caps) {
+ this.mName = name;
+ this.mIsSupported = isSupported;
+ this.mType = type;
+ this.mCaps = caps;
+ }
+
+ /**
+ * Gets parameter name.
+ */
+ @NonNull
+ public String getParamName() {
+ return mName;
+ }
+
+ /**
+ * Returns whether this parameter is supported or not.
+ */
+ public boolean isSupported() {
+ return mIsSupported;
+ }
+
+ /**
+ * Gets parameter type.
+ */
+ @ParamType
+ public int getParamType() {
+ return mType;
+ }
+
+ /**
+ * Gets capability information.
+ * <p>e.g. use the key {@link #CAPABILITY_MAX} to get the max value.
+ */
+ @NonNull
+ public Bundle getCapabilities() {
+ return new Bundle(mCaps);
+ }
+}
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index 36c49d80e13d..2be47dd87ef2 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -17,6 +17,8 @@
package android.media.quality;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.media.tv.TvInputInfo;
import android.media.tv.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -24,30 +26,102 @@ import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
+ * Profile for picture quality.
* @hide
*/
-
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
-public class PictureProfile implements Parcelable {
+public final class PictureProfile implements Parcelable {
@Nullable
- private Long mId;
+ private String mId;
+ private final int mType;
@NonNull
private final String mName;
@Nullable
private final String mInputId;
- @Nullable
+ @NonNull
private final String mPackageName;
@NonNull
private final Bundle mParams;
- protected PictureProfile(Parcel in) {
- if (in.readByte() == 0) {
- mId = null;
- } else {
- mId = in.readLong();
- }
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "TYPE_", value = {
+ TYPE_SYSTEM,
+ TYPE_APPLICATION})
+ public @interface ProfileType {}
+
+ /**
+ * System profile type.
+ *
+ * <p>A profile of system type is managed by the system, and readable to the package define in
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_SYSTEM = 1;
+ /**
+ * Application profile type.
+ *
+ * <p>A profile of application type is managed by the package define in
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_APPLICATION = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "ERROR_", value = {
+ ERROR_UNKNOWN,
+ ERROR_NO_PERMISSION,
+ ERROR_DUPLICATE,
+ ERROR_INVALID_ARGUMENT,
+ ERROR_NOT_ALLOWLISTED
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * Error code for unknown errors.
+ * @hide
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Error code for missing necessary permission to handle the profiles.
+ * @hide
+ */
+ public static final int ERROR_NO_PERMISSION = 1;
+
+ /**
+ * Error code for creating a profile with existing profile type and name.
+ *
+ * @see #getProfileType()
+ * @see #getName()
+ * @hide
+ */
+ public static final int ERROR_DUPLICATE = 2;
+
+ /**
+ * Error code for invalid argument.
+ * @hide
+ */
+ public static final int ERROR_INVALID_ARGUMENT = 3;
+
+ /**
+ * Error code for the case when an operation requires an allowlist but the caller is not in the
+ * list.
+ *
+ * @see MediaQualityManager#getPictureProfileAllowList()
+ * @hide
+ */
+ public static final int ERROR_NOT_ALLOWLISTED = 4;
+
+
+ private PictureProfile(@NonNull Parcel in) {
+ mId = in.readString();
+ mType = in.readInt();
mName = in.readString();
mInputId = in.readString();
mPackageName = in.readString();
@@ -55,13 +129,9 @@ public class PictureProfile implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
- if (mId == null) {
- dest.writeByte((byte) 0);
- } else {
- dest.writeByte((byte) 1);
- dest.writeLong(mId);
- }
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mType);
dest.writeString(mName);
dest.writeString(mInputId);
dest.writeString(mPackageName);
@@ -73,6 +143,7 @@ public class PictureProfile implements Parcelable {
return 0;
}
+ @NonNull
public static final Creator<PictureProfile> CREATOR = new Creator<PictureProfile>() {
@Override
public PictureProfile createFromParcel(Parcel in) {
@@ -92,95 +163,166 @@ public class PictureProfile implements Parcelable {
* @hide
*/
public PictureProfile(
- @Nullable Long id,
+ @Nullable String id,
+ int type,
@NonNull String name,
@Nullable String inputId,
- @Nullable String packageName,
+ @NonNull String packageName,
@NonNull Bundle params) {
this.mId = id;
+ this.mType = type;
this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
}
+ /**
+ * Gets profile ID.
+ *
+ * <p>A profile ID is a globally unique ID generated and assigned by the system. For profile
+ * objects retrieved from system (e.g {@link MediaQualityManager#getAvailablePictureProfiles()})
+ * this profile ID is non-null; For profiles built locally with {@link Builder}, it's
+ * {@code null}.
+ *
+ * @return the unique profile ID; {@code null} if the profile is built locally with
+ * {@link Builder}.
+ */
@Nullable
- public Long getProfileId() {
+ public String getProfileId() {
return mId;
}
+ /**
+ * Only used by system to assign the ID.
+ * @hide
+ */
+ public void setProfileId(String id) {
+ mId = id;
+ }
+
+ /**
+ * Gets profile type.
+ */
+ @ProfileType
+ public int getProfileType() {
+ return mType;
+ }
+
+ /**
+ * Gets the profile name.
+ */
@NonNull
public String getName() {
return mName;
}
+ /**
+ * Gets the input ID if the profile is for a TV input.
+ *
+ * @return the corresponding TV input ID; {@code null} if the profile is not associated with a
+ * TV input.
+ *
+ * @see TvInputInfo#getId()
+ */
@Nullable
public String getInputId() {
return mInputId;
}
+ /**
+ * Gets the package name of this profile.
+ *
+ * <p>The package name defines the user of a profile. Only this specific package and system app
+ * can access to this profile.
+ *
+ * @return the package name; {@code null} if the profile is built locally using
+ * {@link Builder} and the package is not set.
+ */
@Nullable
public String getPackageName() {
return mPackageName;
}
+
+ /**
+ * Gets the parameters of this profile.
+ *
+ * <p>The keys of commonly used parameters can be found in
+ * {@link MediaQualityContract.PictureQuality}.
+ */
@NonNull
public Bundle getParameters() {
return new Bundle(mParams);
}
/**
- * A builder for {@link PictureProfile}
+ * A builder for {@link PictureProfile}.
+ * @hide
*/
- public static class Builder {
+ public static final class Builder {
@Nullable
- private Long mId;
+ private String mId;
+ private int mType = TYPE_APPLICATION;
@NonNull
private String mName;
@Nullable
private String mInputId;
- @Nullable
+ @NonNull
private String mPackageName;
@NonNull
private Bundle mParams;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder(@NonNull String name) {
mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
}
/**
- * Copy constructor.
- *
- * @hide
+ * Copy constructor of builder.
*/
public Builder(@NonNull PictureProfile p) {
mId = null; // ID needs to be reset
+ mType = p.getProfileType();
mName = p.getName();
mPackageName = p.getPackageName();
mInputId = p.getInputId();
+ mParams = p.getParameters();
}
/* @hide using by MediaQualityService */
/**
- * Sets profile ID.
- * @hide using by MediaQualityService
+ * Only used by system to assign the ID.
+ * @hide
*/
@NonNull
- public Builder setProfileId(@Nullable Long id) {
+ public Builder setProfileId(@Nullable String id) {
mId = id;
return this;
}
/**
+ * Sets profile type.
+ *
+ * @hide @SystemApi
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+ @NonNull
+ public Builder setProfileType(@ProfileType int value) {
+ mType = value;
+ return this;
+ }
+
+ /**
* Sets input ID.
+ *
+ * @see PictureProfile#getInputId()
+ *
+ * @hide @SystemApi
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setInputId(@NonNull String value) {
mInputId = value;
@@ -189,7 +331,12 @@ public class PictureProfile implements Parcelable {
/**
* Sets package name of the profile.
+ *
+ * @see PictureProfile#getPackageName()
+ *
+ * @hide @SystemApi
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
@NonNull
public Builder setPackageName(@NonNull String value) {
mPackageName = value;
@@ -198,6 +345,8 @@ public class PictureProfile implements Parcelable {
/**
* Sets profile parameters.
+ *
+ * @see PictureProfile#getParameters()
*/
@NonNull
public Builder setParameters(@NonNull Bundle params) {
@@ -213,6 +362,7 @@ public class PictureProfile implements Parcelable {
PictureProfile o = new PictureProfile(
mId,
+ mType,
mName,
mInputId,
mPackageName,
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/PictureProfileHandle.aidl
new file mode 100644
index 000000000000..5d14631dbb73
--- /dev/null
+++ b/media/java/android/media/quality/PictureProfileHandle.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+parcelable PictureProfileHandle;
diff --git a/media/java/android/media/quality/PictureProfileHandle.java b/media/java/android/media/quality/PictureProfileHandle.java
new file mode 100644
index 000000000000..2b1cda4eb742
--- /dev/null
+++ b/media/java/android/media/quality/PictureProfileHandle.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+// TODO(b/337330263): Expose as public API after API review
+/**
+ * A type-safe handle to a picture profile, which represents a collection of parameters used to
+ * configure picture processing hardware to enhance the quality of graphic buffers.
+ * @hide
+ */
+@FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+public final class PictureProfileHandle implements Parcelable {
+ private final long mId;
+
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+ public PictureProfileHandle(long id) {
+ mId = id;
+ }
+
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+ public long getId() {
+ return mId;
+ }
+
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mId);
+ }
+
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW)
+ @NonNull
+ public static final Creator<PictureProfileHandle> CREATOR =
+ new Creator<PictureProfileHandle>() {
+ @Override
+ public PictureProfileHandle createFromParcel(Parcel in) {
+ return new PictureProfileHandle(in);
+ }
+
+ @Override
+ public PictureProfileHandle[] newArray(int size) {
+ return new PictureProfileHandle[size];
+ }
+ };
+
+ private PictureProfileHandle(@NonNull Parcel in) {
+ mId = in.readLong();
+ }
+}
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index e0fcf9d32e86..20d117bf15cf 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -18,6 +18,7 @@ package android.media.quality;
import android.annotation.FlaggedApi;
import android.media.tv.flags.Flags;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,6 +38,8 @@ public class SoundProfile implements Parcelable {
private final String mInputId;
@Nullable
private final String mPackageName;
+ @NonNull
+ private final Bundle mParams;
protected SoundProfile(Parcel in) {
if (in.readByte() == 0) {
@@ -47,6 +50,7 @@ public class SoundProfile implements Parcelable {
mName = in.readString();
mInputId = in.readString();
mPackageName = in.readString();
+ mParams = in.readBundle();
}
@Override
@@ -60,6 +64,7 @@ public class SoundProfile implements Parcelable {
dest.writeString(mName);
dest.writeString(mInputId);
dest.writeString(mPackageName);
+ dest.writeBundle(mParams);
}
@Override
@@ -89,12 +94,14 @@ public class SoundProfile implements Parcelable {
@Nullable Long id,
@NonNull String name,
@Nullable String inputId,
- @Nullable String packageName) {
+ @Nullable String packageName,
+ @NonNull Bundle params) {
this.mId = id;
this.mName = name;
com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
this.mInputId = inputId;
this.mPackageName = packageName;
+ this.mParams = params;
}
@Nullable
@@ -116,6 +123,10 @@ public class SoundProfile implements Parcelable {
public String getPackageName() {
return mPackageName;
}
+ @NonNull
+ public Bundle getParameters() {
+ return new Bundle(mParams);
+ }
/**
* A builder for {@link SoundProfile}
@@ -129,6 +140,8 @@ public class SoundProfile implements Parcelable {
private String mInputId;
@Nullable
private String mPackageName;
+ @NonNull
+ private Bundle mParams;
/**
* Creates a new Builder.
@@ -181,6 +194,14 @@ public class SoundProfile implements Parcelable {
}
/**
+ * Sets profile parameters.
+ */
+ @NonNull
+ public Builder setParameters(@NonNull Bundle params) {
+ mParams = new Bundle(params);
+ return this;
+ }
+ /**
* Builds the instance.
*/
@NonNull
@@ -190,7 +211,8 @@ public class SoundProfile implements Parcelable {
mId,
mName,
mInputId,
- mPackageName);
+ mPackageName,
+ mParams);
return o;
}
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 6658918518ea..abfc24413d56 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,8 @@
package android.media.tv;
+import static android.media.tv.flags.Flags.tifExtensionStandardization;
+
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -159,6 +161,11 @@ public abstract class TvInputService extends Service {
new RemoteCallbackList<>();
private TvInputManager mTvInputManager;
+ /**
+ * @hide
+ */
+ protected TvInputServiceExtensionManager mTvInputServiceExtensionManager =
+ new TvInputServiceExtensionManager();
@Override
public final IBinder onBind(Intent intent) {
@@ -211,12 +218,23 @@ public abstract class TvInputService extends Service {
}
@Override
- public List<String> getAvailableExtensionInterfaceNames() {
- return TvInputService.this.getAvailableExtensionInterfaceNames();
+ public List<String> getAvailableExtensionInterfaceNames() {
+ List<String> extensionNames =
+ TvInputService.this.getAvailableExtensionInterfaceNames();
+ if (tifExtensionStandardization()) {
+ extensionNames.addAll(
+ TvInputServiceExtensionManager.getStandardExtensionInterfaceNames());
+ }
+ return extensionNames;
}
@Override
public IBinder getExtensionInterface(String name) {
+ if (tifExtensionStandardization() && name != null) {
+ if (TvInputServiceExtensionManager.checkIsStandardizedInterfaces(name)) {
+ return mTvInputServiceExtensionManager.getExtensionIBinder(name);
+ }
+ }
return TvInputService.this.getExtensionInterface(name);
}
diff --git a/media/java/android/media/tv/TvInputServiceExtensionManager.java b/media/java/android/media/tv/TvInputServiceExtensionManager.java
new file mode 100644
index 000000000000..9442726508c6
--- /dev/null
+++ b/media/java/android/media/tv/TvInputServiceExtensionManager.java
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.media.tv.flags.Flags;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * This class provides a list of available standardized TvInputService extension interface names
+ * and a container storing IBinder objects that implement these interfaces created by SoC/OEMs.
+ * It also provides an API for SoC/OEMs to register implemented IBinder objects.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_TIF_EXTENSION_STANDARDIZATION)
+public final class TvInputServiceExtensionManager {
+ private static final String TAG = "TvInputServiceExtensionManager";
+ private static final String SCAN_PACKAGE = "android.media.tv.extension.scan.";
+ private static final String OAD_PACKAGE = "android.media.tv.extension.oad.";
+ private static final String CAM_PACKAGE = "android.media.tv.extension.cam.";
+ private static final String RATING_PACKAGE = "android.media.tv.extension.rating.";
+ private static final String TIME_PACKAGE = "android.media.tv.extension.time.";
+ private static final String TELETEXT_PACKAGE = "android.media.tv.extension.teletext.";
+ private static final String SCAN_BSU_PACKAGE = "android.media.tv.extension.scanbsu.";
+ private static final String CLIENT_TOKEN_PACKAGE = "android.media.tv.extension.clienttoken.";
+ private static final String SCREEN_MODE_PACKAGE = "android.media.tv.extension.screenmode.";
+ private static final String SIGNAL_PACKAGE = "android.media.tv.extension.signal.";
+ private static final String SERVICE_DATABASE_PACKAGE = "android.media.tv.extension.servicedb.";
+ private static final String PVR_PACKAGE = "android.media.tv.extension.pvr.";
+ private static final String EVENT_PACKAGE = "android.media.tv.extension.event.";
+ private static final String ANALOG_PACKAGE = "android.media.tv.extension.analog.";
+ private static final String TUNE_PACKAGE = "android.media.tv.extension.tune.";
+
+ /** @hide */
+ @IntDef(prefix = {"REGISTER_"}, value = {
+ REGISTER_SUCCESS,
+ REGISTER_FAIL_NAME_NOT_STANDARDIZED,
+ REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED,
+ REGISTER_FAIL_REMOTE_EXCEPTION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RegisterResult {}
+
+ /**
+ * Registering binder returns success when it abides standardized interface structure
+ */
+ public static final int REGISTER_SUCCESS = 0;
+ /**
+ * Registering binder returns failure when the extension name is not in the standardization
+ * list
+ */
+ public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1;
+ /**
+ * Registering binder returns failure when the IBinder does not implement standardized interface
+ */
+ public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2;
+ /**
+ * Registering binder returns failure when remote server is not available
+ */
+ public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3;
+
+ /** @hide */
+ @StringDef({
+ ISCAN_INTERFACE,
+ ISCAN_SESSION,
+ ISCAN_LISTENER,
+ IHDPLUS_INFO,
+ IOPERATOR_DETECTION,
+ IOPERATOR_DETECTION_LISTENER,
+ IREGION_CHANNEL_LIST,
+ IREGION_CHANNEL_LIST_LISTENER,
+ ITARGET_REGION,
+ ITARGET_REGION_LISTENER,
+ ILCN_CONFLICT,
+ ILCN_CONFLICT_LISTENER,
+ ILCNV2_CHANNEL_LIST,
+ ILCNV2_CHANNEL_LIST_LISTENER,
+ IFAVORITE_NETWORK,
+ IFAVORITE_NETWORK_LISTENER,
+ ITKGS_INFO,
+ ITKGS_INFO_LISTENER,
+ ISCAN_SAT_SEARCH,
+ IOAD_UPDATE_INTERFACE,
+ ICAM_APP_INFO_SERVICE,
+ ICAM_APP_INFO_LISTENER,
+ ICAM_MONITORING_SERVICE,
+ ICAM_INFO_LISTENER,
+ ICI_OPERATOR_INTERFACE,
+ ICI_OPERATOR_LISTENER,
+ ICAM_PROFILE_INTERFACE,
+ ICONTENT_CONTROL_SERVICE,
+ ICAM_DRM_INFO_LISTENER,
+ ICAM_PIN_SERVICE,
+ ICAM_PIN_CAPABILITY_LISTENER,
+ ICAM_PIN_STATUS_LISTENER,
+ ICAM_HOST_CONTROL_SERVICE,
+ ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK,
+ ICAM_HOST_CONTROL_INFO_LISTENER,
+ ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG,
+ ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER,
+ IMMI_INTERFACE,
+ IMMI_SESSION,
+ IMMI_STATUS_CALLBACK,
+ IENTER_MENU_ERROR_CALLBACK,
+ IDOWNLOADABLE_RATING_TABLE_MONITOR,
+ IRATING_INTERFACE,
+ IPMT_RATING_INTERFACE,
+ IPMT_RATING_LISTENER,
+ IVBI_RATING_INTERFACE,
+ IVBI_RATING_LISTENER,
+ IPROGRAM_INFO,
+ IPROGRAM_INFO_LISTENER,
+ IBROADCAST_TIME,
+ IDATA_SERVICE_SIGNAL_INFO,
+ IDATA_SERVICE_SIGNAL_INFO_LISTENER,
+ ITELETEXT_PAGE_SUB_CODE,
+ ISCAN_BACKGROUND_SERVICE_UPDATE,
+ ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER,
+ ICLIENT_TOKEN,
+ ISCREEN_MODE_SETTINGS,
+ IHDMI_SIGNAL_INTERFACE,
+ IHDMI_SIGNAL_INFO_LISTENER,
+ IAUDIO_SIGNAL_INFO,
+ IANALOG_AUDIO_INFO,
+ IAUDIO_SIGNAL_INFO_LISTENER,
+ IVIDEO_SIGNAL_INFO,
+ IVIDEO_SIGNAL_INFO_LISTENER,
+ ISERVICE_LIST_EDIT,
+ ISERVICE_LIST_EDIT_LISTENER,
+ ISERVICE_LIST,
+ ISERVICE_LIST_TRANSFER_INTERFACE,
+ ISERVICE_LIST_EXPORT_SESSION,
+ ISERVICE_LIST_EXPORT_LISTENER,
+ ISERVICE_LIST_IMPORT_SESSION,
+ ISERVICE_LIST_IMPORT_LISTENER,
+ ISERVICE_LIST_SET_CHANNEL_LIST_SESSION,
+ ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER,
+ ICHANNEL_LIST_TRANSFER,
+ IRECORDED_CONTENTS,
+ IDELETE_RECORDED_CONTENTS_CALLBACK,
+ IGET_INFO_RECORDED_CONTENTS_CALLBACK,
+ IEVENT_MONITOR,
+ IEVENT_MONITOR_LISTENER,
+ IEVENT_DOWNLOAD,
+ IEVENT_DOWNLOAD_LISTENER,
+ IEVENT_DOWNLOAD_SESSION,
+ IANALOG_ATTRIBUTE_INTERFACE,
+ ICHANNEL_TUNED_INTERFACE,
+ ICHANNEL_TUNED_LISTENER,
+ ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE,
+ ITUNER_FRONTEND_SIGNAL_INFO_LISTENER,
+ IMUX_TUNE_SESSION,
+ IMUX_TUNE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StandardizedExtensionName {}
+ /**
+ * Interface responsible for creating scan session and obtaining related parameters.
+ */
+ public static final String ISCAN_INTERFACE = SCAN_PACKAGE + "IScanInterface";
+ /**
+ * Interface that handles scan session and get/store related information.
+ * @hide
+ */
+ public static final String ISCAN_SESSION = SCAN_PACKAGE + "IScanSession";
+ /**
+ * Interface that notifies changes related to scan session.
+ * @hide
+ */
+ public static final String ISCAN_LISTENER = SCAN_PACKAGE + "IScanListener";
+ /**
+ * Interface for setting HDPlus information.
+ * @hide
+ */
+ public static final String IHDPLUS_INFO = SCAN_PACKAGE + "IHDPlusInfo";
+ /**
+ * Interface for handling operator detection for scanning.
+ * @hide
+ */
+ public static final String IOPERATOR_DETECTION = SCAN_PACKAGE + "IOperatorDetection";
+ /**
+ * Interface for changes related to operator detection searches.
+ * @hide
+ */
+ public static final String IOPERATOR_DETECTION_LISTENER = SCAN_PACKAGE
+ + "IOperatorDetectionListener";
+ /**
+ * Interface for handling region channel list for scanning.
+ * @hide
+ */
+ public static final String IREGION_CHANNEL_LIST = SCAN_PACKAGE + "IRegionChannelList";
+ /**
+ * Interface for changes related to changes in region channel list search.
+ * @hide
+ */
+ public static final String IREGION_CHANNEL_LIST_LISTENER = SCAN_PACKAGE
+ + "IRegionChannelListListener";
+ /**
+ * Interface for handling target region information.
+ * @hide
+ */
+ public static final String ITARGET_REGION = SCAN_PACKAGE + "ITargetRegion";
+ /**
+ * Interface for changes related to target regions during scanning.
+ * @hide
+ */
+ public static final String ITARGET_REGION_LISTENER = SCAN_PACKAGE + "ITargetRegionListener";
+ /**
+ * Interface for handling LCN conflict groups.
+ * @hide
+ */
+ public static final String ILCN_CONFLICT = SCAN_PACKAGE + "ILcnConflict";
+ /**
+ * Interface for detecting LCN conflicts during scanning.
+ * @hide
+ */
+ public static final String ILCN_CONFLICT_LISTENER = SCAN_PACKAGE + "ILcnConflictListener";
+ /**
+ * Interface for handling LCN V2 channel list information.
+ * @hide
+ */
+ public static final String ILCNV2_CHANNEL_LIST = SCAN_PACKAGE + "ILcnV2ChannelList";
+ /**
+ * Interface for detecting LCN V2 channel list during scanning.
+ * @hide
+ */
+ public static final String ILCNV2_CHANNEL_LIST_LISTENER = SCAN_PACKAGE
+ + "ILcnV2ChannelListListener";
+ /**
+ * Interface for handling favorite network related information.
+ * @hide
+ */
+ public static final String IFAVORITE_NETWORK = SCAN_PACKAGE + "IFavoriteNetwork";
+ /**
+ * Interface for detecting favorite network during scanning.
+ * @hide
+ */
+ public static final String IFAVORITE_NETWORK_LISTENER = SCAN_PACKAGE
+ + "IFavoriteNetworkListener";
+ /**
+ * Interface for handling Turksat channel update system service.
+ * @hide
+ */
+ public static final String ITKGS_INFO = SCAN_PACKAGE + "ITkgsInfo";
+ /**
+ * Interface for changes related to TKGS information.
+ * @hide
+ */
+ public static final String ITKGS_INFO_LISTENER = SCAN_PACKAGE + "ITkgsInfoListener";
+ /**
+ * Interface for satellite search related to low noise block downconverter.
+ * @hide
+ */
+ public static final String ISCAN_SAT_SEARCH = SCAN_PACKAGE + "IScanSatSearch";
+ /**
+ * Interface for Over-the-Air Download.
+ */
+ public static final String IOAD_UPDATE_INTERFACE = OAD_PACKAGE + "IOadUpdateInterface";
+ /**
+ * Interface for handling conditional access module app related information.
+ */
+ public static final String ICAM_APP_INFO_SERVICE = CAM_PACKAGE + "ICamAppInfoService";
+ /**
+ * Interface for changes on conditional access module app related information.
+ * @hide
+ */
+ public static final String ICAM_APP_INFO_LISTENER = CAM_PACKAGE + "ICamAppInfoListener";
+ /**
+ * Interface for handling conditional access module related information.
+ * @hide
+ */
+ public static final String ICAM_MONITORING_SERVICE = CAM_PACKAGE + "ICamMonitoringService";
+ /**
+ * Interface for changes on conditional access module related information.
+ * @hide
+ */
+ public static final String ICAM_INFO_LISTENER = CAM_PACKAGE + "ICamInfoListener";
+ /**
+ * Interface for handling control of CI+ operations.
+ * @hide
+ */
+ public static final String ICI_OPERATOR_INTERFACE = CAM_PACKAGE + "ICiOperatorInterface";
+ /**
+ * Interfaces for changes on CI+ operations.
+ * @hide
+ */
+ public static final String ICI_OPERATOR_LISTENER = CAM_PACKAGE + "ICiOperatorListener";
+ /**
+ * Interface for handling conditional access module profile related information.
+ * @hide
+ */
+ public static final String ICAM_PROFILE_INTERFACE = CAM_PACKAGE + "ICamProfileInterface";
+ /**
+ * Interface for handling conditional access module DRM related information.
+ * @hide
+ */
+ public static final String ICONTENT_CONTROL_SERVICE = CAM_PACKAGE + "IContentControlService";
+ /**
+ * Interface for changes on DRM.
+ * @hide
+ */
+ public static final String ICAM_DRM_INFO_LISTENER = CAM_PACKAGE + "ICamDrmInfoListener";
+ /**
+ * Interface for handling conditional access module pin related information.
+ * @hide
+ */
+ public static final String ICAM_PIN_SERVICE = CAM_PACKAGE + "ICamPinService";
+ /**
+ * Interface for changes on conditional access module pin capability.
+ * @hide
+ */
+ public static final String ICAM_PIN_CAPABILITY_LISTENER = CAM_PACKAGE
+ + "ICamPinCapabilityListener";
+ /**
+ * Interface for changes on conditional access module pin status.
+ * @hide
+ */
+ public static final String ICAM_PIN_STATUS_LISTENER = CAM_PACKAGE + "ICamPinStatusListener";
+ /**
+ * Interface for handling conditional access module host control service.
+ * @hide
+ */
+ public static final String ICAM_HOST_CONTROL_SERVICE = CAM_PACKAGE + "ICamHostControlService";
+ /**
+ * Interface for handling conditional access module ask release reply.
+ * @hide
+ */
+ public static final String ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK = CAM_PACKAGE
+ + "ICamHostControlAskReleaseReplyCallback";
+ /**
+ * Interface for changes on conditional access module host control service.
+ * @hide
+ */
+ public static final String ICAM_HOST_CONTROL_INFO_LISTENER = CAM_PACKAGE
+ + "ICamHostControlInfoListener";
+ /**
+ * Interface for handling conditional access module host control service tune_quietly_flag.
+ * @hide
+ */
+ public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG = CAM_PACKAGE
+ + "ICamHostControlTuneQuietlyFlag";
+ /**
+ * Interface for changes on conditional access module host control service tune_quietly_flag.
+ * @hide
+ */
+ public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER = CAM_PACKAGE
+ + "ICamHostControlTuneQuietlyFlagListener";
+ /**
+ * Interface for handling conditional access module multi media interface.
+ * @hide
+ */
+ public static final String IMMI_INTERFACE = CAM_PACKAGE + "IMmiInterface";
+ /**
+ * Interface for controlling conditional access module multi media session.
+ * @hide
+ */
+ public static final String IMMI_SESSION = CAM_PACKAGE + "IMmiSession";
+ /**
+ * Interface for changes on conditional access module multi media session status.
+ * @hide
+ */
+ public static final String IMMI_STATUS_CALLBACK = CAM_PACKAGE + "IMmiStatusCallback";
+ /**
+ * Interface for changes on conditional access app info related to entering menu.
+ * @hide
+ */
+ public static final String IENTER_MENU_ERROR_CALLBACK = CAM_PACKAGE + "IEnterMenuErrorCallback";
+ /**
+ * Interface for handling RRT downloadable rating data.
+ * @hide
+ */
+ public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = RATING_PACKAGE
+ + "IDownloadableRatingTableMonitor";
+ /**
+ * Interface for handling Region Rating Table rating system related information.
+ */
+ public static final String IRATING_INTERFACE = RATING_PACKAGE + "IRatingInterface";
+ /**
+ * Interface for handling PMT rating related information.
+ * @hide
+ */
+ public static final String IPMT_RATING_INTERFACE = RATING_PACKAGE + "IPmtRatingInterface";
+ /**
+ * Interface for changes on PMT rating related information.
+ * @hide
+ */
+ public static final String IPMT_RATING_LISTENER = RATING_PACKAGE + "IPmtRatingListener";
+ /**
+ * Interface for handling IVBI rating related information.
+ * @hide
+ */
+ public static final String IVBI_RATING_INTERFACE = RATING_PACKAGE + "IVbiRatingInterface";
+ /**
+ * Interface for changes on IVBI rating related information.
+ * @hide
+ */
+ public static final String IVBI_RATING_LISTENER = RATING_PACKAGE + "IVbiRatingListener";
+ /**
+ * Interface for handling program rating related information.
+ * @hide
+ */
+ public static final String IPROGRAM_INFO = RATING_PACKAGE + "IProgramInfo";
+ /**
+ * Interface for changes on program rating related information.
+ * @hide
+ */
+ public static final String IPROGRAM_INFO_LISTENER = RATING_PACKAGE + "IProgramInfoListener";
+ /**
+ * Interface for getting broadcast time related information.
+ */
+ public static final String IBROADCAST_TIME = TIME_PACKAGE + "BroadcastTime";
+ /**
+ * Interface for handling data service signal information on teletext.
+ */
+ public static final String IDATA_SERVICE_SIGNAL_INFO = TELETEXT_PACKAGE
+ + "IDataServiceSignalInfo";
+ /**
+ * Interface for changes on data service signal information on teletext.
+ * @hide
+ */
+ public static final String IDATA_SERVICE_SIGNAL_INFO_LISTENER = TELETEXT_PACKAGE
+ + "IDataServiceSignalInfoListener";
+ /**
+ * Interface for handling teletext page information.
+ * @hide
+ */
+ public static final String ITELETEXT_PAGE_SUB_CODE = TELETEXT_PACKAGE + "ITeletextPageSubCode";
+ /**
+ * Interface for handling scan background service update.
+ * @hide
+ */
+ public static final String ISCAN_BACKGROUND_SERVICE_UPDATE = SCAN_BSU_PACKAGE
+ + "IScanBackgroundServiceUpdate";
+ /**
+ * Interface for changes on background service update
+ * @hide
+ */
+ public static final String ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER = SCAN_BSU_PACKAGE
+ + "IScanBackgroundServiceUpdateListener";
+ /**
+ * Interface for generating client token.
+ */
+ public static final String ICLIENT_TOKEN = CLIENT_TOKEN_PACKAGE + "IClientToken";
+ /**
+ * Interfaces for handling screen mode information.
+ */
+ public static final String ISCREEN_MODE_SETTINGS = SCREEN_MODE_PACKAGE + "IScreenModeSettings";
+ /**
+ * Interfaces for handling HDMI signal information update.
+ */
+ public static final String IHDMI_SIGNAL_INTERFACE = SIGNAL_PACKAGE + "IHdmiSignalInterface";
+ /**
+ * Interfaces for changes on HDMI signal information update.
+ * @hide
+ */
+ public static final String IHDMI_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ + "IHdmiSignalInfoListener";
+ /**
+ * Interfaces for handling audio signal information update.
+ * @hide
+ */
+ public static final String IAUDIO_SIGNAL_INFO = SIGNAL_PACKAGE + "IAudioSignalInfo";
+ /**
+ * Interfaces for handling analog audio signal information update.
+ * @hide
+ */
+ public static final String IANALOG_AUDIO_INFO = SIGNAL_PACKAGE + "IAnalogAudioInfo";
+ /**
+ * Interfaces for change on audio signal information update.
+ * @hide
+ */
+ public static final String IAUDIO_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ + "IAudioSignalInfoListener";
+ /**
+ * Interfaces for handling video signal information update.
+ * @hide
+ */
+ public static final String IVIDEO_SIGNAL_INFO = SIGNAL_PACKAGE + "IVideoSignalInfo";
+ /**
+ * Interfaces for changes on video signal information update.
+ * @hide
+ */
+ public static final String IVIDEO_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ + "IVideoSignalInfoListener";
+ /**
+ * Interfaces for handling service database updates.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_EDIT = SERVICE_DATABASE_PACKAGE + "IServiceListEdit";
+ /**
+ * Interfaces for changes on service database updates.
+ */
+ public static final String ISERVICE_LIST_EDIT_LISTENER = SERVICE_DATABASE_PACKAGE
+ + "IServiceListEditListener";
+ /**
+ * Interfaces for getting service database related information.
+ * @hide
+ */
+ public static final String ISERVICE_LIST = SERVICE_DATABASE_PACKAGE + "IServiceList";
+ /**
+ * Interfaces for transferring service database related information.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_TRANSFER_INTERFACE = SERVICE_DATABASE_PACKAGE
+ + "IServiceListTransferInterface";
+ /**
+ * Interfaces for exporting service database session.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_EXPORT_SESSION = SERVICE_DATABASE_PACKAGE
+ + "IServiceListExportSession";
+ /**
+ * Interfaces for changes on exporting service database session.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_EXPORT_LISTENER = SERVICE_DATABASE_PACKAGE
+ + "IServiceListExportListener";
+ /**
+ * Interfaces for importing service database session.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_IMPORT_SESSION = SERVICE_DATABASE_PACKAGE
+ + "IServiceListImportSession";
+ /**
+ * Interfaces for changes on importing service database session.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_IMPORT_LISTENER = SERVICE_DATABASE_PACKAGE
+ + "IServiceListImportListener";
+ /**
+ * Interfaces for setting channel list resources.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_SET_CHANNEL_LIST_SESSION = SERVICE_DATABASE_PACKAGE
+ + "IServiceListSetChannelListSession";
+ /**
+ * Interfaces for changes on setting channel list resources.
+ * @hide
+ */
+ public static final String ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER = SERVICE_DATABASE_PACKAGE
+ + "IServiceListSetChannelListListener";
+ /**
+ * Interfaces for transferring channel list resources.
+ * @hide
+ */
+ public static final String ICHANNEL_LIST_TRANSFER = SERVICE_DATABASE_PACKAGE
+ + "IChannelListTransfer";
+ /**
+ * Interface for operations related to recorded contents.
+ */
+ public static final String IRECORDED_CONTENTS = PVR_PACKAGE + "IRecordedContents";
+ /**
+ * Interfaces for changes on deleting record contents.
+ * @hide
+ */
+ public static final String IDELETE_RECORDED_CONTENTS_CALLBACK = PVR_PACKAGE
+ + "IDeleteRecordedContentsCallback";
+ /**
+ * Interfaces for changes on getting record contents.
+ * @hide
+ */
+ public static final String IGET_INFO_RECORDED_CONTENTS_CALLBACK = PVR_PACKAGE
+ + "IGetInfoRecordedContentsCallback";
+ /**
+ * Interfaces for monitoring present event information.
+ */
+ public static final String IEVENT_MONITOR = EVENT_PACKAGE + "IEventMonitor";
+ /**
+ * Interfaces for changes on present event information.
+ * @hide
+ */
+ public static final String IEVENT_MONITOR_LISTENER = EVENT_PACKAGE + "IEventMonitorListener";
+ /**
+ * Interfaces for handling download event information.
+ * @hide
+ */
+ public static final String IEVENT_DOWNLOAD = EVENT_PACKAGE + "IEventDownload";
+ /**
+ * Interfaces for changes on downloading event information.
+ * @hide
+ */
+ public static final String IEVENT_DOWNLOAD_LISTENER = EVENT_PACKAGE + "IEventDownloadListener";
+ /**
+ * Interfaces for handling download event information for DVB and DTMB.
+ * @hide
+ */
+ public static final String IEVENT_DOWNLOAD_SESSION = EVENT_PACKAGE + "IEventDownloadSession";
+ /**
+ * Interfaces for handling analog color system.
+ * @hide
+ */
+ public static final String IANALOG_ATTRIBUTE_INTERFACE = ANALOG_PACKAGE
+ + "IAnalogAttributeInterface";
+ /**
+ * Interfaces for monitoring channel tuned information.
+ * @hide
+ */
+ public static final String ICHANNEL_TUNED_INTERFACE = TUNE_PACKAGE + "IChannelTunedInterface";
+ /**
+ * Interfaces for changes on channel tuned information.
+ * @hide
+ */
+ public static final String ICHANNEL_TUNED_LISTENER = TUNE_PACKAGE + "IChannelTunedListener";
+ /**
+ * Interfaces for handling tuner frontend signal info.
+ * @hide
+ */
+ public static final String ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE = SIGNAL_PACKAGE
+ + "ITunerFrontendSignalInfoInterface";
+ /**
+ * Interfaces for changes on tuner frontend signal info.
+ * @hide
+ */
+ public static final String ITUNER_FRONTEND_SIGNAL_INFO_LISTENER = SIGNAL_PACKAGE
+ + "ITunerFrontendSignalInfoListener";
+ /**
+ * Interfaces for handling mux tune operations.
+ * @hide
+ */
+ public static final String IMUX_TUNE_SESSION = TUNE_PACKAGE + "IMuxTuneSession";
+ /**
+ * Interfaces for initing mux tune session.
+ * @hide
+ */
+ public static final String IMUX_TUNE = TUNE_PACKAGE + "IMuxTune";
+
+ // Set of standardized AIDL interface canonical names
+ private static final Set<String> sTisExtensions = new HashSet<>(Set.of(
+ ISCAN_INTERFACE,
+ ISCAN_SESSION,
+ ISCAN_LISTENER,
+ IHDPLUS_INFO,
+ IOPERATOR_DETECTION,
+ IOPERATOR_DETECTION_LISTENER,
+ IREGION_CHANNEL_LIST,
+ IREGION_CHANNEL_LIST_LISTENER,
+ ITARGET_REGION,
+ ITARGET_REGION_LISTENER,
+ ILCN_CONFLICT,
+ ILCN_CONFLICT_LISTENER,
+ ILCNV2_CHANNEL_LIST,
+ ILCNV2_CHANNEL_LIST_LISTENER,
+ IFAVORITE_NETWORK,
+ IFAVORITE_NETWORK_LISTENER,
+ ITKGS_INFO,
+ ITKGS_INFO_LISTENER,
+ ISCAN_SAT_SEARCH,
+ IOAD_UPDATE_INTERFACE,
+ ICAM_APP_INFO_SERVICE,
+ ICAM_APP_INFO_LISTENER,
+ ICAM_MONITORING_SERVICE,
+ ICAM_INFO_LISTENER,
+ ICI_OPERATOR_INTERFACE,
+ ICI_OPERATOR_LISTENER,
+ ICAM_PROFILE_INTERFACE,
+ ICONTENT_CONTROL_SERVICE,
+ ICAM_DRM_INFO_LISTENER,
+ ICAM_PIN_SERVICE,
+ ICAM_PIN_CAPABILITY_LISTENER,
+ ICAM_PIN_STATUS_LISTENER,
+ ICAM_HOST_CONTROL_SERVICE,
+ ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK,
+ ICAM_HOST_CONTROL_INFO_LISTENER,
+ ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG,
+ ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER,
+ IMMI_INTERFACE,
+ IMMI_SESSION,
+ IMMI_STATUS_CALLBACK,
+ IENTER_MENU_ERROR_CALLBACK,
+ IDOWNLOADABLE_RATING_TABLE_MONITOR,
+ IRATING_INTERFACE,
+ IPMT_RATING_INTERFACE,
+ IPMT_RATING_LISTENER,
+ IVBI_RATING_INTERFACE,
+ IVBI_RATING_LISTENER,
+ IPROGRAM_INFO,
+ IPROGRAM_INFO_LISTENER,
+ IBROADCAST_TIME,
+ IDATA_SERVICE_SIGNAL_INFO,
+ IDATA_SERVICE_SIGNAL_INFO_LISTENER,
+ ITELETEXT_PAGE_SUB_CODE,
+ ISCAN_BACKGROUND_SERVICE_UPDATE,
+ ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER,
+ ICLIENT_TOKEN,
+ ISCREEN_MODE_SETTINGS,
+ IHDMI_SIGNAL_INTERFACE,
+ IHDMI_SIGNAL_INFO_LISTENER,
+ IAUDIO_SIGNAL_INFO,
+ IANALOG_AUDIO_INFO,
+ IAUDIO_SIGNAL_INFO_LISTENER,
+ IVIDEO_SIGNAL_INFO,
+ IVIDEO_SIGNAL_INFO_LISTENER,
+ ISERVICE_LIST_EDIT,
+ ISERVICE_LIST_EDIT_LISTENER,
+ ISERVICE_LIST,
+ ISERVICE_LIST_TRANSFER_INTERFACE,
+ ISERVICE_LIST_EXPORT_SESSION,
+ ISERVICE_LIST_EXPORT_LISTENER,
+ ISERVICE_LIST_IMPORT_SESSION,
+ ISERVICE_LIST_IMPORT_LISTENER,
+ ISERVICE_LIST_SET_CHANNEL_LIST_SESSION,
+ ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER,
+ ICHANNEL_LIST_TRANSFER,
+ IRECORDED_CONTENTS,
+ IDELETE_RECORDED_CONTENTS_CALLBACK,
+ IGET_INFO_RECORDED_CONTENTS_CALLBACK,
+ IEVENT_MONITOR,
+ IEVENT_MONITOR_LISTENER,
+ IEVENT_DOWNLOAD,
+ IEVENT_DOWNLOAD_LISTENER,
+ IEVENT_DOWNLOAD_SESSION,
+ IANALOG_ATTRIBUTE_INTERFACE,
+ ICHANNEL_TUNED_INTERFACE,
+ ICHANNEL_TUNED_LISTENER,
+ ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE,
+ ITUNER_FRONTEND_SIGNAL_INFO_LISTENER,
+ IMUX_TUNE_SESSION,
+ IMUX_TUNE
+ ));
+
+ // Store the mapping between interface names and IBinder
+ private Map<String, IBinder> mExtensionInterfaceIBinderMapping = new HashMap<>();
+
+ TvInputServiceExtensionManager() {
+ }
+
+ /**
+ * Function to return available extension interface names
+ *
+ * @hide
+ */
+ public static @NonNull List<String> getStandardExtensionInterfaceNames() {
+ return new ArrayList<>(sTisExtensions);
+ }
+
+ /**
+ * Function to check if the extension is in the standardization list
+ */
+ static boolean checkIsStandardizedInterfaces(@NonNull String extensionName) {
+ return sTisExtensions.contains(extensionName);
+ }
+
+ /**
+ * Registers IBinder objects that implement standardized AIDL interfaces.
+ * <p>This function should be used by SoCs/OEMs
+ *
+ * @param extensionName Extension Interface Name
+ * @param binder IBinder object to be registered
+ * @return {@link #REGISTER_SUCCESS} on success of registering IBinder object
+ * {@link #REGISTER_FAIL_NAME_NOT_STANDARDIZED} on failure due to registering extension
+ * with non-standardized name
+ * {@link #REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED} on failure due to IBinder not
+ * implementing standardized AIDL interface
+ * {@link #REGISTER_FAIL_REMOTE_EXCEPTION} on failure due to remote exception
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+ @RegisterResult
+ public int registerExtensionIBinder(@StandardizedExtensionName @NonNull String extensionName,
+ @NonNull IBinder binder) {
+ if (!checkIsStandardizedInterfaces(extensionName)) {
+ return REGISTER_FAIL_NAME_NOT_STANDARDIZED;
+ }
+ try {
+ if (binder.getInterfaceDescriptor().equals(extensionName)) {
+ mExtensionInterfaceIBinderMapping.put(extensionName, binder);
+ return REGISTER_SUCCESS;
+ } else {
+ return REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Fetching IBinder object failure due to " + e);
+ return REGISTER_FAIL_REMOTE_EXCEPTION;
+ }
+ }
+
+ /**
+ * Function to get corresponding IBinder object
+ */
+ @Nullable IBinder getExtensionIBinder(@NonNull String extensionName) {
+ return mExtensionInterfaceIBinderMapping.get(extensionName);
+ }
+
+}
diff --git a/media/java/android/mtp/OWNERS b/media/java/android/mtp/OWNERS
index 6b5336e83bdc..77ed08b1f9a5 100644
--- a/media/java/android/mtp/OWNERS
+++ b/media/java/android/mtp/OWNERS
@@ -1,10 +1,9 @@
set noparent
-aprasath@google.com
anothermark@google.com
-kumarashishg@google.com
-sarup@google.com
+febinthattil@google.com
+aprasath@google.com
jsharkey@android.com
jameswei@google.com
rmojumder@google.com
-
+kumarashishg@google.com
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 4492c858c084..001653b08f0c 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1461,6 +1461,21 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
break;
}
+ case MediaCodec::CB_METRICS_FLUSHED:
+ {
+ sp<WrapperObject<std::unique_ptr<mediametrics::Item>>> metrics;
+ CHECK(msg->findObject("metrics", (sp<RefBase>*)&metrics));
+
+ // metrics should never be null. Not sure if checking it here adds any value.
+ if (metrics == nullptr) {
+ return;
+ }
+
+ mediametrics::Item *item = metrics->value.get();
+ obj = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
+ break;
+ }
+
default:
TRESPASS();
}
diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
index 6b5336e83bdc..bdb6cdbea332 100644
--- a/media/tests/MtpTests/OWNERS
+++ b/media/tests/MtpTests/OWNERS
@@ -1,10 +1,9 @@
set noparent
-aprasath@google.com
anothermark@google.com
-kumarashishg@google.com
-sarup@google.com
+febinthattil@google.com
+aprasath@google.com
jsharkey@android.com
jameswei@google.com
rmojumder@google.com
-
+kumarashishg@google.com \ No newline at end of file
diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
index 09573909c288..d9a1221e529c 100644
--- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
+++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
@@ -18,6 +18,7 @@ package android.media.audio.common;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -504,6 +505,27 @@ public final class AidlConversionUnitTests {
assertEquals(AudioDeviceType.OUT_DEVICE, port.ext.getDevice().device.type.type);
}
+ @Test
+ public void testAudioDeviceDescriptionConversion() {
+ for (int nativeDeviceType : AudioSystem.DEVICE_OUT_ALL_SET) {
+ assertNotEquals(
+ AidlConversion.api2aidl_NativeType_AudioDeviceDescription(nativeDeviceType)
+ .type,
+ AudioDeviceType.NONE);
+ }
+
+ for (int nativeDeviceType : AudioSystem.DEVICE_IN_ALL_SET) {
+ if (nativeDeviceType == AudioSystem.DEVICE_IN_COMMUNICATION
+ || nativeDeviceType == AudioSystem.DEVICE_IN_AMBIENT) {
+ continue;
+ }
+ assertNotEquals(
+ AidlConversion.api2aidl_NativeType_AudioDeviceDescription(nativeDeviceType)
+ .type,
+ AudioDeviceType.NONE);
+ }
+ }
+
private static AudioFormatDescription createPcm16FormatAidl() {
final AudioFormatDescription aidl = new AudioFormatDescription();
aidl.type = AudioFormatType.PCM;
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 3eb99c3387f7..da29c49f9d7b 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -55,6 +55,7 @@ cc_library_shared {
"surface_control_input_receiver.cpp",
"choreographer.cpp",
"configuration.cpp",
+ "dynamic_instrumentation_manager.cpp",
"hardware_buffer_jni.cpp",
"input.cpp",
"input_transfer_token.cpp",
@@ -100,6 +101,7 @@ cc_library_shared {
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
"android.os.flags-aconfig-cc",
+ "dynamic_instrumentation_manager_aidl-cpp",
"libnativedisplay",
"libfmq",
],
diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp
new file mode 100644
index 000000000000..d9bacb116f96
--- /dev/null
+++ b/native/android/dynamic_instrumentation_manager.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ADynamicInstrumentationManager"
+#include <android/dynamic_instrumentation_manager.h>
+#include <android/os/instrumentation/ExecutableMethodFileOffsets.h>
+#include <android/os/instrumentation/IDynamicInstrumentationManager.h>
+#include <android/os/instrumentation/MethodDescriptor.h>
+#include <android/os/instrumentation/TargetProcess.h>
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+
+#include <mutex>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::dynamicinstrumentationmanager {
+
+// Global instance of IDynamicInstrumentationManager, service is obtained only on first use.
+static std::mutex mLock;
+static sp<os::instrumentation::IDynamicInstrumentationManager> mService;
+
+sp<os::instrumentation::IDynamicInstrumentationManager> getService() {
+ std::lock_guard<std::mutex> scoped_lock(mLock);
+ if (mService == nullptr || !IInterface::asBinder(mService)->isBinderAlive()) {
+ sp<IBinder> binder =
+ defaultServiceManager()->waitForService(String16("dynamic_instrumentation"));
+ mService = interface_cast<os::instrumentation::IDynamicInstrumentationManager>(binder);
+ }
+ return mService;
+}
+
+} // namespace android::dynamicinstrumentationmanager
+
+using namespace android;
+using namespace dynamicinstrumentationmanager;
+
+struct ADynamicInstrumentationManager_TargetProcess {
+ uid_t uid;
+ uid_t pid;
+ std::string processName;
+
+ ADynamicInstrumentationManager_TargetProcess(uid_t uid, pid_t pid, const char* processName)
+ : uid(uid), pid(pid), processName(processName) {}
+};
+
+ADynamicInstrumentationManager_TargetProcess* ADynamicInstrumentationManager_TargetProcess_create(
+ uid_t uid, pid_t pid, const char* processName) {
+ return new ADynamicInstrumentationManager_TargetProcess(uid, pid, processName);
+}
+
+void ADynamicInstrumentationManager_TargetProcess_destroy(
+ ADynamicInstrumentationManager_TargetProcess* instance) {
+ delete instance;
+}
+
+struct ADynamicInstrumentationManager_MethodDescriptor {
+ std::string fqcn;
+ std::string methodName;
+ std::vector<std::string> fqParameters;
+
+ ADynamicInstrumentationManager_MethodDescriptor(const char* fqcn, const char* methodName,
+ const char* fullyQualifiedParameters[],
+ size_t numParameters)
+ : fqcn(fqcn), methodName(methodName) {
+ std::vector<std::string> fqParameters;
+ fqParameters.reserve(numParameters);
+ std::copy_n(fullyQualifiedParameters, numParameters, std::back_inserter(fqParameters));
+ this->fqParameters = std::move(fqParameters);
+ }
+};
+
+ADynamicInstrumentationManager_MethodDescriptor*
+ADynamicInstrumentationManager_MethodDescriptor_create(const char* fullyQualifiedClassName,
+ const char* methodName,
+ const char* fullyQualifiedParameters[],
+ size_t numParameters) {
+ return new ADynamicInstrumentationManager_MethodDescriptor(fullyQualifiedClassName, methodName,
+ fullyQualifiedParameters,
+ numParameters);
+}
+
+void ADynamicInstrumentationManager_MethodDescriptor_destroy(
+ ADynamicInstrumentationManager_MethodDescriptor* instance) {
+ delete instance;
+}
+
+struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets {
+ std::string containerPath;
+ uint64_t containerOffset;
+ uint64_t methodOffset;
+};
+
+ADynamicInstrumentationManager_ExecutableMethodFileOffsets*
+ADynamicInstrumentationManager_ExecutableMethodFileOffsets_create() {
+ return new ADynamicInstrumentationManager_ExecutableMethodFileOffsets();
+}
+
+const char* ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ return instance->containerPath.c_str();
+}
+
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ return instance->containerOffset;
+}
+
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ return instance->methodOffset;
+}
+
+void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) {
+ delete instance;
+}
+
+int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
+ const ADynamicInstrumentationManager_TargetProcess* targetProcess,
+ const ADynamicInstrumentationManager_MethodDescriptor* methodDescriptor,
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets** out) {
+ android::os::instrumentation::TargetProcess targetProcessParcel;
+ targetProcessParcel.uid = targetProcess->uid;
+ targetProcessParcel.pid = targetProcess->pid;
+ targetProcessParcel.processName = targetProcess->processName;
+
+ android::os::instrumentation::MethodDescriptor methodDescriptorParcel;
+ methodDescriptorParcel.fullyQualifiedClassName = methodDescriptor->fqcn;
+ methodDescriptorParcel.methodName = methodDescriptor->methodName;
+ methodDescriptorParcel.fullyQualifiedParameters = methodDescriptor->fqParameters;
+
+ sp<os::instrumentation::IDynamicInstrumentationManager> service = getService();
+ if (service == nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ std::optional<android::os::instrumentation::ExecutableMethodFileOffsets> offsets;
+ binder_status_t result =
+ service->getExecutableMethodFileOffsets(targetProcessParcel, methodDescriptorParcel,
+ &offsets)
+ .exceptionCode();
+ if (result != OK) {
+ return result;
+ }
+
+ if (offsets != std::nullopt) {
+ auto* value = new ADynamicInstrumentationManager_ExecutableMethodFileOffsets();
+ value->containerPath = offsets->containerPath;
+ value->containerOffset = offsets->containerOffset;
+ value->methodOffset = offsets->methodOffset;
+ *out = value;
+ } else {
+ *out = nullptr;
+ }
+
+ return result;
+} \ No newline at end of file
diff --git a/native/android/include_platform/android/dynamic_instrumentation_manager.h b/native/android/include_platform/android/dynamic_instrumentation_manager.h
new file mode 100644
index 000000000000..6c46288954bf
--- /dev/null
+++ b/native/android/include_platform/android/dynamic_instrumentation_manager.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADYNAMICINSTRUMENTATIONMANAGER_H__
+#define __ADYNAMICINSTRUMENTATIONMANAGER_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+struct ADynamicInstrumentationManager_MethodDescriptor;
+typedef struct ADynamicInstrumentationManager_MethodDescriptor
+ ADynamicInstrumentationManager_MethodDescriptor;
+
+struct ADynamicInstrumentationManager_TargetProcess;
+typedef struct ADynamicInstrumentationManager_TargetProcess
+ ADynamicInstrumentationManager_TargetProcess;
+
+struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets;
+typedef struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets;
+
+/**
+ * Initializes an ADynamicInstrumentationManager_TargetProcess. Caller must clean up when they are
+ * done with ADynamicInstrumentationManager_TargetProcess_destroy.
+ *
+ * @param uid of targeted process.
+ * @param pid of targeted process.
+ * @param processName to disambiguate from corner cases that may arise from pid reuse.
+ */
+ADynamicInstrumentationManager_TargetProcess* _Nonnull
+ ADynamicInstrumentationManager_TargetProcess_create(
+ uid_t uid, pid_t pid, const char* _Nonnull processName) __INTRODUCED_IN(36);
+/**
+ * Clean up an ADynamicInstrumentationManager_TargetProcess.
+ *
+ * @param instance returned from ADynamicInstrumentationManager_TargetProcess_create.
+ */
+void ADynamicInstrumentationManager_TargetProcess_destroy(
+ ADynamicInstrumentationManager_TargetProcess* _Nonnull instance) __INTRODUCED_IN(36);
+
+/**
+ * Initializes an ADynamicInstrumentationManager_MethodDescriptor. Caller must clean up when they
+ * are done with ADynamicInstrumentationManager_MethodDescriptor_Destroy.
+ *
+ * @param fullyQualifiedClassName fqcn of class containing the method.
+ * @param methodName
+ * @param fullyQualifiedParameters fqcn of parameters of the method's signature, or e.g. "int" for
+ * primitives.
+ * @param numParameters length of `fullyQualifiedParameters` array.
+ */
+ADynamicInstrumentationManager_MethodDescriptor* _Nonnull
+ ADynamicInstrumentationManager_MethodDescriptor_create(
+ const char* _Nonnull fullyQualifiedClassName, const char* _Nonnull methodName,
+ const char* _Nonnull fullyQualifiedParameters[_Nonnull], size_t numParameters)
+ __INTRODUCED_IN(36);
+/**
+ * Clean up an ADynamicInstrumentationManager_MethodDescriptor.
+ *
+ * @param instance returned from ADynamicInstrumentationManager_MethodDescriptor_create.
+ */
+void ADynamicInstrumentationManager_MethodDescriptor_destroy(
+ ADynamicInstrumentationManager_MethodDescriptor* _Nonnull instance) __INTRODUCED_IN(36);
+
+/**
+ * Get the containerPath calculated by
+ * ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @return The OS path of the containing file.
+ */
+const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Get the containerOffset calculated by
+ * ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @return The offset of the containing file within the process' memory.
+ */
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Get the methodOffset calculated by ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ * @return The offset of the method within the containing file.
+ */
+uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Clean up an ADynamicInstrumentationManager_ExecutableMethodFileOffsets.
+ *
+ * @param instance returned from ADynamicInstrumentationManager_getExecutableMethodFileOffsets.
+ */
+void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy(
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance)
+ __INTRODUCED_IN(36);
+/**
+ * Provides ART metadata about the described java method within the target process.
+ *
+ * @param targetProcess describes for which process the data is requested.
+ * @param methodDescriptor describes the targeted method.
+ * @param out will be populated with the data if successful. A nullptr combined
+ * with an OK status means that the program method is defined, but the offset
+ * info was unavailable because it is not AOT compiled.
+ * @return status indicating success or failure. The values correspond to the `binder_exception_t`
+ * enum values from <android/binder_status.h>.
+ */
+int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets(
+ const ADynamicInstrumentationManager_TargetProcess* _Nonnull targetProcess,
+ const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull methodDescriptor,
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull* _Nullable out)
+ __INTRODUCED_IN(36);
+
+__END_DECLS
+
+#endif // __ADYNAMICINSTRUMENTATIONMANAGER_H__
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b025cb880ee7..a0460572abfc 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -4,6 +4,15 @@ LIBANDROID {
AActivityManager_removeUidImportanceListener; # systemapi introduced=31
AActivityManager_isUidActive; # systemapi introduced=31
AActivityManager_getUidImportance; # systemapi introduced=31
+ ADynamicInstrumentationManager_TargetProcess_create; # systemapi
+ ADynamicInstrumentationManager_TargetProcess_destroy; # systemapi
+ ADynamicInstrumentationManager_MethodDescriptor_create; # systemapi
+ ADynamicInstrumentationManager_MethodDescriptor_destroy; # systemapi
+ ADynamicInstrumentationManager_getExecutableMethodFileOffsets; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset; # systemapi
+ ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy; # systemapi
AAssetDir_close;
AAssetDir_getNextFileName;
AAssetDir_rewind;
diff --git a/native/android/tests/thermal/NativeThermalUnitTest.cpp b/native/android/tests/thermal/NativeThermalUnitTest.cpp
index 6d6861a3026a..4e319fc41d7c 100644
--- a/native/android/tests/thermal/NativeThermalUnitTest.cpp
+++ b/native/android/tests/thermal/NativeThermalUnitTest.cpp
@@ -67,6 +67,14 @@ public:
MOCK_METHOD(Status, getThermalHeadroomThresholds, (::std::vector<float> * _aidl_return),
(override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+ MOCK_METHOD(Status, registerThermalHeadroomListener,
+ (const ::android::sp<::android::os::IThermalHeadroomListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, unregisterThermalHeadroomListener,
+ (const ::android::sp<::android::os::IThermalHeadroomListener>& listener,
+ bool* _aidl_return),
+ (override));
};
class NativeThermalUnitTest : public Test {
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 96b7c1339190..008120429c40 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -207,6 +207,7 @@ package android.nfc.cardemulation {
method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public boolean isEuiccSupported();
method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
+ method @FlaggedApi("android.nfc.nfc_event_listener") public void registerNfcEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.cardemulation.CardEmulation.NfcEventListener);
method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
method public boolean removeAidsForService(android.content.ComponentName, String);
@@ -216,6 +217,7 @@ package android.nfc.cardemulation {
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
method public boolean supportsAidPrefixRegistration();
+ method @FlaggedApi("android.nfc.nfc_event_listener") public void unregisterNfcEventListener(@NonNull android.nfc.cardemulation.CardEmulation.NfcEventListener);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
@@ -233,13 +235,16 @@ package android.nfc.cardemulation {
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
}
+ @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventListener {
+ method @FlaggedApi("android.nfc.nfc_event_listener") public default void onObserveModeStateChanged(boolean);
+ method @FlaggedApi("android.nfc.nfc_event_listener") public default void onPreferredServiceChanged(boolean);
+ }
+
public abstract class HostApduService extends android.app.Service {
ctor public HostApduService();
method public final void notifyUnhandled();
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
- method @FlaggedApi("android.nfc.nfc_event_listener") public void onObserveModeStateChanged(boolean);
- method @FlaggedApi("android.nfc.nfc_event_listener") public void onPreferredServiceChanged(boolean);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>);
method public final void sendResponseApdu(byte[]);
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 24e14e69637b..c25f77b12116 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -59,6 +59,7 @@ package android.nfc {
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List<android.nfc.NfcRoutingTableEntry> getRoutingTable();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean hasUserEnabledNfc();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAutoChangeEnabled();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent();
@@ -91,6 +92,7 @@ package android.nfc {
method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onDisableFinished(int);
method public void onDisableStarted();
+ method public void onEeListenActivated(boolean);
method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onEnableFinished(int);
method public void onEnableStarted();
@@ -98,23 +100,80 @@ package android.nfc {
method public void onHceEventReceived(int);
method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String);
method public void onLaunchHceTapAgainDialog(@NonNull android.nfc.cardemulation.ApduServiceInfo, @NonNull String);
+ method public void onLogEventNotified(@NonNull android.nfc.OemLogItems);
method public void onNdefMessage(@NonNull android.nfc.Tag, @NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onReaderOptionChanged(boolean);
method public void onRfDiscoveryStarted(boolean);
method public void onRfFieldActivated(boolean);
method public void onRoutingChanged();
+ method public void onRoutingTableFull();
method public void onStateUpdated(int);
- method public void onTagConnected(boolean, @NonNull android.nfc.Tag);
+ method public void onTagConnected(boolean);
method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>);
}
+ @FlaggedApi("android.nfc.nfc_oem_extension") public abstract class NfcRoutingTableEntry {
+ method public int getNfceeId();
+ }
+
+ @FlaggedApi("android.nfc.nfc_oem_extension") public final class OemLogItems implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAction();
+ method public int getCallingPid();
+ method @Nullable public byte[] getCommandApdu();
+ method public int getEvent();
+ method @Nullable public byte[] getResponseApdu();
+ method @Nullable public java.time.Instant getRfFieldEventTimeMillis();
+ method @Nullable public android.nfc.Tag getTag();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.nfc.OemLogItems> CREATOR;
+ field public static final int EVENT_DISABLE = 2; // 0x2
+ field public static final int EVENT_ENABLE = 1; // 0x1
+ field public static final int EVENT_UNSET = 0; // 0x0
+ field public static final int LOG_ACTION_HCE_DATA = 516; // 0x204
+ field public static final int LOG_ACTION_NFC_TOGGLE = 513; // 0x201
+ field public static final int LOG_ACTION_RF_FIELD_STATE_CHANGED = 1; // 0x1
+ field public static final int LOG_ACTION_SCREEN_STATE_CHANGED = 518; // 0x206
+ field public static final int LOG_ACTION_TAG_DETECTED = 3; // 0x3
+ }
+
@FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingStatus {
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultIsoDepRoute();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultOffHostRoute();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultRoute();
}
+ @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableAidEntry extends android.nfc.NfcRoutingTableEntry {
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public String getAid();
+ }
+
+ @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableProtocolEntry extends android.nfc.NfcRoutingTableEntry {
+ method @FlaggedApi("android.nfc.nfc_oem_extension") public int getProtocol();
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_ISO_DEP = 4; // 0x4
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_NDEF = 7; // 0x7
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_NFC_DEP = 5; // 0x5
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T1T = 1; // 0x1
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T2T = 2; // 0x2
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T3T = 3; // 0x3
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T5T = 6; // 0x6
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_UNDETERMINED = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_UNSUPPORTED = -1; // 0xffffffff
+ }
+
+ @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableSystemCodeEntry extends android.nfc.NfcRoutingTableEntry {
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public byte[] getSystemCode();
+ }
+
+ @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableTechnologyEntry extends android.nfc.NfcRoutingTableEntry {
+ method @FlaggedApi("android.nfc.nfc_oem_extension") public int getTechnology();
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_A = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_B = 1; // 0x1
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_F = 2; // 0x2
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_UNSUPPORTED = -1; // 0xffffffff
+ field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_V = 3; // 0x3
+ }
+
}
package android.nfc.cardemulation {
diff --git a/nfc/java/android/nfc/ComponentNameAndUser.aidl b/nfc/java/android/nfc/ComponentNameAndUser.aidl
new file mode 100644
index 000000000000..e677998a7970
--- /dev/null
+++ b/nfc/java/android/nfc/ComponentNameAndUser.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable ComponentNameAndUser; \ No newline at end of file
diff --git a/nfc/java/android/nfc/ComponentNameAndUser.java b/nfc/java/android/nfc/ComponentNameAndUser.java
new file mode 100644
index 000000000000..59e6c62926c9
--- /dev/null
+++ b/nfc/java/android/nfc/ComponentNameAndUser.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public class ComponentNameAndUser implements Parcelable {
+ @UserIdInt private final int mUserId;
+ private ComponentName mComponentName;
+
+ public ComponentNameAndUser(@UserIdInt int userId, ComponentName componentName) {
+ mUserId = userId;
+ mComponentName = componentName;
+ }
+
+ /**
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mUserId);
+ out.writeParcelable(mComponentName, flags);
+ }
+
+ public static final Parcelable.Creator<ComponentNameAndUser> CREATOR =
+ new Parcelable.Creator<ComponentNameAndUser>() {
+ public ComponentNameAndUser createFromParcel(Parcel in) {
+ return new ComponentNameAndUser(in);
+ }
+
+ public ComponentNameAndUser[] newArray(int size) {
+ return new ComponentNameAndUser[size];
+ }
+ };
+
+ private ComponentNameAndUser(Parcel in) {
+ mUserId = in.readInt();
+ mComponentName = in.readParcelable(null, ComponentName.class);
+ }
+
+ @UserIdInt
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ @Override
+ public String toString() {
+ return mComponentName + " for user id: " + mUserId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj != null && obj instanceof ComponentNameAndUser) {
+ ComponentNameAndUser other = (ComponentNameAndUser) obj;
+ return other.getUserId() == mUserId
+ && Objects.equals(other.getComponentName(), mComponentName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ if (mComponentName == null) {
+ return mUserId;
+ }
+ return mComponentName.hashCode() + mUserId;
+ }
+}
diff --git a/nfc/java/android/nfc/Entry.aidl b/nfc/java/android/nfc/Entry.aidl
new file mode 100644
index 000000000000..148c4ec86845
--- /dev/null
+++ b/nfc/java/android/nfc/Entry.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+parcelable Entry; \ No newline at end of file
diff --git a/nfc/java/android/nfc/Entry.java b/nfc/java/android/nfc/Entry.java
new file mode 100644
index 000000000000..49d0f10dbfce
--- /dev/null
+++ b/nfc/java/android/nfc/Entry.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+/** @hide */
+public final class Entry implements Parcelable {
+ private final byte mType;
+ private final byte mNfceeId;
+ private final String mEntry;
+
+ public Entry(String entry, byte type, byte nfceeId) {
+ mEntry = entry;
+ mType = type;
+ mNfceeId = nfceeId;
+ }
+
+ public byte getType() {
+ return mType;
+ }
+
+ public byte getNfceeId() {
+ return mNfceeId;
+ }
+
+ public String getEntry() {
+ return mEntry;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private Entry(Parcel in) {
+ this.mEntry = in.readString();
+ this.mNfceeId = in.readByte();
+ this.mType = in.readByte();
+ }
+
+ public static final @NonNull Parcelable.Creator<Entry> CREATOR =
+ new Parcelable.Creator<Entry>() {
+ @Override
+ public Entry createFromParcel(Parcel in) {
+ return new Entry(in);
+ }
+
+ @Override
+ public Entry[] newArray(int size) {
+ return new Entry[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mEntry);
+ dest.writeByte(mNfceeId);
+ dest.writeByte(mType);
+ }
+}
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index a166b283f55b..40fd0683f465 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -18,6 +18,7 @@ package android.nfc;
import android.app.PendingIntent;
import android.content.IntentFilter;
+import android.nfc.Entry;
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TechListParcel;
@@ -117,4 +118,6 @@ interface INfcAdapter
void triggerInitialization();
boolean getSettingStatus();
boolean isTagPresent();
+ List<Entry> getRoutingTableEntryList();
+ void indicateDataMigration(boolean inProgress, String pkg);
}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 8535e4a9cfd2..5e2e92d958a4 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -17,6 +17,8 @@
package android.nfc;
import android.content.ComponentName;
+import android.nfc.INfcEventListener;
+
import android.nfc.cardemulation.AidGroup;
import android.nfc.cardemulation.ApduServiceInfo;
import android.os.RemoteCallback;
@@ -55,4 +57,7 @@ interface INfcCardEmulation
boolean isAutoChangeEnabled();
List<String> getRoutingStatus();
void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech, String sc);
+
+ void registerNfcEventListener(in INfcEventListener listener);
+ void unregisterNfcEventListener(in INfcEventListener listener);
}
diff --git a/nfc/java/android/nfc/INfcEventListener.aidl b/nfc/java/android/nfc/INfcEventListener.aidl
new file mode 100644
index 000000000000..5162c26ac536
--- /dev/null
+++ b/nfc/java/android/nfc/INfcEventListener.aidl
@@ -0,0 +1,11 @@
+package android.nfc;
+
+import android.nfc.ComponentNameAndUser;
+
+/**
+ * @hide
+ */
+oneway interface INfcEventListener {
+ void onPreferredServiceChanged(in ComponentNameAndUser ComponentNameAndUser);
+ void onObserveModeStateChanged(boolean isEnabled);
+} \ No newline at end of file
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index 48c7ee659266..fb793b024288 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -18,6 +18,7 @@ package android.nfc;
import android.content.ComponentName;
import android.nfc.cardemulation.ApduServiceInfo;
import android.nfc.NdefMessage;
+import android.nfc.OemLogItems;
import android.nfc.Tag;
import android.os.ResultReceiver;
@@ -27,7 +28,7 @@ import java.util.List;
* @hide
*/
interface INfcOemExtensionCallback {
- void onTagConnected(boolean connected, in Tag tag);
+ void onTagConnected(boolean connected);
void onStateUpdated(int state);
void onApplyRouting(in ResultReceiver isSkipped);
void onNdefRead(in ResultReceiver isSkipped);
@@ -46,8 +47,11 @@ interface INfcOemExtensionCallback {
void onCardEmulationActivated(boolean isActivated);
void onRfFieldActivated(boolean isActivated);
void onRfDiscoveryStarted(boolean isDiscoveryStarted);
+ void onEeListenActivated(boolean isActivated);
void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer);
void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent);
void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category);
void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category);
+ void onRoutingTableFull();
+ void onLogEventNotified(in OemLogItems item);
}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index d9fd42fd4f7a..c5d8191b22e6 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -2795,11 +2795,8 @@ public final class NfcAdapter {
@IntRange(from = 0, to = 15) int gid, @IntRange(from = 0) int oid,
@NonNull byte[] payload) {
Objects.requireNonNull(payload, "Payload must not be null");
- try {
- return sService.sendVendorNciMessage(mt, gid, oid, payload);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return callServiceReturn(() -> sService.sendVendorNciMessage(mt, gid, oid, payload),
+ SEND_VENDOR_NCI_STATUS_FAILED);
}
/**
@@ -2873,6 +2870,18 @@ public final class NfcAdapter {
}
/**
+ * Used by data migration to indicate data migration is in progrerss or not.
+ *
+ * Note: This is @hide intentionally since the client is inside the NFC apex.
+ * @param inProgress true if migration is in progress, false once done.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void indicateDataMigration(boolean inProgress) {
+ callService(() -> sService.indicateDataMigration(inProgress, mContext.getPackageName()));
+ }
+
+ /**
* Returns an instance of {@link NfcOemExtension} associated with {@link NfcAdapter} instance.
* @hide
*/
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 474ff8c663e6..57ee981caf9c 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -43,6 +43,7 @@ import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -71,6 +72,11 @@ import java.util.function.Supplier;
public final class NfcOemExtension {
private static final String TAG = "NfcOemExtension";
private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000;
+ private static final int TYPE_TECHNOLOGY = 0;
+ private static final int TYPE_PROTOCOL = 1;
+ private static final int TYPE_AID = 2;
+ private static final int TYPE_SYSTEMCODE = 3;
+
private final NfcAdapter mAdapter;
private final NfcOemExtensionCallback mOemNfcExtensionCallback;
private boolean mIsRegistered = false;
@@ -80,6 +86,7 @@ public final class NfcOemExtension {
private boolean mCardEmulationActivated = false;
private boolean mRfFieldActivated = false;
private boolean mRfDiscoveryStarted = false;
+ private boolean mEeListenActivated = false;
/**
* Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled.
@@ -195,9 +202,8 @@ public final class NfcOemExtension {
* ex - if tag is connected notify cover and Nfctest app if app is in testing mode
*
* @param connected status of the tag true if tag is connected otherwise false
- * @param tag Tag details
*/
- void onTagConnected(boolean connected, @NonNull Tag tag);
+ void onTagConnected(boolean connected);
/**
* Update the Nfc Adapter State
@@ -327,6 +333,13 @@ public final class NfcOemExtension {
void onRfDiscoveryStarted(boolean isDiscoveryStarted);
/**
+ * Notifies the NFCEE (NFC Execution Environment) Listen has been activated.
+ *
+ * @param isActivated true, if EE Listen is ON, else EE Listen is OFF.
+ */
+ void onEeListenActivated(boolean isActivated);
+
+ /**
* Gets the intent to find the OEM package in the OEM App market. If the consumer returns
* {@code null} or a timeout occurs, the intent from the first available package will be
* used instead.
@@ -379,6 +392,19 @@ public final class NfcOemExtension {
* @param category the category of the service
*/
void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category);
+
+ /**
+ * Callback to indicate that routing table is full and the OEM can optionally launch a
+ * dialog to request the user to remove some Card Emulation apps from the device to free
+ * routing table space.
+ */
+ void onRoutingTableFull();
+
+ /**
+ * Callback when OEM specified log event are notified.
+ * @param item the log items that contains log information of NFC event.
+ */
+ void onLogEventNotified(@NonNull OemLogItems item);
}
@@ -437,6 +463,7 @@ public final class NfcOemExtension {
callback.onCardEmulationActivated(mCardEmulationActivated);
callback.onRfFieldActivated(mRfFieldActivated);
callback.onRfDiscoveryStarted(mRfDiscoveryStarted);
+ callback.onEeListenActivated(mEeListenActivated);
});
}
}
@@ -681,12 +708,45 @@ public final class NfcOemExtension {
));
}
+ /**
+ * Gets current routing table entries.
+ * @return List of {@link NfcRoutingTableEntry} representing current routing table
+ */
+ @NonNull
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public List<NfcRoutingTableEntry> getRoutingTable() {
+ List<Entry> entryList = NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sService.getRoutingTableEntryList(), null);
+ List<NfcRoutingTableEntry> result = new ArrayList<>();
+ for (Entry entry : entryList) {
+ switch (entry.getType()) {
+ case TYPE_TECHNOLOGY -> result.add(
+ new RoutingTableTechnologyEntry(entry.getNfceeId(),
+ RoutingTableTechnologyEntry.techStringToInt(entry.getEntry()))
+ );
+ case TYPE_PROTOCOL -> result.add(
+ new RoutingTableProtocolEntry(entry.getNfceeId(),
+ RoutingTableProtocolEntry.protocolStringToInt(entry.getEntry()))
+ );
+ case TYPE_AID -> result.add(
+ new RoutingTableAidEntry(entry.getNfceeId(), entry.getEntry())
+ );
+ case TYPE_SYSTEMCODE -> result.add(
+ new RoutingTableSystemCodeEntry(entry.getNfceeId(),
+ entry.getEntry().getBytes(StandardCharsets.UTF_8))
+ );
+ }
+ }
+ return result;
+ }
+
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
@Override
- public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
+ public void onTagConnected(boolean connected) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
- handleVoid2ArgCallback(connected, tag, cb::onTagConnected, ex));
+ handleVoidCallback(connected, cb::onTagConnected, ex));
}
@Override
@@ -711,6 +771,13 @@ public final class NfcOemExtension {
}
@Override
+ public void onEeListenActivated(boolean isActivated) throws RemoteException {
+ mEeListenActivated = isActivated;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isActivated, cb::onEeListenActivated, ex));
+ }
+
+ @Override
public void onStateUpdated(int state) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
handleVoidCallback(state, cb::onStateUpdated, ex));
@@ -793,6 +860,12 @@ public final class NfcOemExtension {
handleVoidCallback(enabled, cb::onReaderOptionChanged, ex));
}
+ public void onRoutingTableFull() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null,
+ (Object input) -> cb.onRoutingTableFull(), ex));
+ }
+
@Override
public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer)
throws RemoteException {
@@ -846,6 +919,12 @@ public final class NfcOemExtension {
handleVoid2ArgCallback(service, category, cb::onLaunchHceTapAgainDialog, ex));
}
+ @Override
+ public void onLogEventNotified(OemLogItems item) throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(item, cb::onLogEventNotified, ex));
+ }
+
private <T> void handleVoidCallback(
T input, Consumer<T> callbackMethod, Executor executor) {
synchronized (mLock) {
diff --git a/nfc/java/android/nfc/NfcRoutingTableEntry.java b/nfc/java/android/nfc/NfcRoutingTableEntry.java
new file mode 100644
index 000000000000..4e913776a030
--- /dev/null
+++ b/nfc/java/android/nfc/NfcRoutingTableEntry.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+/**
+ * Class to represent an entry of routing table. This class is abstract and extended by
+ * {@link RoutingTableTechnologyEntry}, {@link RoutingTableProtocolEntry},
+ * {@link RoutingTableAidEntry} and {@link RoutingTableSystemCodeEntry}.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public abstract class NfcRoutingTableEntry {
+ private final int mNfceeId;
+
+ /** @hide */
+ protected NfcRoutingTableEntry(int nfceeId) {
+ mNfceeId = nfceeId;
+ }
+
+ /**
+ * Gets the NFCEE Id of this entry.
+ * @return an integer of NFCEE Id.
+ */
+ public int getNfceeId() {
+ return mNfceeId;
+ }
+}
diff --git a/nfc/java/android/nfc/OemLogItems.aidl b/nfc/java/android/nfc/OemLogItems.aidl
new file mode 100644
index 000000000000..3bcb445fc7d2
--- /dev/null
+++ b/nfc/java/android/nfc/OemLogItems.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+parcelable OemLogItems; \ No newline at end of file
diff --git a/nfc/java/android/nfc/OemLogItems.java b/nfc/java/android/nfc/OemLogItems.java
new file mode 100644
index 000000000000..6671941c1cc9
--- /dev/null
+++ b/nfc/java/android/nfc/OemLogItems.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+
+/**
+ * A log class for OEMs to get log information of NFC events.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public final class OemLogItems implements Parcelable {
+ /**
+ * Used when RF field state is changed.
+ */
+ public static final int LOG_ACTION_RF_FIELD_STATE_CHANGED = 0X01;
+ /**
+ * Used when NFC is toggled. Event should be set to {@link LogEvent#EVENT_ENABLE} or
+ * {@link LogEvent#EVENT_DISABLE} if this action is used.
+ */
+ public static final int LOG_ACTION_NFC_TOGGLE = 0x0201;
+ /**
+ * Used when sending host routing status.
+ */
+ public static final int LOG_ACTION_HCE_DATA = 0x0204;
+ /**
+ * Used when screen state is changed.
+ */
+ public static final int LOG_ACTION_SCREEN_STATE_CHANGED = 0x0206;
+ /**
+ * Used when tag is detected.
+ */
+ public static final int LOG_ACTION_TAG_DETECTED = 0x03;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "LOG_ACTION_" }, value = {
+ LOG_ACTION_RF_FIELD_STATE_CHANGED,
+ LOG_ACTION_NFC_TOGGLE,
+ LOG_ACTION_HCE_DATA,
+ LOG_ACTION_SCREEN_STATE_CHANGED,
+ LOG_ACTION_TAG_DETECTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LogAction {}
+
+ /**
+ * Represents the event is not set.
+ */
+ public static final int EVENT_UNSET = 0;
+ /**
+ * Represents nfc enable is called.
+ */
+ public static final int EVENT_ENABLE = 1;
+ /**
+ * Represents nfc disable is called.
+ */
+ public static final int EVENT_DISABLE = 2;
+ /** @hide */
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_UNSET,
+ EVENT_ENABLE,
+ EVENT_DISABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LogEvent {}
+ private int mAction;
+ private int mEvent;
+ private int mCallingPid;
+ private byte[] mCommandApdus;
+ private byte[] mResponseApdus;
+ private Instant mRfFieldOnTime;
+ private Tag mTag;
+
+ /** @hide */
+ public OemLogItems(@LogAction int action, @LogEvent int event, int callingPid,
+ byte[] commandApdus, byte[] responseApdus, Instant rfFieldOnTime,
+ Tag tag) {
+ mAction = action;
+ mEvent = event;
+ mTag = tag;
+ mCallingPid = callingPid;
+ mCommandApdus = commandApdus;
+ mResponseApdus = responseApdus;
+ mRfFieldOnTime = rfFieldOnTime;
+ }
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable
+ * instance's marshaled representation. For example, if the object will
+ * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
+ * the return value of this method must include the
+ * {@link #CONTENTS_FILE_DESCRIPTOR} bit.
+ *
+ * @return a bitmask indicating the set of special object types marshaled
+ * by this Parcelable object instance.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mAction);
+ dest.writeInt(mEvent);
+ dest.writeInt(mCallingPid);
+ dest.writeInt(mCommandApdus.length);
+ dest.writeByteArray(mCommandApdus);
+ dest.writeInt(mResponseApdus.length);
+ dest.writeByteArray(mResponseApdus);
+ dest.writeLong(mRfFieldOnTime.getEpochSecond());
+ dest.writeInt(mRfFieldOnTime.getNano());
+ dest.writeParcelable(mTag, 0);
+ }
+
+ /** @hide */
+ public static class Builder {
+ private final OemLogItems mItem;
+
+ public Builder(@LogAction int type) {
+ mItem = new OemLogItems(type, EVENT_UNSET, 0, new byte[0], new byte[0], null, null);
+ }
+
+ /** Setter of the log action. */
+ public OemLogItems.Builder setAction(@LogAction int action) {
+ mItem.mAction = action;
+ return this;
+ }
+
+ /** Setter of the log calling event. */
+ public OemLogItems.Builder setCallingEvent(@LogEvent int event) {
+ mItem.mEvent = event;
+ return this;
+ }
+
+ /** Setter of the log calling Pid. */
+ public OemLogItems.Builder setCallingPid(int pid) {
+ mItem.mCallingPid = pid;
+ return this;
+ }
+
+ /** Setter of APDU command. */
+ public OemLogItems.Builder setApduCommand(byte[] apdus) {
+ mItem.mCommandApdus = apdus;
+ return this;
+ }
+
+ /** Setter of RF field on time. */
+ public OemLogItems.Builder setRfFieldOnTime(Instant time) {
+ mItem.mRfFieldOnTime = time;
+ return this;
+ }
+
+ /** Setter of APDU response. */
+ public OemLogItems.Builder setApduResponse(byte[] apdus) {
+ mItem.mResponseApdus = apdus;
+ return this;
+ }
+
+ /** Setter of dispatched tag. */
+ public OemLogItems.Builder setTag(Tag tag) {
+ mItem.mTag = tag;
+ return this;
+ }
+
+ /** Builds an {@link OemLogItems} instance. */
+ public OemLogItems build() {
+ return mItem;
+ }
+ }
+
+ /**
+ * Gets the action of this log.
+ * @return one of {@link LogAction}
+ */
+ @LogAction
+ public int getAction() {
+ return mAction;
+ }
+
+ /**
+ * Gets the event of this log. This will be set to {@link LogEvent#EVENT_ENABLE} or
+ * {@link LogEvent#EVENT_DISABLE} only when action is set to
+ * {@link LogAction#LOG_ACTION_NFC_TOGGLE}
+ * @return one of {@link LogEvent}
+ */
+ @LogEvent
+ public int getEvent() {
+ return mEvent;
+ }
+
+ /**
+ * Gets the calling Pid of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_NFC_TOGGLE}
+ * @return calling Pid
+ */
+ public int getCallingPid() {
+ return mCallingPid;
+ }
+
+ /**
+ * Gets the command APDUs of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_HCE_DATA}
+ * @return a byte array of command APDUs with the same format as
+ * {@link android.nfc.cardemulation.HostApduService#sendResponseApdu(byte[])}
+ */
+ @Nullable
+ public byte[] getCommandApdu() {
+ return mCommandApdus;
+ }
+
+ /**
+ * Gets the response APDUs of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_HCE_DATA}
+ * @return a byte array of response APDUs with the same format as
+ * {@link android.nfc.cardemulation.HostApduService#sendResponseApdu(byte[])}
+ */
+ @Nullable
+ public byte[] getResponseApdu() {
+ return mResponseApdus;
+ }
+
+ /**
+ * Gets the RF field event time in this log in millisecond. This field will be set only when
+ * action is set to {@link LogAction#LOG_ACTION_RF_FIELD_STATE_CHANGED}
+ * @return an {@link Instant} of RF field event time.
+ */
+ @Nullable
+ public Instant getRfFieldEventTimeMillis() {
+ return mRfFieldOnTime;
+ }
+
+ /**
+ * Gets the tag of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_TAG_DETECTED}
+ * @return a detected {@link Tag} in {@link #LOG_ACTION_TAG_DETECTED} case. Return
+ * null otherwise.
+ */
+ @Nullable
+ public Tag getTag() {
+ return mTag;
+ }
+
+ private String byteToHex(byte[] bytes) {
+ char[] HexArray = "0123456789ABCDEF".toCharArray();
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = HexArray[v >>> 4];
+ hexChars[j * 2 + 1] = HexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ @Override
+ public String toString() {
+ return "[mCommandApdus: "
+ + ((mCommandApdus != null) ? byteToHex(mCommandApdus) : "null")
+ + "[mResponseApdus: "
+ + ((mResponseApdus != null) ? byteToHex(mResponseApdus) : "null")
+ + ", mCallingApi= " + mEvent
+ + ", mAction= " + mAction
+ + ", mCallingPId = " + mCallingPid
+ + ", mRfFieldOnTime= " + mRfFieldOnTime;
+ }
+ private OemLogItems(Parcel in) {
+ this.mAction = in.readInt();
+ this.mEvent = in.readInt();
+ this.mCallingPid = in.readInt();
+ this.mCommandApdus = new byte[in.readInt()];
+ in.readByteArray(this.mCommandApdus);
+ this.mResponseApdus = new byte[in.readInt()];
+ in.readByteArray(this.mResponseApdus);
+ this.mRfFieldOnTime = Instant.ofEpochSecond(in.readLong(), in.readInt());
+ this.mTag = in.readParcelable(Tag.class.getClassLoader(), Tag.class);
+ }
+
+ public static final @NonNull Parcelable.Creator<OemLogItems> CREATOR =
+ new Parcelable.Creator<OemLogItems>() {
+ @Override
+ public OemLogItems createFromParcel(Parcel in) {
+ return new OemLogItems(in);
+ }
+
+ @Override
+ public OemLogItems[] newArray(int size) {
+ return new OemLogItems[size];
+ }
+ };
+
+}
diff --git a/nfc/java/android/nfc/RoutingTableAidEntry.java b/nfc/java/android/nfc/RoutingTableAidEntry.java
new file mode 100644
index 000000000000..7634fe342ab9
--- /dev/null
+++ b/nfc/java/android/nfc/RoutingTableAidEntry.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * Represents an AID entry in current routing table.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public class RoutingTableAidEntry extends NfcRoutingTableEntry {
+ private final String mValue;
+
+ /** @hide */
+ public RoutingTableAidEntry(int nfceeId, String value) {
+ super(nfceeId);
+ this.mValue = value;
+ }
+
+ /**
+ * Gets AID value.
+ * @return String of AID
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @NonNull
+ public String getAid() {
+ return mValue;
+ }
+}
diff --git a/nfc/java/android/nfc/RoutingTableProtocolEntry.java b/nfc/java/android/nfc/RoutingTableProtocolEntry.java
new file mode 100644
index 000000000000..0c5be7dd9d97
--- /dev/null
+++ b/nfc/java/android/nfc/RoutingTableProtocolEntry.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a protocol entry in current routing table.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public class RoutingTableProtocolEntry extends NfcRoutingTableEntry {
+ /**
+ * Protocol undetermined.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_UNDETERMINED = 0;
+ /**
+ * T1T Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_T1T = 1;
+ /**
+ * T2T Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_T2T = 2;
+ /**
+ * T3T Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_T3T = 3;
+ /**
+ * ISO-DEP Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_ISO_DEP = 4;
+ /**
+ * DEP Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_NFC_DEP = 5;
+ /**
+ * T5T Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_T5T = 6;
+ /**
+ * NDEF Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_NDEF = 7;
+ /**
+ * Unsupported Protocol
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int PROTOCOL_UNSUPPORTED = -1;
+
+ /**
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "PROTOCOL_" }, value = {
+ PROTOCOL_UNDETERMINED,
+ PROTOCOL_T1T,
+ PROTOCOL_T2T,
+ PROTOCOL_T3T,
+ PROTOCOL_ISO_DEP,
+ PROTOCOL_NFC_DEP,
+ PROTOCOL_T5T,
+ PROTOCOL_NDEF,
+ PROTOCOL_UNSUPPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtocolValue {}
+
+ private final @ProtocolValue int mValue;
+
+ /** @hide */
+ public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value) {
+ super(nfceeId);
+ this.mValue = value;
+ }
+
+ /**
+ * Gets Protocol value.
+ * @return Protocol defined in {@link ProtocolValue}
+ */
+ @ProtocolValue
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public int getProtocol() {
+ return mValue;
+ }
+
+ /** @hide */
+ @ProtocolValue
+ public static int protocolStringToInt(String protocolString) {
+ return switch (protocolString) {
+ case "PROTOCOL_T1T" -> PROTOCOL_T1T;
+ case "PROTOCOL_T2T" -> PROTOCOL_T2T;
+ case "PROTOCOL_T3T" -> PROTOCOL_T3T;
+ case "PROTOCOL_ISO_DEP" -> PROTOCOL_ISO_DEP;
+ case "PROTOCOL_NFC_DEP" -> PROTOCOL_NFC_DEP;
+ case "PROTOCOL_T5T" -> PROTOCOL_T5T;
+ case "PROTOCOL_NDEF" -> PROTOCOL_NDEF;
+ case "PROTOCOL_UNDETERMINED" -> PROTOCOL_UNDETERMINED;
+ default -> PROTOCOL_UNSUPPORTED;
+ };
+ }
+}
diff --git a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
new file mode 100644
index 000000000000..f87ad5f95195
--- /dev/null
+++ b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * Represents a system code entry in current routing table.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public class RoutingTableSystemCodeEntry extends NfcRoutingTableEntry {
+ private final byte[] mValue;
+
+ /** @hide */
+ public RoutingTableSystemCodeEntry(int nfceeId, byte[] value) {
+ super(nfceeId);
+ this.mValue = value;
+ }
+
+ /**
+ * Gets system code value.
+ * @return Byte array of system code
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @NonNull
+ public byte[] getSystemCode() {
+ return mValue;
+ }
+}
diff --git a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
new file mode 100644
index 000000000000..f51a5299be11
--- /dev/null
+++ b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a technology entry in current routing table.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry {
+ /**
+ * Technology-A
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int TECHNOLOGY_A = 0;
+ /**
+ * Technology-B
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int TECHNOLOGY_B = 1;
+ /**
+ * Technology-F
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int TECHNOLOGY_F = 2;
+ /**
+ * Technology-V
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int TECHNOLOGY_V = 3;
+ /**
+ * Unsupported technology
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public static final int TECHNOLOGY_UNSUPPORTED = -1;
+
+ /**
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "TECHNOLOGY_" }, value = {
+ TECHNOLOGY_A,
+ TECHNOLOGY_B,
+ TECHNOLOGY_F,
+ TECHNOLOGY_V,
+ TECHNOLOGY_UNSUPPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TechnologyValue{}
+
+ private final @TechnologyValue int mValue;
+
+ /** @hide */
+ public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value) {
+ super(nfceeId);
+ this.mValue = value;
+ }
+
+ /**
+ * Gets technology value.
+ * @return technology value
+ */
+ @TechnologyValue
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public int getTechnology() {
+ return mValue;
+ }
+
+ /** @hide */
+ @TechnologyValue
+ public static int techStringToInt(String tech) {
+ return switch (tech) {
+ case "TECHNOLOGY_A" -> TECHNOLOGY_A;
+ case "TECHNOLOGY_B" -> TECHNOLOGY_B;
+ case "TECHNOLOGY_F" -> TECHNOLOGY_F;
+ case "TECHNOLOGY_V" -> TECHNOLOGY_V;
+ default -> TECHNOLOGY_UNSUPPORTED;
+ };
+ }
+}
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index d75318f53fe3..9ff83fe77c9b 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -52,10 +52,12 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.TreeMap;
import java.util.regex.Pattern;
/**
@@ -204,7 +206,8 @@ public final class ApduServiceInfo implements Parcelable {
this(info, onHost, description, staticAidGroups, dynamicAidGroups,
requiresUnlock, requiresScreenOn, bannerResource, uid,
settingsActivityName, offHost, staticOffHost, isEnabled,
- new HashMap<String, Boolean>(), new HashMap<Pattern, Boolean>());
+ new HashMap<String, Boolean>(), new TreeMap<>(
+ Comparator.comparing(Pattern::toString)));
}
/**
@@ -340,7 +343,8 @@ public final class ApduServiceInfo implements Parcelable {
mStaticAidGroups = new HashMap<String, AidGroup>();
mDynamicAidGroups = new HashMap<String, AidGroup>();
mAutoTransact = new HashMap<String, Boolean>();
- mAutoTransactPatterns = new HashMap<Pattern, Boolean>();
+ mAutoTransactPatterns = new TreeMap<Pattern, Boolean>(
+ Comparator.comparing(Pattern::toString));
mOnHost = onHost;
final int depth = parser.getDepth();
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index d8f04c50b695..eb28c3b9c930 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -17,6 +17,7 @@
package android.nfc.cardemulation;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -33,15 +34,18 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.ComponentNameAndUser;
import android.nfc.Constants;
import android.nfc.Flags;
import android.nfc.INfcCardEmulation;
+import android.nfc.INfcEventListener;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.util.ArrayMap;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -50,6 +54,8 @@ import java.util.HashMap;
import java.util.HexFormat;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.regex.Pattern;
/**
@@ -1076,4 +1082,107 @@ public final class CardEmulation {
default -> throw new IllegalStateException("Unexpected value: " + route);
};
}
+
+ /** Listener for preferred service state changes. */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ public interface NfcEventListener {
+ /**
+ * This method is called when this package gains or loses preferred Nfc service status,
+ * either the Default Wallet Role holder (see {@link
+ * android.app.role.RoleManager#ROLE_WALLET}) or the preferred service of the foreground
+ * activity set with {@link #setPreferredService(Activity, ComponentName)}
+ *
+ * @param isPreferred true is this service has become the preferred Nfc service, false if it
+ * is no longer the preferred service
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ default void onPreferredServiceChanged(boolean isPreferred) {}
+
+ /**
+ * This method is called when observe mode has been enabled or disabled.
+ *
+ * @param isEnabled true if observe mode has been enabled, false if it has been disabled
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ default void onObserveModeStateChanged(boolean isEnabled) {}
+ }
+
+ private final ArrayMap<NfcEventListener, Executor> mNfcEventListeners = new ArrayMap<>();
+
+ final INfcEventListener mINfcEventListener =
+ new INfcEventListener.Stub() {
+ public void onPreferredServiceChanged(ComponentNameAndUser componentNameAndUser) {
+ if (!android.nfc.Flags.nfcEventListener()) {
+ return;
+ }
+ boolean isPreferred =
+ componentNameAndUser != null
+ && componentNameAndUser.getUserId()
+ == mContext.getUser().getIdentifier()
+ && componentNameAndUser.getComponentName() != null
+ && Objects.equals(
+ mContext.getPackageName(),
+ componentNameAndUser.getComponentName()
+ .getPackageName());
+ synchronized (mNfcEventListeners) {
+ mNfcEventListeners.forEach(
+ (listener, executor) -> {
+ executor.execute(
+ () -> listener.onPreferredServiceChanged(isPreferred));
+ });
+ }
+ }
+
+ public void onObserveModeStateChanged(boolean isEnabled) {
+ if (!android.nfc.Flags.nfcEventListener()) {
+ return;
+ }
+ synchronized (mNfcEventListeners) {
+ mNfcEventListeners.forEach(
+ (listener, executor) -> {
+ executor.execute(
+ () -> listener.onObserveModeStateChanged(isEnabled));
+ });
+ }
+ }
+ };
+
+ /**
+ * Register a listener for NFC Events.
+ *
+ * @param executor The Executor to run the call back with
+ * @param listener The listener to register
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ public void registerNfcEventListener(
+ @NonNull @CallbackExecutor Executor executor, @NonNull NfcEventListener listener) {
+ if (!android.nfc.Flags.nfcEventListener()) {
+ return;
+ }
+ synchronized (mNfcEventListeners) {
+ mNfcEventListeners.put(listener, executor);
+ if (mNfcEventListeners.size() == 1) {
+ callService(() -> sService.registerNfcEventListener(mINfcEventListener));
+ }
+ }
+ }
+
+ /**
+ * Unregister a preferred service listener that was previously registered with {@link
+ * #registerNfcEventListener(Executor, NfcEventListener)}
+ *
+ * @param listener The previously registered listener to unregister
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ public void unregisterNfcEventListener(@NonNull NfcEventListener listener) {
+ if (!android.nfc.Flags.nfcEventListener()) {
+ return;
+ }
+ synchronized (mNfcEventListeners) {
+ mNfcEventListeners.remove(listener);
+ if (mNfcEventListeners.size() == 0) {
+ callService(() -> sService.unregisterNfcEventListener(mINfcEventListener));
+ }
+ }
+ }
}
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index cd8e19c54565..4f601f0704b4 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -239,15 +239,6 @@ public abstract class HostApduService extends Service {
*/
public static final int MSG_POLLING_LOOP = 4;
- /**
- * @hide
- */
- public static final int MSG_OBSERVE_MODE_CHANGE = 5;
-
- /**
- * @hide
- */
- public static final int MSG_PREFERRED_SERVICE_CHANGED = 6;
/**
* @hide
@@ -343,16 +334,6 @@ public abstract class HostApduService extends Service {
processPollingFrames(pollingFrames);
}
break;
- case MSG_OBSERVE_MODE_CHANGE:
- if (android.nfc.Flags.nfcEventListener()) {
- onObserveModeStateChanged(msg.arg1 == 1);
- }
- break;
- case MSG_PREFERRED_SERVICE_CHANGED:
- if (android.nfc.Flags.nfcEventListener()) {
- onPreferredServiceChanged(msg.arg1 == 1);
- }
- break;
default:
super.handleMessage(msg);
}
@@ -462,25 +443,4 @@ public abstract class HostApduService extends Service {
*/
public abstract void onDeactivated(int reason);
-
- /**
- * This method is called when this service is the preferred Nfc service and
- * Observe mode has been enabled or disabled.
- *
- * @param isEnabled true if observe mode has been enabled, false if it has been disabled
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
- public void onObserveModeStateChanged(boolean isEnabled) {
-
- }
-
- /**
- * This method is called when this service gains or loses preferred Nfc service status.
- *
- * @param isPreferred true is this service has become the preferred Nfc service,
- * false if it is no longer the preferred service
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
- public void onPreferredServiceChanged(boolean isPreferred) {
- }
}
diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml
index bc725fee2d83..50b7312b2dbb 100644
--- a/packages/CarrierDefaultApp/res/values-bs/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml
@@ -7,9 +7,9 @@
<string name="no_data_notification_id" msgid="668400731803969521">"Prijenos podataka na mobilnoj mreži je deaktiviran"</string>
<string name="portal_notification_detail" msgid="2295729385924660881">"Dodirnite da posjetite %s web lokaciju"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"Obratite se pružaocu usluga %s"</string>
- <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nema veze za prijenos podataka na mobilnoj mreži"</string>
- <string name="no_mobile_data_connection" msgid="544980465184147010">"Dodajte plan prijenosa podataka ili rominga putem operatera %s"</string>
- <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status prijenosa podataka na mobilnoj mreži"</string>
+ <string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nema veze za prenos podataka na mobilnoj mreži"</string>
+ <string name="no_mobile_data_connection" msgid="544980465184147010">"Dodajte plan prenosa podataka ili rominga putem operatera %s"</string>
+ <string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Status prenosa podataka na mobilnoj mreži"</string>
<string name="action_bar_label" msgid="4290345990334377177">"Prijava na mobilnu mrežu"</string>
<string name="ssl_error_warning" msgid="3127935140338254180">"Mreža kojoj pokušavate pristupiti ima sigurnosnih problema."</string>
<string name="ssl_error_example" msgid="6188711843183058764">"Naprimjer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index abb02992fc40..86e891899268 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -40,7 +40,7 @@
<string name="summary_generic" msgid="1761976003668044801">"Aplikasi ini akan dapat menyinkronkan info, seperti nama penelepon, antara ponsel dan perangkat yang dipilih"</string>
<string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
<string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
- <string name="consent_cancel" msgid="5655005528379285841">"Batalkan"</string>
+ <string name="consent_cancel" msgid="5655005528379285841">"Batal"</string>
<string name="consent_back" msgid="2560683030046918882">"Kembali"</string>
<string name="permission_expand" msgid="893185038020887411">"Luaskan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
<string name="permission_collapse" msgid="3320833884220844084">"Ciutkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 5ac1e56e4331..9520a328d9a2 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -56,7 +56,7 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"जवळपासची डिव्हाइस"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट बदला"</string>
<string name="permission_storage" msgid="6831099350839392343">"फोटो आणि मीडिया"</string>
- <string name="permission_notifications" msgid="4099418516590632909">"सूचना"</string>
+ <string name="permission_notifications" msgid="4099418516590632909">"नोटिफिकेशन"</string>
<string name="permission_phone_summary" msgid="8246321093970051702">"फोन कॉल करणे आणि ते व्यवस्थापित करणे"</string>
<string name="permission_call_logs_summary" msgid="7545243592757693321">"फोन कॉल लॉग रीड अँड राइट करणे"</string>
<string name="permission_sms_summary" msgid="8499509535410068616">"एसएमएस पाठवणे आणि पाहणे"</string>
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index f1103e19c90e..992f581a8a70 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -896,7 +896,8 @@ public class RescueParty {
int systemUserId = UserHandle.SYSTEM.getIdentifier();
int[] userIds = { systemUserId };
try {
- for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) {
+ for (File file : FileUtils.listFilesOrEmpty(
+ Environment.getDataSystemDeviceProtectedDirectory())) {
try {
final int userId = Integer.parseInt(file.getName());
if (userId != systemUserId) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8277e573e7c2..311def80f248 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -499,8 +499,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
// Check if the package is listed among the system modules or is an
// APK inside an updatable APEX.
try {
- final PackageInfo pkg = mContext.getPackageManager()
- .getPackageInfo(packageName, 0 /* flags */);
+ PackageManager pm = mContext.getPackageManager();
+ final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
String apexPackageName = pkg.getApexPackageName();
if (apexPackageName != null) {
packageName = apexPackageName;
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 2acedd56e505..129e47f22f38 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -300,6 +300,31 @@ public class PackageWatchdog {
sPackageWatchdog = this;
}
+ /**
+ * Creating this temp constructor to match module constructor.
+ * Note: To be only used in tests.
+ * Creates a PackageWatchdog that allows injecting dependencies,
+ * except for connectivity module connector.
+ */
+ @VisibleForTesting
+ PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler,
+ Handler longTaskHandler, ExplicitHealthCheckController controller,
+ SystemClock clock) {
+ mContext = context;
+ mPolicyFile = policyFile;
+ mShortTaskHandler = shortTaskHandler;
+ mLongTaskHandler = longTaskHandler;
+ mHealthCheckController = controller;
+ mConnectivityModuleConnector = ConnectivityModuleConnector.getInstance();
+ mSystemClock = clock;
+ mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
+ mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
+
+ loadFromFile();
+ sPackageWatchdog = this;
+ }
+
/** Creates or gets singleton instance of PackageWatchdog. */
public static @NonNull PackageWatchdog getInstance(@NonNull Context context) {
synchronized (sPackageWatchdogLock) {
@@ -358,7 +383,7 @@ public class PackageWatchdog {
*
* <p>If monitoring a package supporting explicit health check, at the end of the monitoring
* duration if {@link #onHealthCheckPassed} was never called,
- * {@link PackageHealthObserver#execute} will be called as if the package failed.
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} will be called as if the package failed.
*
* <p>If {@code observer} is already monitoring a package in {@code packageNames},
* the monitoring window of that package will be reset to {@code durationMs} and the health
@@ -521,8 +546,8 @@ public class PackageWatchdog {
maybeExecute(currentObserverToNotify, versionedPackage,
failureReason, currentObserverImpact, mitigationCount);
} else {
- currentObserverToNotify.execute(versionedPackage,
- failureReason, mitigationCount);
+ currentObserverToNotify.onExecuteHealthCheckMitigation(
+ versionedPackage, failureReason, mitigationCount);
}
}
}
@@ -557,7 +582,8 @@ public class PackageWatchdog {
maybeExecute(currentObserverToNotify, failingPackage, failureReason,
currentObserverImpact, /*mitigationCount=*/ 1);
} else {
- currentObserverToNotify.execute(failingPackage, failureReason, 1);
+ currentObserverToNotify.onExecuteHealthCheckMitigation(failingPackage,
+ failureReason, 1);
}
}
}
@@ -571,7 +597,8 @@ public class PackageWatchdog {
synchronized (mLock) {
mLastMitigation = mSystemClock.uptimeMillis();
}
- currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount);
+ currentObserverToNotify.onExecuteHealthCheckMitigation(versionedPackage, failureReason,
+ mitigationCount);
}
}
@@ -633,12 +660,12 @@ public class PackageWatchdog {
currentObserverInternal.setBootMitigationCount(
currentObserverMitigationCount);
saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
- currentObserverToNotify.executeBootLoopMitigation(
+ currentObserverToNotify.onExecuteBootLoopMitigation(
currentObserverMitigationCount);
} else {
mBootThreshold.setMitigationCount(mitigationCount);
mBootThreshold.saveMitigationCountToMetadata();
- currentObserverToNotify.executeBootLoopMitigation(mitigationCount);
+ currentObserverToNotify.onExecuteBootLoopMitigation(mitigationCount);
}
}
}
@@ -724,7 +751,8 @@ public class PackageWatchdog {
return mPackagesExemptFromImpactLevelThreshold;
}
- /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}.
+ /** Possible severity values of the user impact of a
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation}.
* @hide
*/
@Retention(SOURCE)
@@ -772,7 +800,7 @@ public class PackageWatchdog {
*
*
* @return any one of {@link PackageHealthObserverImpact} to express the impact
- * to the user on {@link #execute}
+ * to the user on {@link #onExecuteHealthCheckMitigation}
*/
@PackageHealthObserverImpact int onHealthCheckFailed(
@Nullable VersionedPackage versionedPackage,
@@ -789,7 +817,7 @@ public class PackageWatchdog {
* (including this time).
* @return {@code true} if action was executed successfully, {@code false} otherwise
*/
- boolean execute(@Nullable VersionedPackage versionedPackage,
+ boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason, int mitigationCount);
@@ -809,7 +837,7 @@ public class PackageWatchdog {
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
*/
- default boolean executeBootLoopMitigation(int mitigationCount) {
+ default boolean onExecuteBootLoopMitigation(int mitigationCount) {
return false;
}
@@ -1090,7 +1118,7 @@ public class PackageWatchdog {
if (versionedPkg != null) {
Slog.i(TAG,
"Explicit health check failed for package " + versionedPkg);
- registeredObserver.execute(versionedPkg,
+ registeredObserver.onExecuteHealthCheckMitigation(versionedPkg,
PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
}
}
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
index feb5775e5aac..f757236613d1 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
@@ -859,7 +859,7 @@ public class RescueParty {
}
@Override
- public boolean execute(@Nullable VersionedPackage failedPackage,
+ public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
if (isDisabled()) {
return false;
@@ -927,7 +927,7 @@ public class RescueParty {
}
@Override
- public boolean executeBootLoopMitigation(int mitigationCount) {
+ public boolean onExecuteBootLoopMitigation(int mitigationCount) {
if (isDisabled()) {
return false;
}
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index d206c66ed09a..7445534e95bf 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -158,7 +158,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
// Note: For non-native crashes the rollback-all step has higher impact
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (getAvailableRollback(failedPackage) != null) {
- // Rollback is available, we may get a callback into #execute
+ // Rollback is available, we may get a callback into #onExecuteHealthCheckMitigation
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (anyRollbackAvailable) {
// If any rollbacks are available, we will commit them
@@ -175,7 +175,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
@Override
- public boolean execute(@Nullable VersionedPackage failedPackage,
+ public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason, int mitigationCount) {
Slog.i(TAG, "Executing remediation."
+ " failedPackage: "
@@ -229,7 +229,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
}
@Override
- public boolean executeBootLoopMitigation(int mitigationCount) {
+ public boolean onExecuteBootLoopMitigation(int mitigationCount) {
if (Flags.recoverabilityDetection()) {
List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index e1418678a9f8..b2dcb7fa4f53 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -61,7 +61,7 @@ android_library {
"SettingsLibUtils",
"SettingsLibZeroStatePreference",
"settingslib_media_flags_lib",
- "settingslib_flags_lib",
+ "aconfig_settingslib_flags_java_lib",
],
plugins: ["androidx.room_room-compiler-plugin"],
@@ -107,20 +107,6 @@ java_aconfig_library {
aconfig_declarations: "settingslib_media_flags",
}
-aconfig_declarations {
- name: "settingslib_flags",
- package: "com.android.settingslib.flags",
- container: "system",
- srcs: [
- "aconfig/settingslib.aconfig",
- ],
-}
-
-java_aconfig_library {
- name: "settingslib_flags_lib",
- aconfig_declarations: "settingslib_flags",
-}
-
soong_config_module_type {
name: "avatar_picker_java_defaults",
module_type: "java_defaults",
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
index 3b52df7e5fbb..c3f6eb71c2e7 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java
@@ -30,21 +30,28 @@ public class AppPreference extends Preference {
public AppPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setLayoutResource(R.layout.preference_app);
+ init(context);
}
public AppPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setLayoutResource(R.layout.preference_app);
+ init(context);
}
public AppPreference(Context context) {
super(context);
- setLayoutResource(R.layout.preference_app);
+ init(context);
}
public AppPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- setLayoutResource(R.layout.preference_app);
+ init(context);
+ }
+
+ private void init(Context context) {
+ int resId = SettingsThemeHelper.isExpressiveTheme(context)
+ ? com.android.settingslib.widget.theme.R.layout.settingslib_expressive_preference
+ : R.layout.preference_app;
+ setLayoutResource(resId);
}
}
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
index ecd500e1a160..3dcdfbaeb8b3 100644
--- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java
@@ -32,22 +32,29 @@ public class AppSwitchPreference extends SwitchPreferenceCompat {
public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setLayoutResource(R.layout.preference_app);
+ init(context);
}
public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setLayoutResource(R.layout.preference_app);
+ init(context);
}
public AppSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- setLayoutResource(R.layout.preference_app);
+ init(context);
}
public AppSwitchPreference(Context context) {
super(context);
- setLayoutResource(R.layout.preference_app);
+ init(context);
+ }
+
+ private void init(Context context) {
+ int resId = SettingsThemeHelper.isExpressiveTheme(context)
+ ? com.android.settingslib.widget.theme.R.layout.settingslib_expressive_preference
+ : R.layout.preference_app;
+ setLayoutResource(resId);
}
@Override
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index 979ff96be3f7..993555e78bea 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -37,7 +37,7 @@ import com.google.android.material.button.MaterialButton;
/**
* A preference handled a button
*/
-public class ButtonPreference extends Preference {
+public class ButtonPreference extends Preference implements GroupSectionDividerMixin {
enum ButtonStyle {
FILLED_NORMAL(0, 0, R.layout.settingslib_expressive_button_filled),
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
index 062e9b8ed032..42ffa67d5b1e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
@@ -17,6 +17,7 @@
package com.android.settingslib.collapsingtoolbar;
import android.os.Build;
+import android.view.ViewGroup;
import androidx.activity.ComponentActivity;
import androidx.activity.EdgeToEdge;
@@ -53,6 +54,8 @@ public class EdgeToEdgeUtils {
.getInsets(WindowInsetsCompat.Type.statusBars()).top;
// Apply the insets paddings to the view.
v.setPadding(insets.left, statusBarHeight, insets.right, insets.bottom);
+ ((ViewGroup)v).setClipToPadding(false);
+ ((ViewGroup)v).setClipChildren(false);
// Return CONSUMED if you don't want the window insets to keep being
// passed down to descendant views.
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 843d2aadf333..cd03dd7ca1b3 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -202,6 +202,12 @@ open class KeyedDataObservable<K> : KeyedObservable<K> {
entry.value.execute { observer.onKeyChanged(key, reason) }
}
}
+
+ fun hasAnyObserver(): Boolean {
+ synchronized(observers) { if (observers.isNotEmpty()) return true }
+ synchronized(keyedObservers) { if (keyedObservers.isNotEmpty()) return true }
+ return false
+ }
}
/** [KeyedObservable] with no-op implementations for all interfaces. */
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index d60290ed91ef..37f47543c536 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -43,7 +43,7 @@ import java.net.URISyntaxException;
* A custom preference acting as "footer" of a page. It has a field for icon and text. It is added
* to screen as the last preference.
*/
-public class FooterPreference extends Preference {
+public class FooterPreference extends Preference implements GroupSectionDividerMixin {
private static final String TAG = "FooterPreference";
public static final String KEY_FOOTER = "footer_preference";
diff --git a/packages/SettingsLib/Graph/graph.proto b/packages/SettingsLib/Graph/graph.proto
index cbe602e00406..6b93cd73164f 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -77,6 +77,8 @@ message PreferenceProto {
// Intent to show and locate the preference (might have highlight animation on
// the preference).
optional IntentProto launch_intent = 14;
+ // Descriptor of the preference value.
+ optional PreferenceValueDescriptorProto value_descriptor = 15;
// Target of an Intent
message ActionTarget {
@@ -103,9 +105,28 @@ message TextProto {
message PreferenceValueProto {
oneof value {
bool boolean_value = 1;
+ int32 int_value = 2;
}
}
+// Proto of preference value descriptor.
+message PreferenceValueDescriptorProto {
+ oneof type {
+ bool boolean_type = 1;
+ RangeValueProto range_value = 2;
+ }
+}
+
+// Proto of preference value that is between a range.
+message RangeValueProto {
+ // The lower bound (inclusive) of the range.
+ optional int32 min = 1;
+ // The upper bound (inclusive) of the range.
+ optional int32 max = 2;
+ // The increment step within the range. 0 means unset, which implies step size is 1.
+ optional int32 step = 3;
+}
+
// Proto of android.content.Intent
message IntentProto {
// The action of the Intent.
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index fdffe5de3f53..5ceee6d09978 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -65,6 +65,7 @@ constructor(
val visitedScreens: Set<String> = setOf(),
val locale: Locale? = null,
val includeValue: Boolean = true,
+ val includeValueDescriptor: Boolean = true,
)
object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 9cb872a797db..2256bb38dd2c 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -49,6 +49,7 @@ import com.android.settingslib.metadata.PreferenceScreenMetadata
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.PreferenceTitleProvider
+import com.android.settingslib.metadata.RangeValue
import com.android.settingslib.preference.PreferenceScreenFactory
import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale
@@ -66,6 +67,7 @@ private constructor(private val context: Context, private val request: GetPrefer
private val builder by lazy { PreferenceGraphProto.newBuilder() }
private val visitedScreens = mutableSetOf<String>().apply { addAll(request.visitedScreens) }
private val includeValue = request.includeValue
+ private val includeValueDescriptor = request.includeValueDescriptor
private suspend fun init() {
for (key in request.screenKeys) {
@@ -270,7 +272,8 @@ private constructor(private val context: Context, private val request: GetPrefer
summary = textProto { string = it.toString() }
}
}
- if (metadata.icon != 0) icon = metadata.icon
+ val metadataIcon = metadata.getPreferenceIcon(context)
+ if (metadataIcon != 0) icon = metadataIcon
if (metadata.keywords != 0) keywords = metadata.keywords
val preferenceExtras = metadata.extras(context)
preferenceExtras?.let { extras = it.toProto() }
@@ -283,14 +286,37 @@ private constructor(private val context: Context, private val request: GetPrefer
restricted = metadata.isRestricted(context)
}
persistent = metadata.isPersistent(context)
- if (
- includeValue &&
- persistent &&
- metadata is BooleanValue &&
- metadata is PersistentPreference<*>
- ) {
- metadata.storage(context).getValue(metadata.key, Boolean::class.javaObjectType)?.let {
- value = preferenceValueProto { booleanValue = it }
+ if (persistent) {
+ if (includeValue && metadata is PersistentPreference<*>) {
+ value = preferenceValueProto {
+ when (metadata) {
+ is BooleanValue ->
+ metadata
+ .storage(context)
+ .getValue(metadata.key, Boolean::class.javaObjectType)
+ ?.let { booleanValue = it }
+ is RangeValue -> {
+ metadata
+ .storage(context)
+ .getValue(metadata.key, Int::class.javaObjectType)
+ ?.let { intValue = it }
+ }
+ else -> {}
+ }
+ }
+ }
+ if (includeValueDescriptor) {
+ valueDescriptor = preferenceValueDescriptorProto {
+ when (metadata) {
+ is BooleanValue -> booleanType = true
+ is RangeValue -> rangeValue = rangeValueProto {
+ min = metadata.getMinValue(context)
+ max = metadata.getMaxValue(context)
+ step = metadata.getIncrementStep(context)
+ }
+ else -> {}
+ }
+ }
}
}
if (metadata is PreferenceScreenMetadata) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index d8db1bb776f5..6e4db1d90484 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -27,8 +27,10 @@ import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.BooleanValue
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
+import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.PreferenceScreenRegistry
+import com.android.settingslib.metadata.RangeValue
import com.android.settingslib.metadata.ReadWritePermit
/** Request to set preference value. */
@@ -114,27 +116,39 @@ class PreferenceSetterApiHandler(override val id: Int) : ApiHandler<PreferenceSe
if (metadata is PreferenceAvailabilityProvider && !metadata.isAvailable(application)) {
return PreferenceSetterResult.UNAVAILABLE
}
+
+ fun <T> PreferenceMetadata.checkWritePermit(value: T): Int {
+ @Suppress("UNCHECKED_CAST") val preference = (this as PersistentPreference<T>)
+ return when (preference.getWritePermit(application, value, myUid, callingUid)) {
+ ReadWritePermit.ALLOW -> PreferenceSetterResult.OK
+ ReadWritePermit.DISALLOW -> PreferenceSetterResult.DISALLOW
+ ReadWritePermit.REQUIRE_APP_PERMISSION ->
+ PreferenceSetterResult.REQUIRE_APP_PERMISSION
+ ReadWritePermit.REQUIRE_USER_AGREEMENT ->
+ PreferenceSetterResult.REQUIRE_USER_AGREEMENT
+ else -> PreferenceSetterResult.INTERNAL_ERROR
+ }
+ }
+
val storage = metadata.storage(application)
val value = request.value
try {
if (value.hasBooleanValue()) {
if (metadata !is BooleanValue) return PreferenceSetterResult.INVALID_REQUEST
val booleanValue = value.booleanValue
- @Suppress("UNCHECKED_CAST")
- val booleanPreference = metadata as PersistentPreference<Boolean>
- when (
- booleanPreference.getWritePermit(application, booleanValue, myUid, callingUid)
- ) {
- ReadWritePermit.ALLOW -> {}
- ReadWritePermit.DISALLOW -> return PreferenceSetterResult.DISALLOW
- ReadWritePermit.REQUIRE_APP_PERMISSION ->
- return PreferenceSetterResult.REQUIRE_APP_PERMISSION
- ReadWritePermit.REQUIRE_USER_AGREEMENT ->
- return PreferenceSetterResult.REQUIRE_USER_AGREEMENT
- else -> return PreferenceSetterResult.INTERNAL_ERROR
- }
+ val resultCode = metadata.checkWritePermit(booleanValue)
+ if (resultCode != PreferenceSetterResult.OK) return resultCode
storage.setValue(key, Boolean::class.javaObjectType, booleanValue)
return PreferenceSetterResult.OK
+ } else if (value.hasIntValue()) {
+ val intValue = value.intValue
+ val resultCode = metadata.checkWritePermit(intValue)
+ if (resultCode != PreferenceSetterResult.OK) return resultCode
+ if (metadata is RangeValue && !metadata.isValidValue(application, intValue)) {
+ return PreferenceSetterResult.INVALID_REQUEST
+ }
+ storage.setValue(key, Int::class.javaObjectType, intValue)
+ return PreferenceSetterResult.OK
}
} catch (e: Exception) {
return PreferenceSetterResult.INTERNAL_ERROR
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
index d7dae7771acd..dee32d9ed80e 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
@@ -24,7 +24,9 @@ import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceProto.ActionTarget
import com.android.settingslib.graph.proto.PreferenceScreenProto
+import com.android.settingslib.graph.proto.PreferenceValueDescriptorProto
import com.android.settingslib.graph.proto.PreferenceValueProto
+import com.android.settingslib.graph.proto.RangeValueProto
import com.android.settingslib.graph.proto.TextProto
/** Returns root or null. */
@@ -89,6 +91,16 @@ inline fun actionTargetProto(init: ActionTarget.Builder.() -> Unit) =
inline fun preferenceValueProto(init: PreferenceValueProto.Builder.() -> Unit) =
PreferenceValueProto.newBuilder().also(init).build()
+/** Kotlin DSL-style builder for [PreferenceValueDescriptorProto]. */
+@JvmSynthetic
+inline fun preferenceValueDescriptorProto(init: PreferenceValueDescriptorProto.Builder.() -> Unit) =
+ PreferenceValueDescriptorProto.newBuilder().also(init).build()
+
+/** Kotlin DSL-style builder for [RangeValueProto]. */
+@JvmSynthetic
+inline fun rangeValueProto(init: RangeValueProto.Builder.() -> Unit) =
+ RangeValueProto.newBuilder().also(init).build()
+
/** Kotlin DSL-style builder for [TextProto]. */
@JvmSynthetic
inline fun textProto(init: TextProto.Builder.() -> Unit) = TextProto.newBuilder().also(init).build()
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml
index 759da1d0b021..150020cb17c5 100644
--- a/packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rGB/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="help_feedback_label" msgid="7106780063063027882">"Help &amp; feedback"</string>
+ <string name="help_feedback_label" msgid="7106780063063027882">"Help and feedback"</string>
</resources>
diff --git a/packages/SettingsLib/IntroPreference/Android.bp b/packages/SettingsLib/IntroPreference/Android.bp
index 155db186c702..8f9fb7a2e408 100644
--- a/packages/SettingsLib/IntroPreference/Android.bp
+++ b/packages/SettingsLib/IntroPreference/Android.bp
@@ -29,5 +29,6 @@ android_library {
min_sdk_version: "21",
apex_available: [
"//apex_available:platform",
+ "com.android.healthfitness",
],
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index 94d373bed0a5..a2b826a50e58 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -136,6 +136,18 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
for (it in children) action(it)
}
+ /** Traversals preference hierarchy recursively and applies given action. */
+ fun forEachRecursively(action: (PreferenceHierarchyNode) -> Unit) {
+ action(this)
+ for (child in children) {
+ if (child is PreferenceHierarchy) {
+ child.forEachRecursively(action)
+ } else {
+ action(child)
+ }
+ }
+ }
+
/** Traversals preference hierarchy and applies given action. */
suspend fun forEachAsync(action: suspend (PreferenceHierarchyNode) -> Unit) {
for (it in children) action(it)
@@ -154,21 +166,6 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
}
return null
}
-
- /** Returns all the [PreferenceHierarchyNode]s appear in the hierarchy. */
- fun getAllPreferences(): List<PreferenceHierarchyNode> =
- mutableListOf<PreferenceHierarchyNode>().also { getAllPreferences(it) }
-
- private fun getAllPreferences(result: MutableList<PreferenceHierarchyNode>) {
- result.add(this)
- for (child in children) {
- if (child is PreferenceHierarchy) {
- child.getAllPreferences(result)
- } else {
- result.add(child)
- }
- }
- }
}
/**
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 72cf40387a25..6b7be91c1903 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -108,6 +108,9 @@ interface PreferenceBinding {
}
}
+/** Interface indicates that a virtual [Preference] should be created for binding. */
+interface PreferenceBindingPlaceholder
+
/** Abstract preference screen to provide preference hierarchy and binding factory. */
interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenProvider {
@@ -115,7 +118,7 @@ interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenPr
fun isFlagEnabled(context: Context): Boolean = true
val preferenceBindingFactory: PreferenceBindingFactory
- get() = DefaultPreferenceBindingFactory
+ get() = PreferenceBindingFactory.defaultFactory
override fun createPreferenceScreen(factory: PreferenceScreenFactory) =
factory.getOrCreatePreferenceScreen().apply {
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
index 87c289f48ff0..51b9aac029a5 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -45,10 +45,15 @@ interface PreferenceBindingFactory {
/** Returns the [PreferenceBinding] associated with the [PreferenceMetadata]. */
fun getPreferenceBinding(metadata: PreferenceMetadata): PreferenceBinding?
+
+ companion object {
+ /** Default preference binding factory. */
+ @JvmStatic var defaultFactory: PreferenceBindingFactory = DefaultPreferenceBindingFactory()
+ }
}
/** Default [PreferenceBindingFactory]. */
-object DefaultPreferenceBindingFactory : PreferenceBindingFactory {
+open class DefaultPreferenceBindingFactory : PreferenceBindingFactory {
override fun getPreferenceBinding(metadata: PreferenceMetadata) =
metadata as? PreferenceBinding
@@ -66,5 +71,6 @@ class KeyedPreferenceBindingFactory(private val bindings: Map<String, Preference
PreferenceBindingFactory {
override fun getPreferenceBinding(metadata: PreferenceMetadata) =
- bindings[metadata.key] ?: DefaultPreferenceBindingFactory.getPreferenceBinding(metadata)
+ bindings[metadata.key]
+ ?: PreferenceBindingFactory.defaultFactory.getPreferenceBinding(metadata)
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index d40a6f6c55f4..762f5eaf6325 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -23,7 +23,6 @@ import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
-import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
import com.android.settingslib.metadata.PreferenceTitleProvider
@@ -71,10 +70,10 @@ interface TwoStatePreferenceBinding : PreferenceBinding {
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
super.bind(preference, metadata)
- (metadata as? PersistentPreference<*>)
- ?.storage(preference.context)
- ?.getValue(metadata.key, Boolean::class.javaObjectType)
- ?.let { (preference as TwoStatePreference).isChecked = it }
+ (preference as TwoStatePreference).apply {
+ // "false" is kind of placeholder, metadata datastore should provide the default value
+ isChecked = preferenceDataStore!!.getBoolean(key, false)
+ }
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index 5f4b88f81de6..991d5b7791e9 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -21,18 +21,18 @@ import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.annotation.XmlRes
-import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.preference.PreferenceScreenBindingHelper.Companion.bindRecursively
+import com.android.settingslib.widget.SettingsBasePreferenceFragment
/** Fragment to display a preference screen. */
open class PreferenceFragment :
- PreferenceFragmentCompat(), PreferenceScreenProvider, PreferenceScreenBindingKeyProvider {
+ SettingsBasePreferenceFragment(), PreferenceScreenProvider, PreferenceScreenBindingKeyProvider {
- private var preferenceScreenBindingHelper: PreferenceScreenBindingHelper? = null
+ protected var preferenceScreenBindingHelper: PreferenceScreenBindingHelper? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceScreen = createPreferenceScreen()
@@ -129,7 +129,9 @@ open class PreferenceFragment :
}
protected fun getPreferenceKeysInHierarchy(): Set<String> =
- preferenceScreenBindingHelper?.getPreferences()?.map { it.metadata.key }?.toSet() ?: setOf()
+ preferenceScreenBindingHelper?.let {
+ mutableSetOf<String>().apply { it.forEachRecursively { add(it.metadata.key) } }
+ } ?: setOf()
companion object {
private const val TAG = "PreferenceFragment"
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
index c26ba1814ac1..657f69a738f2 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt
@@ -40,11 +40,11 @@ fun PreferenceGroup.inflatePreferenceHierarchy(
addPreference(preferenceGroup)
preferenceGroup.inflatePreferenceHierarchy(preferenceBindingFactory, it)
} else {
- preferenceBindingFactory.bind(widget, it, preferenceBinding)
(metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
widget.preferenceDataStore =
storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
}
+ preferenceBindingFactory.bind(widget, it, preferenceBinding)
// MUST add preference after binding for persistent preference to get initial value
// (preference key is set within bind method)
addPreference(widget)
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index d75f3e84274d..cfe6089169d3 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -143,7 +143,8 @@ class PreferenceScreenBindingHelper(
}
}
- fun getPreferences() = preferenceHierarchy.getAllPreferences()
+ fun forEachRecursively(action: (PreferenceHierarchyNode) -> Unit) =
+ preferenceHierarchy.forEachRecursively(action)
fun onCreate() {
for (preference in lifecycleAwarePreferences) {
@@ -191,11 +192,11 @@ class PreferenceScreenBindingHelper(
companion object {
/** Preference value is changed. */
- private const val CHANGE_REASON_VALUE = 0
+ const val CHANGE_REASON_VALUE = 0
/** Preference state (title/summary, enable state, etc.) is changed. */
- private const val CHANGE_REASON_STATE = 1
+ const val CHANGE_REASON_STATE = 1
/** Dependent preference state is changed. */
- private const val CHANGE_REASON_DEPENDENT = 2
+ const val CHANGE_REASON_DEPENDENT = 2
/** Updates preference screen that has incomplete hierarchy. */
@JvmStatic
@@ -217,34 +218,47 @@ class PreferenceScreenBindingHelper(
preferenceScreen: PreferenceScreen,
preferenceBindingFactory: PreferenceBindingFactory,
preferenceHierarchy: PreferenceHierarchy,
- ) =
- preferenceScreen.bindRecursively(
- preferenceBindingFactory,
- preferenceHierarchy.getAllPreferences().associateBy { it.metadata.key },
- )
-
- private fun PreferenceGroup.bindRecursively(
- preferenceBindingFactory: PreferenceBindingFactory,
- preferences: Map<String, PreferenceHierarchyNode>,
- storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
) {
- preferences[key]?.let { preferenceBindingFactory.bind(this, it) }
- val count = preferenceCount
- for (index in 0 until count) {
- val preference = getPreference(index)
- if (preference is PreferenceGroup) {
- preference.bindRecursively(preferenceBindingFactory, preferences, storages)
- } else {
- preferences[preference.key]?.let {
- preferenceBindingFactory.bind(preference, it)
- val metadata = it.metadata
- (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
- preference.preferenceDataStore =
- storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+ val preferences = mutableMapOf<String, PreferenceHierarchyNode>()
+ preferenceHierarchy.forEachRecursively {
+ val metadata = it.metadata
+ preferences[metadata.key] = it
+ }
+ val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>()
+
+ fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) {
+ (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
+ preferenceDataStore =
+ storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+ }
+ }
+
+ fun PreferenceGroup.bindRecursively() {
+ preferences.remove(key)?.let { preferenceBindingFactory.bind(this, it) }
+ val count = preferenceCount
+ for (index in 0 until count) {
+ val preference = getPreference(index)
+ if (preference is PreferenceGroup) {
+ preference.bindRecursively()
+ } else {
+ preferences.remove(preference.key)?.let {
+ preference.setPreferenceDataStore(it.metadata)
+ preferenceBindingFactory.bind(preference, it)
}
}
}
}
+
+ preferenceScreen.bindRecursively()
+ for (node in preferences.values) {
+ val metadata = node.metadata
+ val binding = preferenceBindingFactory.getPreferenceBinding(metadata)
+ if (binding !is PreferenceBindingPlaceholder) continue
+ val preference = binding.createWidget(preferenceScreen.context)
+ preference.setPreferenceDataStore(metadata)
+ preferenceBindingFactory.bind(preference, node, binding)
+ preferenceScreen.addPreference(preference)
+ }
}
}
}
diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
new file mode 100644
index 000000000000..00bad5203f07
--- /dev/null
+++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.preference
+
+import android.content.Context
+import androidx.annotation.VisibleForTesting
+import androidx.preference.Preference
+import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceMetadata
+
+/** Creates [Preference] widget and binds with metadata. */
+@VisibleForTesting
+fun <P : Preference> PreferenceMetadata.createAndBindWidget(context: Context): P {
+ val binding = PreferenceBindingFactory.defaultFactory.getPreferenceBinding(this)!!
+ return (binding.createWidget(context) as P).also {
+ if (this is PersistentPreference<*>) {
+ storage(context)?.let { keyValueStore ->
+ it.preferenceDataStore = PreferenceDataStoreAdapter(keyValueStore)
+ }
+ }
+ binding.bind(it, this)
+ }
+}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index 155ee831ae52..78e27fe5ad04 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -29,6 +29,7 @@ android_library {
"//apex_available:platform",
"com.android.permission",
"com.android.mediaprovider",
+ "com.android.healthfitness",
],
}
@@ -51,5 +52,6 @@ java_aconfig_library {
"//apex_available:platform",
"com.android.permission",
"com.android.mediaprovider",
+ "com.android.healthfitness",
],
}
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml
new file mode 100644
index 000000000000..c0c08699cc2a
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:colorControlHighlight">
+ <item
+ android:bottom="16dp"
+ android:end="?android:attr/listPreferredItemPaddingEnd"
+ android:start="?android:attr/listPreferredItemPaddingStart"
+ android:top="2dp">
+ <shape
+ android:shape="rectangle"
+ android:tint="?android:attr/colorAccent">
+ <corners
+ android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
+ android:bottomRightRadius="@dimen/settingslib_preference_corner_radius"
+ android:topLeftRadius="4dp"
+ android:topRightRadius="4dp" />
+ <padding android:bottom="16dp" />
+ <solid android:color="#42000000" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml
new file mode 100644
index 000000000000..8099d9b3d7f7
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:colorControlHighlight">
+ <item
+ android:end="?android:attr/listPreferredItemPaddingEnd"
+ android:start="?android:attr/listPreferredItemPaddingStart"
+ android:top="2dp">
+ <shape
+ android:shape="rectangle"
+ android:tint="?android:attr/colorAccent">
+ <corners android:radius="4dp" />
+ <solid android:color="#42000000" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml
new file mode 100644
index 000000000000..a119a4ae083f
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:colorControlHighlight">
+ <item
+ android:bottom="16dp"
+ android:end="?android:attr/listPreferredItemPaddingEnd"
+ android:start="?android:attr/listPreferredItemPaddingStart"
+ android:top="2dp">
+ <shape
+ android:shape="rectangle"
+ android:tint="?android:attr/colorAccent">
+ <corners android:radius="@dimen/settingslib_preference_corner_radius" />
+ <padding android:bottom="16dp" />
+ <solid android:color="#42000000" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml
new file mode 100644
index 000000000000..052eb01cab8d
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:colorControlHighlight">
+ <item
+ android:color="?android:attr/colorAccent"
+ android:end="?android:attr/listPreferredItemPaddingEnd"
+ android:start="?android:attr/listPreferredItemPaddingStart"
+ android:top="2dp">
+ <shape
+ android:shape="rectangle"
+ android:tint="?android:attr/colorAccent">
+ <corners
+ android:bottomLeftRadius="4dp"
+ android:bottomRightRadius="4dp"
+ android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
+ android:topRightRadius="@dimen/settingslib_preference_corner_radius" />
+ <solid android:color="#42000000" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
index ccdf37d452b0..0cd0b3cb14f1 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
@@ -22,7 +22,7 @@
android:minWidth="@dimen/settingslib_expressive_space_medium3"
android:minHeight="@dimen/settingslib_expressive_space_medium3"
android:gravity="center"
- android:layout_marginEnd="-8dp"
+ android:layout_marginEnd="-4dp"
android:filterTouchesWhenObscured="false">
<androidx.preference.internal.PreferenceImageView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
index cc42dab6737e..944bef6c9e09 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
@@ -21,6 +21,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingVertical="@dimen/settingslib_expressive_space_small1"
+ android:paddingHorizontal="@dimen/settingslib_expressive_space_small1"
android:filterTouchesWhenObscured="false">
<TextView
@@ -40,7 +41,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
- android:layout_alignLeft="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_gravity="start"
android:textAlignment="viewStart"
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml
index 3c69027c2080..cec8e45e2bfb 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml
@@ -36,33 +36,34 @@
<style name="SettingsLibPreference.SwitchPreference" parent="SettingsSwitchPreference.SettingsLib"/>
<style name="SettingsLibPreference.Expressive">
- <item name="android:layout">@layout/settingslib_expressive_preference</item>
+ <item name="layout">@layout/settingslib_expressive_preference</item>
</style>
<style name="SettingsLibPreference.Category.Expressive">
</style>
<style name="SettingsLibPreference.CheckBoxPreference.Expressive">
- <item name="android:layout">@layout/settingslib_expressive_preference</item>
+ <item name="layout">@layout/settingslib_expressive_preference</item>
</style>
<style name="SettingsLibPreference.SwitchPreferenceCompat.Expressive">
- <item name="android:layout">@layout/settingslib_expressive_preference</item>
+ <item name="layout">@layout/settingslib_expressive_preference</item>
<item name="android:widgetLayout">@layout/settingslib_expressive_preference_switch</item>
</style>
<style name="SettingsLibPreference.SeekBarPreference.Expressive"/>
<style name="SettingsLibPreference.PreferenceScreen.Expressive">
- <item name="android:layout">@layout/settingslib_expressive_preference</item>
+ <item name="layout">@layout/settingslib_expressive_preference</item>
</style>
<style name="SettingsLibPreference.DialogPreference.Expressive">
+ <item name="layout">@layout/settingslib_expressive_preference</item>
</style>
<style name="SettingsLibPreference.DialogPreference.EditTextPreference.Expressive">
- <item name="android:layout">@layout/settingslib_expressive_preference</item>
- <item name="android:dialogLayout">@layout/settingslib_preference_dialog_edittext</item>
+ <item name="layout">@layout/settingslib_expressive_preference</item>
+ <item name="dialogLayout">@layout/settingslib_preference_dialog_edittext</item>
</style>
<style name="SettingsLibPreference.DropDown.Expressive">
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
index 535d80f609fb..265c065e924e 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -21,7 +21,7 @@ import androidx.preference.PreferenceScreen
import androidx.recyclerview.widget.RecyclerView
/** Base class for Settings to use PreferenceFragmentCompat */
-open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
+abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> {
if (SettingsThemeHelper.isExpressiveTheme(requireContext()))
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index 98b7f7664b1b..6a0632073de9 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -16,8 +16,10 @@
package com.android.settingslib.widget
+import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
+import android.util.TypedValue
import androidx.annotation.DrawableRes
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
@@ -27,12 +29,12 @@ import androidx.preference.PreferenceViewHolder
import com.android.settingslib.widget.theme.R
/**
- * A custom adapter for displaying settings preferences in a list, handling rounded corners
- * for preference items within a group.
+ * A custom adapter for displaying settings preferences in a list, handling rounded corners for
+ * preference items within a group.
*/
-open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
- preferenceGroup: PreferenceGroup
-) : PreferenceGroupAdapter(preferenceGroup) {
+@SuppressLint("RestrictedApi")
+open class SettingsPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
+ PreferenceGroupAdapter(preferenceGroup) {
private val mPreferenceGroup = preferenceGroup
private var mRoundCornerMappingList: ArrayList<Int> = ArrayList()
@@ -41,6 +43,7 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
private var mGroupPaddingStart = 0
private var mNormalPaddingEnd = 0
private var mGroupPaddingEnd = 0
+ @DrawableRes private var mLegacyBackgroundRes: Int
private val mHandler = Handler(Looper.getMainLooper())
@@ -54,9 +57,17 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
mNormalPaddingEnd =
context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
mGroupPaddingEnd = mNormalPaddingEnd * 2
+ val outValue = TypedValue()
+ context.theme.resolveAttribute(
+ android.R.attr.selectableItemBackground,
+ outValue,
+ true, /* resolveRefs */
+ )
+ mLegacyBackgroundRes = outValue.resourceId
updatePreferences()
}
+ @SuppressLint("RestrictedApi")
override fun onPreferenceHierarchyChange(preference: Preference) {
super.onPreferenceHierarchyChange(preference)
@@ -65,6 +76,7 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
mHandler.post(syncRunnable)
}
+ @SuppressLint("RestrictedApi")
override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
updateBackground(holder, position)
@@ -79,6 +91,7 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
}
}
+ @SuppressLint("RestrictedApi")
private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) {
cornerStyles.clear()
cornerStyles.addAll(MutableList(itemCount) { 0 })
@@ -151,20 +164,38 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
}
}
- /** handle roundCorner background */
+ /** handle roundCorner background */
private fun updateBackground(holder: PreferenceViewHolder, position: Int) {
- @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/)
+ val context = holder.itemView.context
+ @DrawableRes
+ val backgroundRes =
+ when (SettingsThemeHelper.isExpressiveTheme(context)) {
+ true -> getRoundCornerDrawableRes(position, isSelected = false)
+ else -> mLegacyBackgroundRes
+ }
val v = holder.itemView
- val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
- val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
-
- v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+ // Update padding
+ if (SettingsThemeHelper.isExpressiveTheme(context)) {
+ val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
+ val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+ v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+ }
+ // Update background
v.setBackgroundResource(backgroundRes)
}
@DrawableRes
protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
+ return getRoundCornerDrawableRes(position, isSelected, false)
+ }
+
+ @DrawableRes
+ protected fun getRoundCornerDrawableRes(
+ position: Int,
+ isSelected: Boolean,
+ isHighlighted: Boolean,
+ ): Int {
val cornerType = mRoundCornerMappingList[position]
if ((cornerType and ROUND_CORNER_CENTER) == 0) {
@@ -175,24 +206,28 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
(cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> {
// the first
if (isSelected) R.drawable.settingslib_round_background_top_selected
+ else if (isHighlighted) R.drawable.settingslib_round_background_top_highlighted
else R.drawable.settingslib_round_background_top
}
(cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> {
// the last
if (isSelected) R.drawable.settingslib_round_background_bottom_selected
+ else if (isHighlighted) R.drawable.settingslib_round_background_bottom_highlighted
else R.drawable.settingslib_round_background_bottom
}
(cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> {
// the only one preference
if (isSelected) R.drawable.settingslib_round_background_selected
+ else if (isHighlighted) R.drawable.settingslib_round_background_highlighted
else R.drawable.settingslib_round_background
}
else -> {
// in the center
if (isSelected) R.drawable.settingslib_round_background_center_selected
+ else if (isHighlighted) R.drawable.settingslib_round_background_center_highlighted
else R.drawable.settingslib_round_background_center
}
}
@@ -203,4 +238,4 @@ open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
private const val ROUND_CORNER_TOP: Int = 1 shl 1
private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2
}
-} \ No newline at end of file
+}
diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
index c1578ef69635..1f8cfb5e432e 100644
--- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
+++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
@@ -34,7 +34,7 @@ class StatusBannerPreference @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
enum class BannerStatus {
GENERIC,
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
index 5be56f8ebc86..9764e64b8509 100644
--- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
+++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
@@ -31,7 +31,7 @@ open class TopIntroPreference @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
private var isCollapsable: Boolean = false
private var minLines: Int = 2
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 81a2e6aeccc1..bf419cc46aeb 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -74,16 +74,6 @@ flag {
}
flag {
- name: "volume_panel_broadcast_fix"
- namespace: "systemui"
- description: "Make the volume panel's repository listen for the new ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED broadcast instead of ACTION_NOTIFICATION_POLICY_CHANGED"
- bug: "347707024"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "volume_dialog_audio_sharing_fix"
namespace: "cross_device_experiences"
description: "Gates whether to show separate volume bars during audio sharing"
@@ -111,6 +101,14 @@ flag {
}
flag {
+ name: "write_system_preference_permission_enabled"
+ is_fixed_read_only: true
+ namespace: "android_settings"
+ description: "Enable WRITE_SYSTEM_PREFERENCE permission and appop"
+ bug: "375193223"
+}
+
+flag {
name: "asha_profile_access_profile_enabled_true"
namespace: "accessibility"
description: "Changes the return value of HearingAidProfile.accessProfileEnabled() to true"
@@ -166,3 +164,10 @@ flag {
description: "Enable the ambient volume control in device details and hearing devices dialog."
bug: "357878944"
}
+
+flag {
+ name: "settings_preference_write_consent_enabled"
+ namespace: "android_settings"
+ description: "Enable the user consent prompt before writing sensitive preferences via service"
+ bug: "378552675"
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 79c379996d8b..6f6a357a4a95 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Gekoppelde toestel"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan nie op hierdie toestel speel nie"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Gradeer rekening op om oor te skakel"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan nie aflaaie hier speel nie"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 5cde078af811..4d7731c5dd45 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"የተገናኘ መሣሪያ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"በዚህ መሣሪያ ላይ ማጫወት አልተቻለም"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ለመቀየር መለያ ያልቁ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ውርዶችን እዚህ ማጫወት አይቻልም"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 09bfd83f7121..8b8131342b7d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -125,7 +125,7 @@
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"الإعدادات الصوتية للوسائط"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"المكالمات الهاتفية"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز إدخال بيانات"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز إدخال"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"الوصول إلى الإنترنت"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"السماح بالوصول إلى جهات الاتصال وسجلّ المكالمات"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"سيتم استخدام المعلومات لإرسال إشعارات المكالمات وغيرها"</string>
@@ -237,7 +237,7 @@
<string name="choose_profile" msgid="343803890897657450">"تحديد الملف"</string>
<string name="category_personal" msgid="6236798763159385225">"الملف الشخصي"</string>
<string name="category_work" msgid="4014193632325996115">"ملف العمل"</string>
- <string name="category_private" msgid="4244892185452788977">"الملف الخاص"</string>
+ <string name="category_private" msgid="4244892185452788977">"المساخة الخاصة"</string>
<string name="category_clone" msgid="1554511758987195974">"استنساخ"</string>
<string name="development_settings_title" msgid="140296922921597393">"خيارات المطورين"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"تفعيل خيارات المطورين"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"جهاز متّصل"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"لا يمكن تشغيل الوسائط هنا"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"يجب ترقية الحساب للتبديل"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"المحتوى المنزَّل غير متوافق"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 62d6f68e3611..02324de0276f 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"সংযোগ হৈ থকা ডিভাইচ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইচটো প্লে\' কৰিব নোৱাৰি"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"সলনি কৰিবলৈ একাউণ্ট আপগ্ৰে’ড কৰক"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ইয়াত ডাউনল’ডসমূহ প্লে’ কৰিব নোৱাৰি"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 459cfaaae9c4..25e85d53cdce 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Xarici cihaz"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Qoşulmuş cihaz"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oxutmaq olmur"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Keçirmək üçün hesabı güncəllə"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Burada endirmələri oxutmaq olmur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index f883c93fd15e..4214a4016965 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Spoljni uređaj"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne možete da pustite na ovom uređaju"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite nalog radi prebacivanja"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Preuzimanja ne mogu da se puštaju ovde"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index ca07c4def3a0..3e645b3f1ae0 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Знешняя прылада"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Падключаная прылада"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не ўдаецца прайграць на гэтай прыладзе"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Для пераключэння перайдзіце на іншую версію ўліковага запісу"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Тут не ўдаецца прайграць спампоўкі"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 7661a31d4dc2..8bdd17ebd861 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Възпроизвеждането не е възможно на това устройство"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Надстройте профила, за да превключите"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Изтеглянията не могат да се възпроизвеждат тук"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index ca98e0e510a5..834eb1eef1ce 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"কানেক্ট থাকা ডিভাইস"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইসে চালানো যাবে না"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"পাল্টাতে অ্যাকাউন্ট আপগ্রেড করুন"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"এতে ডাউনলোড করা কন্টেন্ট প্লে করা যাবে না"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index f6ee5877f5fa..069dc04e84b2 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -156,13 +156,13 @@
<item msgid="1241278021345116816">"Optimizirano za kvalitet zvuka (990 kbps/909 kbps)"</item>
<item msgid="3523665555859696539">"Uravnotežen kvalitet zvuka i veze (660kbps/606kbps)"</item>
<item msgid="886408010459747589">"Optimizirano za kvalitet veze (330 kbps/303 kbps)"</item>
- <item msgid="3808414041654351577">"Maksimalan napor (prilagodljiva brzina prijenosa)"</item>
+ <item msgid="3808414041654351577">"Maksimalan napor (prilagodljiva brzina prenosa)"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
<item msgid="804499336721569838">"Optimizirano za kvalitet zvuka"</item>
<item msgid="7451422070435297462">"Uravnotežen kvalitet zvuka i veze"</item>
<item msgid="6173114545795428901">"Optimizirano za kvalitet veze"</item>
- <item msgid="4349908264188040530">"Maksimalan napor (prilagodljiva brzina prijenosa)"</item>
+ <item msgid="4349908264188040530">"Maksimalan napor (prilagodljiva brzina prenosa)"</item>
</string-array>
<string-array name="bluetooth_audio_active_device_summaries">
<item msgid="8019740759207729126"></item>
@@ -276,8 +276,8 @@
</string-array>
<string-array name="usb_configuration_titles">
<item msgid="3358668781763928157">"Punjenje"</item>
- <item msgid="7804797564616858506">"MTP (protokol za prijenos sadržaja medija)"</item>
- <item msgid="910925519184248772">"PTP (protokol za prijenos slika)"</item>
+ <item msgid="7804797564616858506">"MTP (protokol za prenos sadržaja medija)"</item>
+ <item msgid="910925519184248772">"PTP (protokol za prenos slika)"</item>
<item msgid="3825132913289380004">"RNDIS (USB Ethernet)"</item>
<item msgid="8828567335701536560">"Izvor zvuka"</item>
<item msgid="8688681727755534982">"MIDI"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 4d334660895e..75fe8181489d 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -140,10 +140,10 @@
<string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Povezano s LE zvukom"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano sa zvukom medija"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano na zvuk telefona"</string>
- <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Povezano sa serverom za prijenos podataka"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Povezano sa serverom za prenos podataka"</string>
<string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"Povezano na mapu"</string>
<string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"Povezan na SAP"</string>
- <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Nije povezano sa serverom za prijenos podataka"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Nije povezano sa serverom za prenos podataka"</string>
<string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"Povezano s ulaznim uređajem"</string>
<string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Povezano na uređaj za pristup internetu"</string>
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Dijeljenje lokalne internetske veze s uređajem"</string>
@@ -152,7 +152,7 @@
<string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"Koristi za pristup SIM-u"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"Koristi za zvuk medija"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Koristi za zvuk telefona"</string>
- <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Koristi za prijenos fajlova"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Koristi za prenos fajlova"</string>
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Koristi kao ulaz"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Korištenje za slušne aparate"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Koristi za: LE_AUDIO"</string>
@@ -315,7 +315,7 @@
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Aktivirajte Bluetooth Audio Codec\nOdabir: Način rada po kanalima"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Bluetooth Audio LDAC kodek: kvalitet reprodukcije"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Aktivirajte Bluetooth Audio \nOdabir kodeka: kvalitet reprodukcije"</string>
- <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Prijenos: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
+ <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Prenos: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"Privatni DNS"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Odaberite način rada privatnog DNS-a"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"Isključeno"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nije moguće reproducirati na uređaju"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite račun da promijenite"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Nije moguće reproducirati preuzimanja ovdje"</string>
@@ -719,7 +725,7 @@
<string name="accessibility_data_one_bar" msgid="6892888138070752480">"Prijenos podataka na jednoj crtici."</string>
<string name="accessibility_data_two_bars" msgid="9202641507241802499">"Prijenos podataka na dvije crtice."</string>
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Prijenos podataka na tri crtice."</string>
- <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal za prijenos podataka pun."</string>
+ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal za prenos podataka pun."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Veza sa Ethernetom je prekinuta."</string>
<string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
<string name="accessibility_no_calling" msgid="3540827068323895748">"Nema pozivanja."</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 2aa3ffa6d145..2c41b1aee8ef 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -329,7 +329,7 @@
<string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quan aquest mode està activat, és possible que l’adreça MAC d\'aquest dispositiu canviï cada vegada que es connecti a una xarxa amb l\'aleatorització d\'adreces MAC activada"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"D\'ús mesurat"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"D\'ús no mesurat"</string>
- <string name="select_logd_size_title" msgid="1604578195914595173">"Mides de la memòria intermèdia del registre"</string>
+ <string name="select_logd_size_title" msgid="1604578195914595173">"Mides de la memòria cau del registre"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Selecciona la mida de la memòria intermèdia del registre"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Vols esborrar l\'emmagatzematge persistent del registrador?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"Quan deixem de supervisar amb el registrador persistent, hem d\'esborrar les dades del registrador que hi ha al teu dispositiu."</string>
@@ -579,7 +579,7 @@
<string name="alarm_template_far" msgid="6382760514842998629">"Data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durada"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pregunta sempre"</string>
- <string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string>
+ <string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no ho desactivis"</string>
<string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sense nom)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositiu extern"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositiu connectat"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"No es pot reproduir en aquest dispositiu"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualitza el compte per canviar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Les baixades no es poden reproduir aquí"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 0a88338cf264..a4b491bfc1f0 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"V zařízení nelze přehrávat"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Účet je třeba upgradovat"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Stažený obsah zde nelze přehrát"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 983003bf1550..f7402732299b 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhed"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Forbundet enhed"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke afspilles på denne enhed"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Opgrader kontoen for at skifte"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Downloads kan ikke afspilles her"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 75ebb57843b9..c8d8cb55de99 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externes Gerät"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbundenes Gerät"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Wiedergabe auf diesem Gerät nicht möglich"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Zum Umstellen Kontoupgrade durchführen"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Downloads können hier nicht abgespielt werden"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index c838f689528e..c773372790b4 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -520,7 +520,7 @@
<string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Γρήγορη φόρτιση"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ελέγχονται από το διαχειριστή"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string>
- <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένο"</string>
+ <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένη"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Επιτρέπεται"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Δεν επιτρέπεται"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Εγκατ. άγνωστων εφ."</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφ."</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Δεν είναι δυνατή η αναπαραγωγή σε αυτήν τη συσκευή"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Αναβαθμίστε τον λογαριασμό για εναλλαγή"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Δεν είναι δυνατή η αναπαραγωγή των λήψεων εδώ"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b4bf2055df0e..8fe14350f3d9 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Can\'t play downloads here"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index af392b83cfb1..0d560ea7c8b9 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -591,6 +591,9 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
+ <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analog"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Cant play on this device"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Cant play downloads here"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b4bf2055df0e..8fe14350f3d9 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Can\'t play downloads here"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b4bf2055df0e..8fe14350f3d9 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Can\'t play downloads here"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 3c1a27822a3c..99dd4d9a56b9 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"No se pueden reproducir las descargas aquí"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 94502c5e0b3a..cbbadd44d357 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -475,7 +475,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corrección de color puede ser útil si quieres:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;Ver los colores mejor&lt;/li&gt; &lt;li&gt;&amp;nbsp;Quitar los colores para concentrarte mejor&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corrección de color puede ser útil si quieres:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;Ver los colores mejor&lt;/li&gt; &lt;li&gt;&amp;nbsp;Quitar colores para concentrarte mejor&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga pausada para proteger la batería"</string>
@@ -490,7 +490,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"Debería durar hasta las <xliff:g id="TIME">%1$s</xliff:g> basado en tu uso"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Debería durar hasta las <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hasta las <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Puede que se agote la batería sobre las <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"No se pueden reproducir descargas aquí"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 5f5a38c1464b..3c337933fbfb 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Väline seade"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ühendatud seade"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Selles seadmes ei saa esitada"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Lülitamiseks täiendage kontot"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Siin ei saa allalaaditud faile esitada"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 48e4f62e5865..c9947485e5f2 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kanpoko gailua"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Konektatutako gailua"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ezin da erreproduzitu gailu honetan"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Aldatzeko, bertsio-berritu kontua"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Deskargak ezin dira hemen erreproduzitu"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1aeca86aed6c..75b805109824 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"دستگاه متصل"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"نمی‌توان در این دستگاه پخش کرد"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"برای تغییر، حساب را ارتقا دهید"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"نمی‌توان بارگیری‌ها را در اینجا پخش کرد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 53724a3b7d86..9cc33b013979 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Yhdistetty laite"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ei voi toistaa tällä laitteella"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Vaihda päivittämällä tili"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Latauksia ei voi toistaa täällä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 30eba9eceb5a..28692d0afa55 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -512,7 +512,7 @@
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string>
<string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Recharge en cours…"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string>
- <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connecté, mais ne se recharge pas"</string>
+ <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Appareil connecté, mais pas en cours de recharge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
<string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complètement rechargée"</string>
<string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Recharge en pause"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de faire jouer le contenu sur cet appareil"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à jour le compte pour passer à la version payante"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Lecture des téléchargements impossible ici"</string>
@@ -606,7 +612,7 @@
<string name="tv_media_transfer_default" msgid="5403053145185843843">"Sortie audio par défaut de la télévision"</string>
<string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Sortie HDMI"</string>
<string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Haut-parleurs internes"</string>
- <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string>
+ <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez et rallumez l\'appareil"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
<string name="storage_category" msgid="2287342585424631813">"Stockage"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index f4afc8701609..4e7abc938ccf 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -464,7 +464,7 @@
<string name="transcode_disable_cache" msgid="3160069309377467045">"Désactiver la cache de transcodage"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string>
- <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string>
+ <string name="select_webview_provider_title" msgid="3917815648099445503">"Implémentation WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Définir la mise en œuvre WebView"</string>
<string name="select_webview_provider_toast_text" msgid="8512254949169359848">"Ce choix n\'est plus valide. Réessayez."</string>
<string name="picture_color_mode" msgid="1013807330552931903">"Mode de couleur des images"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de lire du contenu sur cet appareil"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à niveau le compte pour changer"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Impossible de lire les téléchargements ici"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index be1fac816f7d..a4d3a0a4f751 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Non se pode reproducir contido neste dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Cambia a conta a un plan superior para facer a modificación"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Non se poden reproducir as descargas neste dispositivo"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 05ec0e15da65..7a26463bc9bd 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"કનેક્ટ કરેલું ડિવાઇસ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"આ ડિવાઇસ પર ચલાવી શકતા નથી"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"સ્વિચ કરવા માટે એકાઉન્ટ અપગ્રેડ કરો"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ડાઉનલોડ કરેલું કન્ટેન્ટ અહીં ચલાવી શકતા નથી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 352dba79565d..83cb3989e909 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट किया गया डिवाइस"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"इस डिवाइस पर मीडिया नहीं चलाया जा सकता"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"प्रीमियम खाते में स्विच करने के लिए, अपना खाता अपग्रेड करें"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"डाउनलोड किए गए वीडियो यहां नहीं चलाए जा सकते"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 2cf02fc2ff39..e78884d65538 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne može se reproducirati ovdje"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite i prebacite se"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ne može se tu reproducirati"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 86a959a97cd0..ca18ac2e191f 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Külső eszköz"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Csatlakoztatott eszköz"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nem játszható le ezen az eszközön"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"A váltáshoz frissítse fiókját"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Itt nem játszhatók le a letöltések"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 4c29740b552a..0749d6042b6b 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Միացված սարք"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Հնարավոր չէ նվագարկել այս սարքում"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Փոխելու համար անցեք հաշվի պրեմիում տարբերակին"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ներբեռնումները չեն նվագարկվում այստեղ"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index bfd8f397be60..7d652c9db683 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Perangkat Eksternal"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Perangkat yang terhubung"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat memutar di perangkat ini"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade akun untuk beralih"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tidak dapat memutar hasil download di sini"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index d0184e112fc0..67f4fe1b9562 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ytra tæki"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tengt tæki"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ekki er hægt að spila í þessu tæki"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppfærðu reikninginn til að skipta"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ekki er hægt að spila niðurhal hér"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 773aa2659171..9f7f41b8b16f 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo smartphone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Qui non è possibile riprodurre i download"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 45c41dd4251b..056aae170610 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -577,8 +577,8 @@
<string name="zen_alarm_warning" msgid="245729928048586280">"לא תושמע ההתראה הבאה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="3346777418136233330">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="6382760514842998629">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
- <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"משך זמן"</string>
- <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"יש לשאול בכל פעם"</string>
+ <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"כמה זמן"</string>
+ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"אני רוצה לבחור בכל פעם"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string>
<string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ללא שם)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"יש לשדרג חשבון כדי לעבור"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"לא ניתן להפעיל הורדות"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index ae3f1970d206..833d1dcf4aef 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部デバイス"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"接続済みのデバイス"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"このデバイス"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"このデバイスでは再生できません"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"アカウントを更新して切り替えてください"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"再生不可: ダウンロードしたコンテンツ"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index fe9dbb52cdd0..acad174698fb 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"დაკავშირებული მოწყობილობა"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ამ მოწყობილობაზე დაკვრა შეუძლებელია"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"გადასართავად განაახლეთ ანგარიში"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"შეუძლებელია აქ ჩამოტვირ. თამაში"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 1162e8f8a00f..e2ce5b0e8ef3 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Жалғанған құрылғы"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Осы құрылғыда ойнату мүмкін емес."</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Ауысу үшін аккаунтты жаңартыңыз."</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктеп алынғандарды осы жерде ойнату мүмкін емес."</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5a184c585ef5..c06aaeeb4bda 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -475,7 +475,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ខ្វាក់ពណ៌ក្រហម (ក្រហម​បៃតង​)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ខ្វាក់ពណ៌ខៀវ (ខៀវ​លឿង​)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ការ​កែតម្រូវ​ពណ៌"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ការ​កែតម្រូវ​ពណ៌អាចមានប្រយោជន៍ នៅពេលអ្នកចង់៖&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;មើលពណ៌កាន់តែត្រឹមត្រូវ&lt;/li&gt; &lt;li&gt;&amp;nbsp;លុបពណ៌ចេញ ដើម្បីជួយឱ្យអ្នកផ្ដោតអារម្មណ៍&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ការ​កែតម្រូវ​ពណ៌អាចមានប្រយោជន៍ នៅពេលអ្នកចង់៖&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;មើលពណ៌កាន់តែត្រឹមត្រូវ&lt;/li&gt; &lt;li&gt;&amp;nbsp;ដកពណ៌ចេញ ដើម្បីជួយឱ្យអ្នកផ្ដោតអារម្មណ៍&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"បដិសេធ​ដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងផ្អាកការសាកថ្ម ដើម្បីការពារថ្ម"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"​ឧបករណ៍ដែលបាន​ភ្ជាប់"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ទូរសព្ទនេះ"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"មិនអាចចាក់នៅលើ​ឧបករណ៍នេះបានទេ"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ដំឡើងកម្រិតគណនី ដើម្បីប្ដូរ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"មិនអាចចាក់ខ្លឹមសារដែលបានទាញយកនៅទីនេះបានទេ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index f34a43cfdd19..2c25501e60a7 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"ಕನೆಕ್ಟ್ ಮಾಡಿರುವ ಸಾಧನ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ಬದಲಾಯಿಸಲು ಖಾತೆಯನ್ನು ಅಪ್‌ಗ್ರೇಡ್ ಮಾಡಿ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ಇಲ್ಲಿ ಡೌನ್‌ಲೋಡ್‌ಗಳನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d803404fb34a..f7dc6ae8603d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -94,7 +94,7 @@
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"연결됨(전화 없음), 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"연결됨(미디어 없음), 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"연결됨(전화 또는 미디어 없음), 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"사용 중입니다. 배터리는 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>입니다."</string>
+ <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"사용 중. 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"사용 중입니다. 배터리는 왼쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>입니다."</string>
<string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"활성 상태입니다. 왼쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"활성 상태입니다. 오른쪽: 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -490,7 +490,7 @@
<string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"사용량을 기준으로 약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
<string name="power_discharge_by" msgid="4113180890060388350">"대략 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"대략 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
- <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g>까지"</string>
+ <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"예상 배터리 종료 시간: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"연결된 기기"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"이 휴대전화"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"이 기기에서 재생할 수 없음"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"계정을 업그레이드하여 전환하기"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"여기서 다운로드한 콘텐츠를 재생할 수 없습니다."</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 785bf43bb4d1..6105db0bf071 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Туташкан түзмөк"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотууга болбойт"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Премиум аккаунтка которулуу керек"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктөлүп алынгандар ойнотулбайт"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 740586e91f56..df3689e72249 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"ອຸປະກອນທີ່ເຊື່ອມຕໍ່"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ຫຼິ້ນຢູ່ອຸປະກອນນີ້ບໍ່ໄດ້"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ອັບເກຣດບັນຊີເພື່ອສະຫຼັບ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ບໍ່ສາມາດຫຼິ້ນເນື້ອຫາທີ່ດາວໂຫຼດຢູ່ນີ້ໄດ້"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 0c235b8a6f35..4d9858733f86 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Išorinis įrenginys"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Prijungtas įrenginys"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Negalima leisti šiame įrenginyje"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Jei norite perjungti, naujovinkite paskyrą"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Čia negalima paleisti atsisiuntimų"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index daaebc880ec9..582982673441 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ārēja ierīce"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pievienotā ierīce"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nevar atskaņot šajā ierīcē."</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Lai pārslēgtu, jauniniet kontu"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Šeit nevar atskaņot lejupielādes"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index b9d970d8388f..5d5d480ecd68 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Поврзан уред"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не може да се пушти на уредов"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Надградете ја сметката за да се префрлите"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Не може да се пуштаат преземања тука"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 7dd2073642f3..71406232cb64 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"കണക്‌റ്റ് ചെയ്‌ത ഉപകരണം"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ഈ ഉപകരണത്തിൽ പ്ലേ ചെയ്യാൻ കഴിയില്ല"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"അക്കൗണ്ട് മാറാൻ അപ്‌ഗ്രേഡ് ചെയ്യുക"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ഡൗൺലോഡുകൾ പ്ലേ ചെയ്യാനാകില്ല"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 4f247ab410a5..c9e0178ce54f 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Холбогдсон төхөөрөмж"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Энэ утас"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Энэ төхөөрөмжид тоглуулах боломжгүй"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Сэлгэхийн тулд бүртгэлийг сайжруулна уу"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Татаж авсан файлыг энд тоглуулах боломжгүй"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index be9464c30241..a6596cdf7d0c 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट केलेले डिव्हाइस"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"या डिव्हाइसवर प्ले करू शकत नाही"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"स्विच करण्यासाठी खाते अपग्रेड करा"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"येथे डाउनलोड प्ले केले जाऊ शकत नाहीत"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 8f67d339fba0..ebde3319a806 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Peranti Luar"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Peranti yang disambungkan"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefon ini"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat dimainkan pada peranti ini"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Tingkatkan akaun untuk beralih"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tidak dapat memainkan muat turun di sini"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 9bb30c74c03e..1131007fe1bd 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -475,7 +475,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (အနီ-အစိမ်း)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (အပြာ-အဝါ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"အရောင် အမှန်ပြင်ခြင်း"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"အရောင် အမှန်ပြင်ခြင်းသည် အောက်ပါတို့အတွက် အသုံးဝင်နိုင်သည်-&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;အရောင်များကို ပိုမိုမှန်ကန်စွာ ကြည့်ရှုခြင်း&lt;/li&gt; &lt;li&gt;&amp;nbsp;အာရုံစိုက်နိုင်ရန် အရောင်များ ဖယ်ရှားခြင်း&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"အရောင် အမှန်ပြင်ခြင်းသည် အောက်ပါတို့အတွက် အသုံးဝင်နိုင်သည်-&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;အရောင်များကို ပိုမိုမှန်ကန်စွာ ကြည့်ရှုရန်&lt;/li&gt; &lt;li&gt;&amp;nbsp;အာရုံစိုက်နိုင်ရန် အရောင်များ ဖယ်ရှားရန်&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီကာကွယ်ရန် အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"ချိတ်ဆက်ကိရိယာ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ဤစက်ပစ္စည်းတွင် ဖွင့်၍မရပါ"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ပြောင်းရန် အကောင့်အဆင့်ကိုမြှင့်ပါ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ဤနေရာတွင် ဒေါင်းလုဒ်များ ဖွင့်မရပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index e16fea25271e..1a71ec07457e 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhet"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tilkoblet enhet"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke spille på denne enheten"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Oppgrader kontoen for å bytte"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan ikke spille av nedlastinger her"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index b26376179c62..dea9fcce87cf 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -184,7 +184,7 @@
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"हटाइएका एपहरू"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"एपहरू र प्रयोगकर्ताहरू हटाइयो।"</string>
- <string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string>
+ <string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अपडेटहरू"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेदरिङ"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"पोर्टेबल हटस्पट"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेदर गर्दै"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट गरिएको डिभाइस"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"यो डिभाइसमा मिडिया प्ले गर्न मिल्दैन"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"आफूले प्रयोग गर्न चाहेको खाता अपग्रेड गर्नुहोस्"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"डाउनलोड गरिएका सामग्री यसमा प्ले गर्न मिल्दैन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 75d561762967..e3be50736a88 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbonden apparaat"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Deze telefoon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan niet afspelen op dit apparaat"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade het account om te schakelen"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan hier geen downloads afspelen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index ae1df210bce3..ea6fdba27506 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"କନେକ୍ଟ କରାଯାଇଥିବା ଡିଭାଇସ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ଏହି ଡିଭାଇସରେ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ସ୍ୱିଚ କରିବା ପାଇଁ ଆକାଉଣ୍ଟକୁ ଅପଗ୍ରେଡ କରନ୍ତୁ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ଏଠାରେ ଡାଉନଲୋଡଗୁଡ଼ିକୁ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index c1ef613f046e..853405167e01 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ਸਵਿੱਚ ਕਰਨ ਲਈ ਖਾਤੇ ਨੂੰ ਅੱਪਗ੍ਰੇਡ ਕਰੋ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ਡਾਊਨਲੋਡਾਂ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 0a8ea16b0279..a9d4dcbf577a 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -520,7 +520,7 @@
<string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Szybkie ładowanie"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolowane przez administratora"</string>
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Obowiązują ustawienia z ograniczonym dostępem"</string>
- <string name="disabled" msgid="8017887509554714950">"Wyłączone"</string>
+ <string name="disabled" msgid="8017887509554714950">"Wyłączona"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Dozwolone"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Niedozwolone"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalowanie nieznanych aplikacji"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Urządzenie zewnętrzne"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Połączone urządzenie"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nie można odtworzyć na tym urządzeniu"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Aby przełączyć, potrzebujesz konta premium"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tutaj nie można odtworzyć pobranych plików"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index c39648817af9..eac646ff4c10 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Neste telefone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Não é possível abrir os downloads aqui"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index be91222c38f2..d8d172d25192 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Atualize a conta para mudar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Não é possível reproduzir as transferências aqui"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index c39648817af9..eac646ff4c10 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Neste telefone"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Não é possível abrir os downloads aqui"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4a0023fd6387..d507b8526c8d 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -196,7 +196,7 @@
<string name="launch_defaults_some" msgid="3631650616557252926">"Unele valori prestabilite sunt configurate"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Nu este configurată nicio valoare prestabilită"</string>
<string name="tts_settings" msgid="8130616705989351312">"Setări redare vocală a textului"</string>
- <string name="tts_settings_title" msgid="7602210956640483039">"Setări pentru redarea vocală a textului"</string>
+ <string name="tts_settings_title" msgid="7602210956640483039">"Redare vocală a textului"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Ritmul vorbirii"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Viteza cu care este vorbit textul"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Înălțime"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispozitiv extern"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispozitiv conectat"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nu se poate reda pe acest dispozitiv"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Fă upgrade contului pentru a comuta"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Aici nu se pot reda descărcări"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 1abe67a3f020..8d07c57fec56 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -125,7 +125,7 @@
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Профиль A2DP"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Звонки"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Профиль HID"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Устройство ввода"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ к интернету"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Разрешить доступ к контактам и журналу звонков"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Эти сведения нужны для оповещений о звонках и других функций"</string>
@@ -283,7 +283,7 @@
<string name="keep_screen_on_summary" msgid="1510731514101925829">"Во время зарядки экран будет всегда включен"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"Включить snoop-логи Bluetooth HCI"</string>
<string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Сохранять все пакеты Bluetooth (перезапустите Bluetooth после изменения этой настройки)"</string>
- <string name="oem_unlock_enable" msgid="5334869171871566731">"Заводская разблокировка"</string>
+ <string name="oem_unlock_enable" msgid="5334869171871566731">"Разблокировка загрузчика"</string>
<string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Разрешить разблокировку загрузчика ОС"</string>
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Разрешить заводскую разблокировку?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ВНИМАНИЕ! Функции защиты не будут работать на устройстве, пока включен этот параметр."</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Подключенное устройство"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Невозможно воспроизвести на этом устройстве."</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Для переключения требуется премиум-аккаунт"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Не удается воспроизвести скачанные файлы"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index a9004d0ebedc..6694bc552e11 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"සම්බන්ධ කළ උපාංගය"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"මෙම දුරකථනය"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"මෙම උපාංගය මත ධාවනය කළ නොහැක"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"මාරු වීමට ගිණුම උත්ශ්‍රේණි කරන්න"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"මෙහි බාගැනීම් වාදනය කළ නොහැක"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 4c41b7d4e397..fa525a20ca66 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -475,7 +475,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomália (červená a zelená)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomália (modrá a žltá)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Úprava farieb"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Úprava farieb môže byť užitočná, keď chcete:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;zobrazovať farby presnejšie;&lt;/li&gt; &lt;li&gt;&amp;nbsp;odstrániť farby, aby ste sa mohli sústrediť.&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Úprava farieb môže byť užitočná, keď chcete:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;vidieť farby presnejšie;&lt;/li&gt; &lt;li&gt;&amp;nbsp;odstrániť farby, aby ste sa mohli sústrediť.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíjanie je pozastavené, aby sa chránila batéria"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externé zariadenie"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pripojené zariadenie"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"V tomto zariadení sa nedá prehrávať obsah"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Inovujte účet a prejdite naň"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tu sa nedajú prehrať stiahnuté súbory"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 0945cc042dc5..9b426aa27e19 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Zunanja naprava"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezana naprava"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ni mogoče predvajati v tej napravi."</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Za preklop je potrebna nadgradnja računa"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Prenosov tu ni mogoče predvajati"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 0b3955106557..73cc518dac0a 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -304,12 +304,12 @@
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string>
<string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string>
<string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Zgjidh versionin MAP të Bluetooth-it"</string>
- <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodeku Bluetooth Audio"</string>
+ <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodeku i audios me Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Shpejtësia e shembullit të Bluetooth Audio"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Shpejtësia e shembullit"</string>
<string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"Çaktivizimi do të thotë se nuk mbështetet nga telefoni ose kufjet"</string>
- <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Bite për shembull Bluetooth Audio"</string>
+ <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Bite të audios me Bluetooth për shembull"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Bite për shembull"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"Modaliteti i kanalit të audios me Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Modaliteti i kanalit"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nuk mund të luhet në këtë pajisje"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Përmirëso llogarinë për të ndryshuar"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Shkarkimet nuk mund të luhen këtu"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index d351a12bac21..d8003d75bd4e 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Повезани уређај"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Овај телефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можете да пустите на овом уређају"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Надоградите налог ради пребацивања"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Преузимања не могу да се пуштају овде"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1d7f62ac6c93..f9bd295253a6 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -235,7 +235,7 @@
<item msgid="6946761421234586000">"400 %"</item>
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Välj profil"</string>
- <string name="category_personal" msgid="6236798763159385225">"Privat"</string>
+ <string name="category_personal" msgid="6236798763159385225">"Personlig"</string>
<string name="category_work" msgid="4014193632325996115">"Jobb"</string>
<string name="category_private" msgid="4244892185452788977">"Privat"</string>
<string name="category_clone" msgid="1554511758987195974">"Klon"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern enhet"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ansluten enhet"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan inte spelas på denna enhet"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppgradera kontot för att byta"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Det går inte att spela upp nedladdningar här"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 820fcc877190..b86be31ec352 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -237,7 +237,7 @@
<string name="choose_profile" msgid="343803890897657450">"Chagua wasifu"</string>
<string name="category_personal" msgid="6236798763159385225">"Binafsi"</string>
<string name="category_work" msgid="4014193632325996115">"Kazini"</string>
- <string name="category_private" msgid="4244892185452788977">"Faragha"</string>
+ <string name="category_private" msgid="4244892185452788977">"Sehemu ya Faragha"</string>
<string name="category_clone" msgid="1554511758987195974">"Kloni"</string>
<string name="development_settings_title" msgid="140296922921597393">"Chaguo za wasanidi"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Washa chaguo za wasanidi programu"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kifaa cha Nje"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Kifaa kilichounganishwa"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Simu hii"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Huwezi kucheza maudhui kwenye kifaa hiki"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Pata toleo jipya la akaunti ili ubadilishe"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Imeshindwa kucheza maudhui yaliyopakuliwa hapa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 98412c1f41f4..1fd78d3842eb 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"இணைக்கப்பட்டுள்ள சாதனம்"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"இந்தச் சாதனத்தில் பிளே செய்ய முடியவில்லை"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"மாற்ற, கணக்கை மேம்படுத்துங்கள்"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"பதிவிறக்கங்களை இங்கே பிளே செய்ய முடியாது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 3770bb776627..fc45d2c8e149 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్‌టర్నల్ పరికరం"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"కనెక్ట్ చేసిన పరికరం"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ఈ పరికరంలో ప్లే చేయడం సాధ్యపడదు"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"మారడానికి ఖాతాను అప్‌గ్రేడ్ చేయండి"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ఇక్కడ డౌన్‌లోడ్‌లను ప్లే చేయడం సాధ్యపడదు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 060b7b267624..d525bc538ef8 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"อุปกรณ์ที่เชื่อมต่อ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"โทรศัพท์เครื่องนี้"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"เล่นในอุปกรณ์นี้ไม่ได้"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"อัปเกรดบัญชีเพื่อเปลี่ยน"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"เล่นเนื้อหาที่ดาวน์โหลดที่นี่ไม่ได้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index e869e7f3bbb4..1df74736d879 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"External na Device"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Nakakonektang device"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ang teleponong ito"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Hindi ma-play sa device na ito"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"I-upgrade ang account para lumipat"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Hindi mape-play ang mga download dito"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 188f5aa2ce8e..d62269605e84 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Harici Cihaz"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Bağlı cihaz"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oynatılamıyor"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Geçiş yapmak için hesabı yükseltin"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"İndirilenler burada oynatılamaz"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index b82db3a8a85f..eb10cb209c20 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Підключений пристрій"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можна відтворювати тут"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Потрібний платний обліковий запис"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Завантаження не відтворюватимуться"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 43f03a20e0fc..8b2eb3fb0189 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"منسلک آلہ"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"اس آلے پر چلایا نہیں جا سکتا"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"سوئچ کرنے کے لیے اکاؤنٹ اپ گریڈ کریں"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ڈاؤن لوڈز کو یہاں چلایا نہیں جا سکتا"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 685d6e63b082..62a6303fd45b 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Tashqi qurilma"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ulangan qurilma"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Shu telefon"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu qurilmada ijro etilmaydi"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Oʻtish uchun hisobingizni yangilang"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Yuklab olingan fayllar ijro etilmaydi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 7e98afddbe10..1b90818fbaf9 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Thiết bị bên ngoài"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Thiết bị đã kết nối"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Điện thoại này"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Không phát được trên thiết bị này"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Nâng cấp tài khoản để chuyển đổi"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Không thể phát các tệp đã tải xuống tại đây"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 0f3373e1142f..94915650223f 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"无法在此设备上播放"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"升级账号后才能切换"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"无法在此设备上播放下载的内容"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index d6ec1ca579e1..aa3ac06298c5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -475,7 +475,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"紅色弱視 (紅綠)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"藍色弱視 (藍黃)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"色彩校正"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"「色彩校正」功能適用於以下情況::&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;你想讓裝置顯示更準確的色彩&lt;/li&gt; &lt;li&gt;&amp;nbsp;你想移除色彩以提高專注力&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"「色彩校正」功能適用於以下情況:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;你想讓裝置顯示更準確的色彩&lt;/li&gt; &lt;li&gt;&amp;nbsp;你想移除色彩以提高專注力&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string>
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連接的裝置"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在此裝置上播放"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"無法在此播放下載內容"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 08bf732652b9..b5eb87da6de3 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連結的裝置"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在這部裝置上播放"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"這裡無法播放下載內容"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index ad4f04555d05..57e0b8d8afeb 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -591,6 +591,12 @@
<string name="media_transfer_external_device_name" msgid="2588672258721846418">"Idivayisi Yangaphandle"</string>
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Idivayisi exhunyiwe"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string>
+ <!-- no translation found for media_transfer_digital_line_name (312091711951124301) -->
+ <skip />
+ <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) -->
+ <skip />
+ <!-- no translation found for media_transfer_aux_line_name (894135835967856689) -->
+ <skip />
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ayikwazi ukudlala kule divayisi"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Thuthukisa i-akhawunti ukuze ushintshe"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Awukwazi ukudlala okudawunilodiwe lapha"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index fd2a1cb14edd..f03014ca95e2 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -821,8 +821,10 @@
<!-- Title of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=32] -->
<string name="enable_linux_terminal_title">Linux development environment</string>
- <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=64] -->
- <string name="enable_linux_terminal_summary">Run Linux terminal on Android</string>
+ <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=none] -->
+ <string name="enable_linux_terminal_summary">(Experimental) Run Linux terminal on Android</string>
+ <!-- Disclaimer below the checkbox that disabling the Linux terminal app would clear its data. [CHAR LIMIT=none] -->
+ <string name="disable_linux_terminal_disclaimer">If you disable, Linux terminal data will be cleared</string>
<!-- HDCP checking title, used for debug purposes only. [CHAR LIMIT=25] -->
<string name="hdcp_checking_title">HDCP checking</string>
@@ -1426,6 +1428,12 @@
<string name="media_transfer_default_device_name">Connected device</string>
<!-- Name of the phone device with an active remote session. [CHAR LIMIT=30] -->
<string name="media_transfer_this_phone">This phone</string>
+ <!-- Name of the digital audio output, i.e. S/PDIF, usually optical. [CHAR LIMIT=30] -->
+ <string name="media_transfer_digital_line_name">S/PDIF</string>
+ <!-- Name of the analog audio output. [CHAR LIMIT=30] -->
+ <string name="media_transfer_analog_line_name">Analog</string>
+ <!-- Name of the AUX audio output. [CHAR LIMIT=30] -->
+ <string name="media_transfer_aux_line_name">AUX</string>
<!-- Sub status indicates device is not available due to an unknown error. [CHAR LIMIT=NONE] -->
<string name="media_output_status_unknown_error">Can\’t play on this device</string>
<!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
index 6578eb7d50a6..c36ade979d47 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -22,14 +22,20 @@ import androidx.annotation.NonNull;
import androidx.preference.DropDownPreference;
import androidx.preference.PreferenceViewHolder;
-public class RestrictedDropDownPreference extends DropDownPreference {
- RestrictedPreferenceHelper mHelper;
+public class RestrictedDropDownPreference extends DropDownPreference implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
public RestrictedDropDownPreference(@NonNull Context context) {
super(context);
mHelper = new RestrictedPreferenceHelper(context, this, null);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt b/packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt
deleted file mode 100644
index 14f9a19ee753..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedInterface.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib
-
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-
-interface RestrictedInterface {
- fun useAdminDisabledSummary(useSummary: Boolean)
-
- fun checkRestrictionAndSetDisabled(userRestriction: String)
-
- fun checkRestrictionAndSetDisabled(userRestriction: String, userId: Int)
-
- /**
- * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
- * package. Marks the preference as disabled if so.
- *
- * @param settingIdentifier The key identifying the setting
- * @param packageName the package to check the settingIdentifier for
- */
- fun checkEcmRestrictionAndSetDisabled(
- settingIdentifier: String,
- packageName: String
- )
-
- val isDisabledByAdmin: Boolean
-
- fun setDisabledByAdmin(admin: EnforcedAdmin?)
-
- val isDisabledByEcm: Boolean
-
- val uid: Int
-
- val packageName: String?
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 495410b0a8ae..332042a5c4f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -34,7 +34,9 @@ import com.android.settingslib.widget.TwoTargetPreference;
* Preference class that supports being disabled by a user restriction
* set by a device admin.
*/
-public class RestrictedPreference extends TwoTargetPreference {
+public class RestrictedPreference extends TwoTargetPreference implements
+ RestrictedPreferenceHelperProvider {
+
RestrictedPreferenceHelper mHelper;
public RestrictedPreference(Context context, AttributeSet attrs,
@@ -67,6 +69,11 @@ public class RestrictedPreference extends TwoTargetPreference {
}
@Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
+ @Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt
new file mode 100644
index 000000000000..f2860845e3d3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelperProvider.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib
+
+/** Provider of [RestrictedPreferenceHelper]. */
+interface RestrictedPreferenceHelperProvider {
+
+ /** Returns the [RestrictedPreferenceHelper] applied to the preference. */
+ fun getRestrictedPreferenceHelper(): RestrictedPreferenceHelper
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
index c52c7ea7328b..573869db5073 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
@@ -32,8 +32,9 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference;
/**
* Selector with widget preference that can be disabled by a device admin using a user restriction.
*/
-public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPreference {
- private RestrictedPreferenceHelper mHelper;
+public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPreference implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
/**
* Perform inflation from XML and apply a class-specific base style.
@@ -82,8 +83,11 @@ public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPr
*/
public RestrictedSelectorWithWidgetPreference(@NonNull Context context) {
this(context, null);
- mHelper =
- new RestrictedPreferenceHelper(context, /* preference= */ this, /* attrs= */ null);
+ }
+
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java
index 1dc5281c266c..b690816a6fb6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSliderPreference.java
@@ -19,7 +19,6 @@ package com.android.settingslib;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Process;
-import android.os.UserHandle;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
@@ -34,8 +33,10 @@ import com.android.settingslib.widget.SliderPreference;
* Slide Preference that supports being disabled by a user restriction
* set by a device admin.
*/
-public class RestrictedSliderPreference extends SliderPreference implements RestrictedInterface {
- RestrictedPreferenceHelper mHelper;
+public class RestrictedSliderPreference extends SliderPreference implements
+ RestrictedPreferenceHelperProvider {
+
+ private final RestrictedPreferenceHelper mHelper;
public RestrictedSliderPreference(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, @Nullable String packageName, int uid) {
@@ -66,6 +67,11 @@ public class RestrictedSliderPreference extends SliderPreference implements Rest
}
@Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
+ @Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
@@ -81,12 +87,12 @@ public class RestrictedSliderPreference extends SliderPreference implements Rest
@Override
public void setEnabled(boolean enabled) {
- if (enabled && isDisabledByAdmin()) {
+ if (enabled && mHelper.isDisabledByAdmin()) {
mHelper.setDisabledByAdmin(null);
return;
}
- if (enabled && isDisabledByEcm()) {
+ if (enabled && mHelper.isDisabledByEcm()) {
mHelper.setDisabledByEcm(null);
return;
}
@@ -95,55 +101,6 @@ public class RestrictedSliderPreference extends SliderPreference implements Rest
}
@Override
- public void useAdminDisabledSummary(boolean useSummary) {
- mHelper.useAdminDisabledSummary(useSummary);
- }
-
- @Override
- public void checkRestrictionAndSetDisabled(@NonNull String userRestriction) {
- mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
- }
-
- @Override
- public void checkRestrictionAndSetDisabled(@NonNull String userRestriction, int userId) {
- mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
- }
-
- @Override
- public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
- @NonNull String packageName) {
- mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
- }
-
- @Override
- public void setDisabledByAdmin(@Nullable RestrictedLockUtils.EnforcedAdmin admin) {
- if (mHelper.setDisabledByAdmin(admin)) {
- notifyChanged();
- }
- }
-
- @Override
- public boolean isDisabledByAdmin() {
- return mHelper.isDisabledByAdmin();
- }
-
- @Override
- public boolean isDisabledByEcm() {
- return mHelper.isDisabledByEcm();
- }
-
- @Override
- public int getUid() {
- return mHelper != null ? mHelper.uid : Process.INVALID_UID;
- }
-
- @Override
- @Nullable
- public String getPackageName() {
- return mHelper != null ? mHelper.packageName : null;
- }
-
- @Override
protected void onAttachedToHierarchy(@NonNull PreferenceManager preferenceManager) {
mHelper.onAttachedToHierarchy();
super.onAttachedToHierarchy(preferenceManager);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index fffbb547c662..727dbe1019ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -45,8 +45,9 @@ import androidx.preference.SwitchPreferenceCompat;
* Version of SwitchPreferenceCompat that can be disabled by a device admin
* using a user restriction.
*/
-public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
- RestrictedPreferenceHelper mHelper;
+public class RestrictedSwitchPreference extends SwitchPreferenceCompat implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
AppOpsManager mAppOpsManager;
boolean mUseAdditionalSummary = false;
CharSequence mRestrictedSwitchSummary;
@@ -98,6 +99,11 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
this(context, null);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
@VisibleForTesting
public void setAppOps(AppOpsManager appOps) {
mAppOpsManager = appOps;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java
index 0096015aa875..34e4d8f7346f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedTopLevelPreference.java
@@ -22,14 +22,16 @@ import android.content.Context;
import android.os.UserHandle;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
/** Top level preference that can be disabled by a device admin using a user restriction. */
-public class RestrictedTopLevelPreference extends Preference {
- private RestrictedPreferenceHelper mHelper;
+public class RestrictedTopLevelPreference extends Preference implements
+ RestrictedPreferenceHelperProvider {
+ private final RestrictedPreferenceHelper mHelper;
public RestrictedTopLevelPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
@@ -51,6 +53,11 @@ public class RestrictedTopLevelPreference extends Preference {
}
@Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
+ @Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 0dc772ab6ecd..ebd5a1deffd2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -224,7 +224,7 @@ public class BluetoothEventManager {
// audio sharing is enabled.
if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
&& state == BluetoothAdapter.STATE_DISCONNECTED
- && BluetoothUtils.isAudioSharingEnabled()) {
+ && BluetoothUtils.isAudioSharingUIAvailable(mContext)) {
LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
if (profileManager != null
&& profileManager.getLeAudioBroadcastProfile() != null
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 612c193da9c3..a87b8153b858 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -64,6 +64,8 @@ public class BluetoothUtils {
public static final int META_INT_ERROR = -1;
public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled";
+ public static final String DEVELOPER_OPTION_PREVIEW_KEY =
+ "bluetooth_le_audio_sharing_ui_preview_enabled";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
private static final Set<Integer> SA_PROFILES =
@@ -643,6 +645,12 @@ public class BluetoothUtils {
&& connectedGroupIds.contains(groupId);
}
+ /** Returns if the le audio sharing UI is available. */
+ public static boolean isAudioSharingUIAvailable(@Nullable Context context) {
+ return isAudioSharingEnabled() || (context != null && isAudioSharingPreviewEnabled(
+ context.getContentResolver()));
+ }
+
/** Returns if the le audio sharing is enabled. */
public static boolean isAudioSharingEnabled() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -653,7 +661,23 @@ public class BluetoothUtils {
&& adapter.isLeAudioBroadcastAssistantSupported()
== BluetoothStatusCodes.FEATURE_SUPPORTED;
} catch (IllegalStateException e) {
- Log.d(TAG, "LE state is on, but there is no bluetooth service.", e);
+ Log.d(TAG, "Fail to check isAudioSharingEnabled, e = ", e);
+ return false;
+ }
+ }
+
+ /** Returns if the le audio sharing preview is enabled in developer option. */
+ public static boolean isAudioSharingPreviewEnabled(@Nullable ContentResolver contentResolver) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ try {
+ return Flags.audioSharingDeveloperOption()
+ && getAudioSharingPreviewValue(contentResolver)
+ && adapter.isLeAudioBroadcastSourceSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED
+ && adapter.isLeAudioBroadcastAssistantSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED;
+ } catch (IllegalStateException e) {
+ Log.d(TAG, "Fail to check isAudioSharingPreviewEnabled, e = ", e);
return false;
}
}
@@ -996,6 +1020,17 @@ public class BluetoothUtils {
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
+ /** Get develop option value for audio sharing preview. */
+ @WorkerThread
+ private static boolean getAudioSharingPreviewValue(@Nullable ContentResolver contentResolver) {
+ if (contentResolver == null) return false;
+ return Settings.Global.getInt(
+ contentResolver,
+ DEVELOPER_OPTION_PREVIEW_KEY,
+ 0 // value off
+ ) == 1;
+ }
+
/** Get secondary {@link CachedBluetoothDevice} in broadcast. */
@Nullable
@WorkerThread
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 8641f7036c50..d0827b30efc9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1245,7 +1245,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
*/
public String getConnectionSummary(boolean shortSummary) {
CharSequence summary = null;
- if (BluetoothUtils.isAudioSharingEnabled()) {
+ if (BluetoothUtils.isAudioSharingUIAvailable(mContext)) {
if (mBluetoothManager == null) {
mBluetoothManager = LocalBluetoothManager.getInstance(mContext, null);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index b9f16edf6e77..4b7cb36f2753 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -383,11 +383,7 @@ public class CsipDeviceManager {
preferredMainDevice.refresh();
hasChanged = true;
}
- if (isWorkProfile()) {
- log("addMemberDevicesIntoMainDevice: skip sync source for work profile");
- } else {
- syncAudioSharingSourceIfNeeded(preferredMainDevice);
- }
+ syncAudioSharingSourceIfNeeded(preferredMainDevice);
}
if (hasChanged) {
log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
@@ -402,8 +398,12 @@ public class CsipDeviceManager {
}
private void syncAudioSharingSourceIfNeeded(CachedBluetoothDevice mainDevice) {
- boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingEnabled();
+ boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingUIAvailable(mContext);
if (isAudioSharingEnabled) {
+ if (isWorkProfile()) {
+ log("addMemberDevicesIntoMainDevice: skip sync source for work profile");
+ return;
+ }
boolean hasBroadcastSource = BluetoothUtils.isBroadcasting(mBtManager)
&& BluetoothUtils.hasConnectedBroadcastSource(
mainDevice, mBtManager);
@@ -433,6 +433,8 @@ public class CsipDeviceManager {
}
}
}
+ } else {
+ log("addMemberDevicesIntoMainDevice: skip sync source, flag disabled");
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPendingIntentAction.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPendingIntentAction.java
index 3d4282cfb6e4..b997e4d29687 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPendingIntentAction.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPendingIntentAction.java
@@ -17,7 +17,6 @@
package com.android.settingslib.bluetooth.devicesettings;
import android.app.PendingIntent;
-import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -47,7 +46,7 @@ public class DeviceSettingPendingIntentAction extends DeviceSettingAction implem
/** Read a {@link DeviceSettingPendingIntentAction} instance from {@link Parcel} */
@NonNull
public static DeviceSettingPendingIntentAction readFromParcel(@NonNull Parcel in) {
- PendingIntent pendingIntent = in.readParcelable(Intent.class.getClassLoader());
+ PendingIntent pendingIntent = in.readParcelable(PendingIntent.class.getClassLoader());
Bundle extras = in.readBundle(Bundle.class.getClassLoader());
return new DeviceSettingPendingIntentAction(pendingIntent, extras);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index edd49c5a8fb7..0209eb8c3fbf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -21,6 +21,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
+import android.os.DeadObjectException
import android.os.IBinder
import android.os.IInterface
import android.os.RemoteException
@@ -52,6 +53,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterIsInstance
@@ -304,6 +306,14 @@ class DeviceSettingServiceConnection(
service.registerDeviceSettingsListener(deviceInfo, listener)
awaitClose { service.unregisterDeviceSettingsListener(deviceInfo, listener) }
}
+ .catch { e ->
+ if (e is DeadObjectException) {
+ Log.e(TAG, "DeadObjectException happens when registering listener.", e)
+ emit(listOf())
+ } else {
+ throw e
+ }
+ }
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 4e1d8e38dfbd..ad196b8c1f7b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,6 +15,9 @@
*/
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.TYPE_AUX_LINE;
+import static android.media.MediaRoute2Info.TYPE_LINE_ANALOG;
+import static android.media.MediaRoute2Info.TYPE_LINE_DIGITAL;
import static android.media.MediaRoute2Info.TYPE_BLE_HEADSET;
import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
@@ -700,6 +703,9 @@ public abstract class InfoMediaManager {
case TYPE_HDMI:
case TYPE_HDMI_ARC:
case TYPE_HDMI_EARC:
+ case TYPE_LINE_DIGITAL:
+ case TYPE_LINE_ANALOG:
+ case TYPE_AUX_LINE:
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
mediaDevice =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 4f315a2a2486..76aa5bf3334c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -75,6 +75,24 @@ public final class InputRouteManager {
@Override
public void onAudioDevicesAdded(@NonNull AudioDeviceInfo[] addedDevices) {
applyDefaultSelectedTypeToAllPresets();
+
+ // Activate the last hot plugged valid input device, to match the output device
+ // behavior.
+ @AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType;
+ for (AudioDeviceInfo info : addedDevices) {
+ if (InputMediaDevice.isSupportedInputDevice(info.getType())) {
+ deviceTypeToActivate = info.getType();
+ }
+ }
+
+ // Only activate if we find a different valid input device. e.g. if none of the
+ // addedDevices is supported input device, we don't need to activate anything.
+ if (mSelectedInputDeviceType != deviceTypeToActivate) {
+ mSelectedInputDeviceType = deviceTypeToActivate;
+ AudioDeviceAttributes deviceAttributes =
+ createInputDeviceAttributes(mSelectedInputDeviceType);
+ setPreferredDeviceForAllPresets(deviceAttributes);
+ }
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index ce1f29766bed..2321097d42d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -33,6 +33,9 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static android.media.MediaRoute2Info.TYPE_LINE_DIGITAL;
+import static android.media.MediaRoute2Info.TYPE_LINE_ANALOG;
+import static android.media.MediaRoute2Info.TYPE_AUX_LINE;
import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION;
import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION_MANAGED;
import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED;
@@ -150,6 +153,9 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
break;
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
+ case TYPE_LINE_DIGITAL:
+ case TYPE_LINE_ANALOG:
+ case TYPE_AUX_LINE:
mType = MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE;
break;
case TYPE_USB_DEVICE:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 481306a18f0e..4766a869e406 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -26,7 +26,9 @@ import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
-
+import static android.media.MediaRoute2Info.TYPE_LINE_DIGITAL;
+import static android.media.MediaRoute2Info.TYPE_LINE_ANALOG;
+import static android.media.MediaRoute2Info.TYPE_AUX_LINE;
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import android.Manifest;
@@ -124,6 +126,15 @@ public class PhoneMediaDevice extends MediaDevice {
name = context.getString(R.string.media_transfer_external_device_name);
}
break;
+ case TYPE_LINE_DIGITAL:
+ name = context.getString(R.string.media_transfer_digital_line_name);
+ break;
+ case TYPE_LINE_ANALOG:
+ name = context.getString(R.string.media_transfer_analog_line_name);
+ break;
+ case TYPE_AUX_LINE:
+ name = context.getString(R.string.media_transfer_aux_line_name);
+ break;
default:
name = context.getString(R.string.media_transfer_default_device_name);
break;
@@ -268,6 +279,9 @@ public class PhoneMediaDevice extends MediaDevice {
switch (mRouteInfo.getType()) {
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
+ case TYPE_LINE_ANALOG:
+ case TYPE_LINE_DIGITAL:
+ case TYPE_AUX_LINE:
id = WIRED_HEADSET_ID;
break;
case TYPE_USB_DEVICE:
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
index 7fdbcdae2276..f446bb8e32d1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
@@ -93,33 +93,23 @@ class ZenModeRepositoryImpl(
IntentFilter().apply {
addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
- if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi())
+ if (android.app.Flags.modesApi())
addAction(
- NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED)
+ NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED
+ )
},
/* broadcastPermission = */ null,
- /* scheduler = */ if (Flags.volumePanelBroadcastFix()) {
- backgroundHandler
- } else {
- null
- },
+ /* scheduler = */ backgroundHandler,
)
awaitClose { context.unregisterReceiver(receiver) }
}
- .let {
- if (Flags.volumePanelBroadcastFix()) {
- // Share the flow to avoid having multiple broadcasts.
- it.flowOn(backgroundCoroutineContext)
- .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
- } else {
- it.shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
- }
- }
+ .flowOn(backgroundCoroutineContext)
+ .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
}
override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> by lazy {
- if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi())
+ if (android.app.Flags.modesApi())
flowFromBroadcast(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED) {
// If available, get the value from extras to avoid a potential binder call.
it?.extras?.getParcelable(EXTRA_NOTIFICATION_POLICY)
@@ -161,11 +151,13 @@ class ZenModeRepositoryImpl(
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
/* notifyForDescendants= */ false,
- observer)
+ observer,
+ )
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG),
/* notifyForDescendants= */ false,
- observer)
+ observer,
+ )
awaitClose { contentResolver.unregisterContentObserver(observer) }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
index 79dabf029c49..5d2a1669e24a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
@@ -41,7 +41,7 @@ class ZenIconKeys {
private static final ImmutableMap<Integer, ZenIcon.Key> TYPE_DEFAULTS = ImmutableMap.of(
AutomaticZenRule.TYPE_UNKNOWN,
- ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_unknown),
+ ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_special_dnd),
AutomaticZenRule.TYPE_OTHER,
ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_other),
AutomaticZenRule.TYPE_SCHEDULE_TIME,
@@ -61,7 +61,7 @@ class ZenIconKeys {
);
private static final ZenIcon.Key FOR_UNEXPECTED_TYPE =
- ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_unknown);
+ ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_special_dnd);
/** Default icon descriptors per mode {@link AutomaticZenRule.Type}. */
static ZenIcon.Key forType(@AutomaticZenRule.Type int ruleType) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
index c3b1a7cb16e3..aeea8cb6df1b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
@@ -24,6 +24,7 @@ import android.media.AudioManager
import android.util.Log
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharedFlow
@@ -31,6 +32,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
@@ -44,6 +46,7 @@ interface AudioManagerEventsReceiver {
class AudioManagerEventsReceiverImpl(
private val context: Context,
coroutineScope: CoroutineScope,
+ backgroundCoroutineContext: CoroutineContext,
) : AudioManagerEventsReceiver {
private val allActions: Collection<String>
@@ -79,6 +82,7 @@ class AudioManagerEventsReceiverImpl(
.filterNotNull()
.filter { intent -> allActions.contains(intent.action) }
.mapNotNull { it.toAudioManagerEvent() }
+ .flowOn(backgroundCoroutineContext)
.shareIn(coroutineScope, SharingStarted.WhileSubscribed())
private fun Intent.toAudioManagerEvent(): AudioManagerEvent? {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
index 35ee8287d52f..58a09fbacc59 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
@@ -60,7 +60,12 @@ class AudioManagerEventsReceiverTest {
fun setup() {
MockitoAnnotations.initMocks(this)
- underTest = AudioManagerEventsReceiverImpl(context, testScope.backgroundScope)
+ underTest =
+ AudioManagerEventsReceiverImpl(
+ context,
+ testScope.backgroundScope,
+ testScope.testScheduler,
+ )
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 0e060dfdd447..6d481dbe64e9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -29,11 +29,13 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -48,6 +50,7 @@ import android.util.Pair;
import com.android.internal.R;
import com.android.settingslib.flags.Flags;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.widget.AdaptiveIcon;
import com.google.common.collect.ImmutableList;
@@ -61,6 +64,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.Collections;
@@ -69,6 +74,7 @@ import java.util.List;
import java.util.Set;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class BluetoothUtilsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -88,6 +94,7 @@ public class BluetoothUtilsTest {
@Mock private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
private Context mContext;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private static final String STRING_METADATA = "string_metadata";
private static final String BOOL_METADATA = "true";
private static final String INT_METADATA = "25";
@@ -109,6 +116,7 @@ public class BluetoothUtilsTest {
mContext = spy(RuntimeEnvironment.application);
mSetFlagsRule.disableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
@@ -1123,4 +1131,129 @@ public class BluetoothUtilsTest {
AudioDeviceInfo.TYPE_HEARING_AID,
address));
}
+
+ @Test
+ public void isAudioSharingEnabled_flagOff_returnsFalse() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+
+ assertThat(BluetoothUtils.isAudioSharingEnabled()).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingEnabled_featureNotSupported_returnsFalse() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+
+ assertThat(BluetoothUtils.isAudioSharingEnabled()).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingEnabled_featureSupported_returnsTrue() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+
+ assertThat(BluetoothUtils.isAudioSharingEnabled()).isTrue();
+ }
+
+ @Test
+ public void isAudioSharingPreviewEnabled_flagOff_returnsFalse() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+
+ assertThat(BluetoothUtils.isAudioSharingPreviewEnabled(
+ mContext.getContentResolver())).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingPreviewEnabled_featureNotSupported_returnsFalse() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+
+ assertThat(BluetoothUtils.isAudioSharingPreviewEnabled(
+ mContext.getContentResolver())).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingPreviewEnabled_developerOptionOff_returnsFalse() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 0);
+
+ assertThat(BluetoothUtils.isAudioSharingPreviewEnabled(
+ mContext.getContentResolver())).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingPreviewEnabled_developerOptionOn_returnsTrue() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 1);
+
+ assertThat(BluetoothUtils.isAudioSharingPreviewEnabled(
+ mContext.getContentResolver())).isTrue();
+ }
+
+ @Test
+ public void isAudioSharingUIAvailable_audioSharingAndPreviewFlagOff_returnsFalse() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+
+ assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingUIAvailable_audioSharingAndPreviewDisabled_returnsFalse() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+
+ assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isFalse();
+ }
+
+ @Test
+ public void isAudioSharingUIAvailable_audioSharingEnabled_returnsTrue() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 0);
+
+ assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isTrue();
+ }
+
+ @Test
+ public void isAudioSharingUIAvailable_audioSharingPreviewEnabled_returnsTrue() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+ BluetoothStatusCodes.FEATURE_SUPPORTED);
+ mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 1);
+
+ assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index 782cee23fb42..d808a25ebc04 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -138,6 +139,18 @@ public class InputRouteManagerTest {
/* address= */ "");
}
+ private AudioDeviceAttributes getUsbHeadsetDeviceAttributes() {
+ return new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_INPUT,
+ AudioDeviceInfo.TYPE_USB_HEADSET,
+ /* address= */ "");
+ }
+
+ private AudioDeviceAttributes getHdmiDeviceAttributes() {
+ return new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_HDMI, /* address= */ "");
+ }
+
private void onPreferredDevicesForCapturePresetChanged(InputRouteManager inputRouteManager) {
final List<AudioDeviceAttributes> audioDeviceAttributesList =
new ArrayList<AudioDeviceAttributes>();
@@ -303,21 +316,47 @@ public class InputRouteManagerTest {
}
@Test
- public void onAudioDevicesAdded_shouldApplyDefaultSelectedDeviceToAllPresets() {
+ public void onAudioDevicesAdded_shouldActivateAddedDevice() {
final AudioManager audioManager = mock(AudioManager.class);
- AudioDeviceAttributes wiredHeadsetDeviceAttributes = getWiredHeadsetDeviceAttributes();
- when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES))
- .thenReturn(Collections.singletonList(wiredHeadsetDeviceAttributes));
-
InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()};
inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
- // Called twice, one after initiation, the other after onAudioDevicesAdded call.
- verify(audioManager, atLeast(2)).getDevicesForAttributes(INPUT_ATTRIBUTES);
+ // The only added wired headset will be activated.
for (@MediaRecorder.Source int preset : PRESETS) {
- verify(audioManager, atLeast(2))
- .setPreferredDeviceForCapturePreset(preset, wiredHeadsetDeviceAttributes);
+ verify(audioManager, atLeast(1))
+ .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes());
+ }
+ }
+
+ @Test
+ public void onAudioDevicesAdded_shouldActivateLastAddedDevice() {
+ final AudioManager audioManager = mock(AudioManager.class);
+ InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+ AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockUsbHeadsetInfo()};
+ inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+ // When adding multiple valid input devices, the last added device (usb headset in this
+ // case) will be activated.
+ for (@MediaRecorder.Source int preset : PRESETS) {
+ verify(audioManager, never())
+ .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes());
+ verify(audioManager, atLeast(1))
+ .setPreferredDeviceForCapturePreset(preset, getUsbHeadsetDeviceAttributes());
+ }
+ }
+
+ @Test
+ public void onAudioDevicesAdded_doNotActivateInvalidAddedDevice() {
+ final AudioManager audioManager = mock(AudioManager.class);
+ InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+ AudioDeviceInfo[] devices = {mockHdmiInfo()};
+ inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+ // Do not activate since HDMI is not a valid input device.
+ for (@MediaRecorder.Source int preset : PRESETS) {
+ verify(audioManager, never())
+ .setPreferredDeviceForCapturePreset(preset, getHdmiDeviceAttributes());
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
index c136644e0959..388af61c6273 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
@@ -23,12 +23,10 @@ import android.content.Context
import android.content.Intent
import android.database.ContentObserver
import android.os.Parcelable
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings.Global
import androidx.test.filters.SmallTest
-import com.android.settingslib.flags.Flags
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModesBackend
@@ -93,26 +91,7 @@ class ZenModeRepositoryTest {
)
}
- @DisableFlags(Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
- @Test
- fun consolidatedPolicyChanges_repositoryEmits_flagsOff() {
- testScope.runTest {
- val values = mutableListOf<NotificationManager.Policy?>()
- `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1)
- underTest.consolidatedNotificationPolicy
- .onEach { values.add(it) }
- .launchIn(backgroundScope)
- runCurrent()
-
- `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2)
- triggerIntent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
- runCurrent()
-
- assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder()
- }
- }
-
- @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
@Test
fun consolidatedPolicyChanges_repositoryEmits_flagsOn() {
testScope.runTest {
@@ -131,7 +110,7 @@ class ZenModeRepositoryTest {
}
}
- @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
@Test
fun consolidatedPolicyChanges_repositoryEmitsFromExtras() {
testScope.runTest {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 064198fc5e46..927a1c59cc76 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -284,5 +284,6 @@ public class SecureSettings {
Settings.Secure.MANDATORY_BIOMETRICS,
Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
Settings.Secure.ADVANCED_PROTECTION_MODE,
+ Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index c002a04d5b11..6d73ee27f076 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -332,6 +332,9 @@ public class SecureSettingsValidators {
VALIDATORS.put(
Secure.ACCESSIBILITY_QS_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS,
+ ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index fbce6ca07b3e..aca2c4ef2a49 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -16,6 +16,7 @@
package com.android.providers.settings;
+import static android.provider.DeviceConfig.DUMP_ARG_NAMESPACE;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
@@ -42,6 +43,7 @@ import android.provider.DeviceConfigShellCommandHandler;
import android.provider.Settings;
import android.provider.Settings.Config.SyncDisabledMode;
import android.provider.UpdatableDeviceConfigServiceReadiness;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
@@ -55,11 +57,13 @@ import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Receives shell commands from the command line related to device config flags, and dispatches them
@@ -80,6 +84,7 @@ public final class DeviceConfigService extends Binder {
final SettingsProvider mProvider;
private static final String TAG = "DeviceConfigService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public DeviceConfigService(SettingsProvider provider) {
mProvider = provider;
@@ -97,14 +102,35 @@ public final class DeviceConfigService extends Binder {
}
}
+ // TODO(b/364399200): add unit test
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ String filter = null;
if (android.provider.flags.Flags.dumpImprovements()) {
- pw.print("SyncDisabledForTests: ");
- MyShellCommand.getSyncDisabledForTests(pw, pw);
+ if (args.length > 0) {
+ switch (args[0]) {
+ case DUMP_ARG_NAMESPACE:
+ if (args.length < 2) {
+ throw new IllegalArgumentException("argument " + DUMP_ARG_NAMESPACE
+ + " requires an extra argument");
+ }
+ filter = args[1];
+ if (DEBUG) {
+ Slog.d(TAG, "dump(): setting filter as " + filter);
+ }
+ break;
+ default:
+ Slog.w(TAG, "dump(): ignoring invalid arguments: " + Arrays.toString(args));
+ break;
+ }
+ }
+ if (filter == null) {
+ pw.print("SyncDisabledForTests: ");
+ MyShellCommand.getSyncDisabledForTests(pw, pw);
- pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): ");
- pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+ pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): ");
+ pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+ }
pw.println("DeviceConfig provider: ");
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd)) {
@@ -117,8 +143,16 @@ public final class DeviceConfigService extends Binder {
IContentProvider iprovider = mProvider.getIContentProvider();
pw.println("DeviceConfig flags:");
+ Pattern lineFilter = filter == null ? null : Pattern.compile("^.*" + filter + ".*\\/.*$");
for (String line : MyShellCommand.listAll(iprovider)) {
- pw.println(line);
+ if (lineFilter == null || lineFilter.matcher(line).matches()) {
+ pw.println(line);
+ }
+ }
+
+ if (filter != null) {
+ // TODO(b/364399200): use filter to skip instead?
+ return;
}
ArrayList<String> missingFiles = new ArrayList<String>();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 2034f36c558b..fb0aaf8e5ae1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1823,6 +1823,9 @@ class SettingsProtoDumpUtil {
Settings.Secure.ACCESSIBILITY_QS_TARGETS,
SecureSettingsProto.Accessibility.QS_TARGETS);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS,
+ SecureSettingsProto.Accessibility.ACCESSIBILITY_KEY_GESTURE_TARGETS);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1919572ff571..7b6321d1cc7d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -263,6 +263,8 @@
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE" />
+ <uses-permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS" />
+ <uses-permission android:name="android.permission.START_VIBRATION_SESSIONS" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
@@ -715,6 +717,9 @@
<uses-permission android:name="android.permission.UWB_PRIVILEGED" />
<uses-permission android:name="android.permission.UWB_RANGING" />
+ <!-- Permission required for CTS test - CtsRangingTestCases -->
+ <uses-permission android:name="android.permission.RANGING" />
+
<!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
@@ -954,6 +959,13 @@
<!-- Permission required for CTS test - CtsTelephonyTestCases -->
<uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" />
+ <!-- Permission required for ExecutableMethodFileOffsetsTest -->
+ <uses-permission android:name="android.permission.DYNAMIC_INSTRUMENTATION" />
+
+ <!-- Permissions required for CTS test - SettingsPreferenceServiceClientTest -->
+ <uses-permission android:name="android.permission.READ_SYSTEM_PREFERENCES" />
+ <uses-permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index b4d81d6937ed..7c478ac78a29 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -353,7 +353,7 @@ public class BugreportProgressService extends Service {
public void onDestroy() {
mServiceHandler.getLooper().quit();
mScreenshotHandler.getLooper().quit();
- mBugreportSingleThreadExecutor.close();
+ mBugreportSingleThreadExecutor.shutdown();
super.onDestroy();
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a18b6c1b301a..bffda8bcae65 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -536,6 +536,8 @@ android_library {
"androidx.room_room-runtime",
"androidx.room_room-ktx",
"androidx.datastore_datastore-preferences",
+ "androidx.media3.media3-common",
+ "androidx.media3.media3-session",
"com.google.android.material_material",
"device_state_flags_lib",
"kotlinx_coroutines_android",
@@ -703,6 +705,8 @@ android_library {
"androidx.room_room-testing",
"androidx.room_room-ktx",
"androidx.datastore_datastore-preferences",
+ "androidx.media3.media3-common",
+ "androidx.media3.media3-session",
"device_state_flags_lib",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f0e1b437ec51..e0117368515b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1013,6 +1013,7 @@
android:autoRemoveFromRecents="true"
android:launchMode="singleTop"
android:showForAllUsers="true"
+ android:turnScreenOn="true"
android:exported="false">
</activity>
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index 2910bba71341..635a97ef8a47 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -87,7 +87,7 @@ immediately for any callbacks added.
There are a few places where CommandQueue is used as a bus to communicate
across sysui. Such as when StatusBar calls CommandQueue#recomputeDisableFlags.
-This is generally used a shortcut to directly trigger CommandQueue rather than
+This is generally used as a shortcut to directly trigger CommandQueue rather than
calling StatusManager and waiting for the call to come back to IStatusBar.
### [com.android.systemui.util.NotificationChannels](/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java)
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING b/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
index 1820f39bb180..1903d22c93cc 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
+++ b/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
@@ -1,8 +1,7 @@
{
- // TODO: b/324945360 - Re-enable on presubmit after fixing failures
"postsubmit": [
{
"name": "AccessibilityMenuServiceTests"
}
]
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-uk/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-uk/strings.xml
index ee02b4a0e33a..ba38023ed951 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-uk/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-uk/strings.xml
@@ -21,7 +21,7 @@
<string name="previous_button_content_description" msgid="840869171117765966">"Перейти на попередній екран"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Перейти на наступний екран"</string>
<string name="accessibility_menu_description" msgid="4458354794093858297">"За допомогою великого екранного меню функцій доступності можна блокувати пристрій, змінювати гучність і яскравість, робити знімки екрана й багато іншого."</string>
- <string name="accessibility_menu_summary" msgid="340071398148208130">"Керування пристроєм за допомогою великого меню"</string>
+ <string name="accessibility_menu_summary" msgid="340071398148208130">"Керувати пристроєм за допомогою великого меню"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Налаштування меню функцій доступності"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Великі кнопки"</string>
<string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Збільшити розмір кнопок у меню функцій доступності"</string>
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 52512464cb30..b5eba08c8f87 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -118,3 +118,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "floating_menu_hearing_device_status_icon"
+ namespace: "accessibility"
+ description: "Update hearing device icon in floating menu according to the connection status."
+ bug: "357882387"
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 5c5edb1d00ba..a4b8821383e0 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -16,6 +16,16 @@ flag {
}
flag {
+ name: "multiuser_wifi_picker_tracker_support"
+ namespace: "systemui"
+ description: "Adds WifiPickerTracker support for multiple users to support when HSUM is enabled."
+ bug: "371586248"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "udfps_view_performance"
namespace: "systemui"
description: "Decrease screen off blocking calls by waiting until the device is finished going to sleep before adding the udfps view."
@@ -26,6 +36,16 @@ flag {
}
flag {
+ name: "user_encrypted_source"
+ namespace: "systemui"
+ description: "Get rid of the local cache and rely on UserManager.isUserUnlocked directly to determine whether user CE storage is encrypted."
+ bug: "333656491"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "modes_ui_dialog_paging"
namespace: "systemui"
description: "Add pagination to the Modes dialog in quick settings."
@@ -247,7 +267,7 @@ flag {
flag {
name: "dual_shade"
namespace: "systemui"
- description: "Enables the BC25 Dual Shade (go/bc25-dual-shade-design)."
+ description: "Enables Dual Shade (go/dual-shade-design-doc)."
bug: "337259436"
}
@@ -404,6 +424,17 @@ flag {
}
flag {
+ name: "status_bar_auto_start_screen_record_chip"
+ namespace: "systemui"
+ description: "When screen recording, use the specified start time to update the screen record "
+ "chip state instead of waiting for an official 'recording started' signal"
+ bug: "366448907"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "status_bar_use_repos_for_call_chip"
namespace: "systemui"
description: "Use repositories as the source of truth for call notifications shown as a chip in"
@@ -468,6 +499,15 @@ flag {
}
flag {
+ name: "status_bar_notification_chips_test"
+ namespace: "systemui"
+ description: "Flag to enable certain features that let us test the status bar notification "
+ "chips with teamfooders. This flag should *never* be released to trunkfood or nextfood."
+ bug: "361346412"
+}
+
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
@@ -518,6 +558,13 @@ flag {
}
flag {
+ name: "lockscreen_custom_clocks"
+ namespace: "systemui"
+ description: "Enable lockscreen custom clocks"
+ bug: "378486437"
+}
+
+flag {
name: "faster_unlock_transition"
namespace: "systemui"
description: "Faster wallpaper unlock transition"
@@ -601,9 +648,9 @@ flag {
}
flag {
- name: "status_bar_simple_fragment"
+ name: "status_bar_root_modernization"
namespace: "systemui"
- description: "Feature flag for refactoring the collapsed status bar fragment"
+ description: "Feature flag for replacing the status bar fragment with a compose root"
bug: "364360986"
}
@@ -1320,16 +1367,6 @@ flag {
}
flag {
- name: "notification_pulsing_fix"
- namespace: "systemui"
- description: "Allow showing new pulsing notifications when the device is already pulsing."
- bug: "335560575"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "media_lockscreen_launch_animation"
namespace : "systemui"
description : "Enable the origin launch animation for UMO when opening on top of lockscreen."
@@ -1515,6 +1552,16 @@ flag {
}
flag {
+ namespace: "systemui"
+ name: "user_aware_settings_repositories"
+ description: "Provide user-aware versions of SecureSettingsRepository and SystemSettingsRepository in SystemUI modules (see doc linked from b/356099784)."
+ bug: "356099784"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notify_password_text_view_user_activity_in_background"
namespace: "systemui"
description: "Decide whether to notify the user activity in password text view, to power manager in the background thread."
@@ -1559,6 +1606,13 @@ flag {
}
flag {
+ name: "show_clipboard_indication"
+ namespace: "systemui"
+ description: "Show indication text under the clipboard overlay when copied something"
+ bug: "361199935"
+}
+
+flag {
name: "media_projection_dialog_behind_lockscreen"
namespace: "systemui"
description: "Ensure MediaProjection Dialog appears behind the lockscreen"
@@ -1640,6 +1694,16 @@ flag {
}
flag {
+ name: "show_toast_when_app_control_brightness"
+ namespace: "systemui"
+ description: "Showing the warning toast if the current running app window has controlled the brightness value."
+ bug: "363225340"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "home_controls_dream_hsum"
namespace: "systemui"
description: "Enables the home controls dream in HSUM"
@@ -1708,3 +1772,22 @@ flag {
bug: "365064144"
}
+flag {
+ name: "stoppable_fgs_system_app"
+ namespace: "systemui"
+ description: "System app with foreground service can opt in to be stoppable."
+ bug: "376564917"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "keyguard_transition_force_finish_on_screen_off"
+ namespace: "systemui"
+ description: "Forces KTF transitions to finish if the screen turns all the way off."
+ bug: "331636736"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 0b15d230dee2..cbe11a3f2f60 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -268,7 +268,8 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
// skip changes that we didn't wrap
if (!leashMap.containsKey(change.getLeash())) continue;
// Only make the update if we are closing Desktop tasks.
- if (change.getTaskInfo().isFreeform() && isClosingMode(change.getMode())) {
+ if (change.getTaskInfo() != null && change.getTaskInfo().isFreeform()
+ && isClosingMode(change.getMode())) {
startTransaction.setAlpha(leashMap.get(launcherChange.getLeash()), 0f);
return;
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 4cf264253bf8..fdb4871423c3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -20,6 +20,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
+import android.graphics.PointF
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.drawable.GradientDrawable
@@ -33,13 +34,13 @@ import android.view.ViewOverlay
import android.view.animation.Interpolator
import android.window.WindowAnimationState
import com.android.app.animation.Interpolators.LINEAR
-import com.android.app.animation.MathUtils.max
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.dynamicanimation.animation.SpringAnimation
import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
import java.util.concurrent.Executor
import kotlin.math.abs
+import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
@@ -91,6 +92,14 @@ class TransitionAnimator(
)
}
+ /**
+ * Similar to [getProgress] above, bug the delay and duration are expressed as percentages
+ * of the animation duration (between 0f and 1f).
+ */
+ internal fun getProgress(linearProgress: Float, delay: Float, duration: Float): Float {
+ return getProgressInternal(totalDuration = 1f, linearProgress, delay, duration)
+ }
+
private fun getProgressInternal(
totalDuration: Float,
linearProgress: Float,
@@ -262,10 +271,10 @@ class TransitionAnimator(
var centerY: Float,
var scale: Float = 0f,
- // Cached values.
- var previousCenterX: Float = -1f,
- var previousCenterY: Float = -1f,
- var previousScale: Float = -1f,
+ // Update flags (used to decide whether it's time to update the transition state).
+ var isCenterXUpdated: Boolean = false,
+ var isCenterYUpdated: Boolean = false,
+ var isScaleUpdated: Boolean = false,
// Completion flags.
var isCenterXDone: Boolean = false,
@@ -286,6 +295,7 @@ class TransitionAnimator(
override fun setValue(state: SpringState, value: Float) {
state.centerX = value
+ state.isCenterXUpdated = true
}
},
CENTER_Y {
@@ -295,6 +305,7 @@ class TransitionAnimator(
override fun setValue(state: SpringState, value: Float) {
state.centerY = value
+ state.isCenterYUpdated = true
}
},
SCALE {
@@ -304,6 +315,7 @@ class TransitionAnimator(
override fun setValue(state: SpringState, value: Float) {
state.scale = value
+ state.isScaleUpdated = true
}
};
@@ -444,8 +456,8 @@ class TransitionAnimator(
* punching a hole in the [transition container][Controller.transitionContainer]) iff [drawHole]
* is true.
*
- * If [useSpring] is true, a multi-spring animation will be used instead of the default
- * interpolators.
+ * If [startVelocity] (expressed in pixels per second) is not null, a multi-spring animation
+ * using it for the initial momentum will be used instead of the default interpolators.
*/
fun startAnimation(
controller: Controller,
@@ -453,9 +465,9 @@ class TransitionAnimator(
windowBackgroundColor: Int,
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
- useSpring: Boolean = false,
+ startVelocity: PointF? = null,
): Animation {
- if (!controller.isLaunching || useSpring) checkReturnAnimationFrameworkFlag()
+ if (!controller.isLaunching || startVelocity != null) checkReturnAnimationFrameworkFlag()
// We add an extra layer with the same color as the dialog/app splash screen background
// color, which is usually the same color of the app background. We first fade in this layer
@@ -474,7 +486,7 @@ class TransitionAnimator(
windowBackgroundLayer,
fadeWindowBackgroundLayer,
drawHole,
- useSpring,
+ startVelocity,
)
.apply { start() }
}
@@ -487,7 +499,7 @@ class TransitionAnimator(
windowBackgroundLayer: GradientDrawable,
fadeWindowBackgroundLayer: Boolean = true,
drawHole: Boolean = false,
- useSpring: Boolean = false,
+ startVelocity: PointF? = null,
): Animation {
val transitionContainer = controller.transitionContainer
val transitionContainerOverlay = transitionContainer.overlay
@@ -504,11 +516,12 @@ class TransitionAnimator(
openingWindowSyncView != null &&
openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl
- return if (useSpring && springTimings != null && springInterpolators != null) {
+ return if (startVelocity != null && springTimings != null && springInterpolators != null) {
createSpringAnimation(
controller,
startState,
endState,
+ startVelocity,
windowBackgroundLayer,
transitionContainer,
transitionContainerOverlay,
@@ -693,6 +706,7 @@ class TransitionAnimator(
controller: Controller,
startState: State,
endState: State,
+ startVelocity: PointF,
windowBackgroundLayer: GradientDrawable,
transitionContainer: View,
transitionContainerOverlay: ViewGroupOverlay,
@@ -721,19 +735,20 @@ class TransitionAnimator(
fun updateProgress(state: SpringState) {
if (
- (!state.isCenterXDone && state.centerX == state.previousCenterX) ||
- (!state.isCenterYDone && state.centerY == state.previousCenterY) ||
- (!state.isScaleDone && state.scale == state.previousScale)
+ !(state.isCenterXUpdated || state.isCenterXDone) ||
+ !(state.isCenterYUpdated || state.isCenterYDone) ||
+ !(state.isScaleUpdated || state.isScaleDone)
) {
// Because all three springs use the same update method, we only actually update
- // when all values have changed, avoiding two redundant calls per frame.
+ // when all properties have received their new value (which could be unchanged from
+ // the previous one), avoiding two redundant calls per frame.
return
}
- // Update the latest values for the check above.
- state.previousCenterX = state.centerX
- state.previousCenterY = state.centerY
- state.previousScale = state.scale
+ // Reset the update flags.
+ state.isCenterXUpdated = false
+ state.isCenterYUpdated = false
+ state.isScaleUpdated = false
// Current scale-based values, that will be used to find the new animation bounds.
val width =
@@ -829,6 +844,7 @@ class TransitionAnimator(
}
setStartValue(startState.centerX)
+ setStartVelocity(startVelocity.x)
setMinValue(min(startState.centerX, endState.centerX))
setMaxValue(max(startState.centerX, endState.centerX))
@@ -850,6 +866,7 @@ class TransitionAnimator(
}
setStartValue(startState.centerY)
+ setStartVelocity(startVelocity.y)
setMinValue(min(startState.centerY, endState.centerY))
setMaxValue(max(startState.centerY, endState.centerY))
@@ -1057,15 +1074,13 @@ class TransitionAnimator(
interpolators = springInterpolators!!
val timings = springTimings!!
fadeInProgress =
- getProgressInternal(
- totalDuration = 1f,
+ getProgress(
linearProgress,
timings.contentBeforeFadeOutDelay,
timings.contentBeforeFadeOutDuration,
)
fadeOutProgress =
- getProgressInternal(
- totalDuration = 1f,
+ getProgress(
linearProgress,
timings.contentAfterFadeInDelay,
timings.contentAfterFadeInDuration,
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index 00d905652987..300bdf2ffb01 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -48,7 +48,7 @@ class ViewHierarchyAnimator {
Bound.LEFT to createViewProperty(Bound.LEFT),
Bound.TOP to createViewProperty(Bound.TOP),
Bound.RIGHT to createViewProperty(Bound.RIGHT),
- Bound.BOTTOM to createViewProperty(Bound.BOTTOM)
+ Bound.BOTTOM to createViewProperty(Bound.BOTTOM),
)
private fun createViewProperty(bound: Bound): IntProperty<View> {
@@ -89,7 +89,7 @@ class ViewHierarchyAnimator {
interpolator: Interpolator = DEFAULT_INTERPOLATOR,
duration: Long = DEFAULT_DURATION,
animateChildren: Boolean = true,
- excludedViews: Set<View> = emptySet()
+ excludedViews: Set<View> = emptySet(),
): Boolean {
return animate(
rootView,
@@ -97,7 +97,7 @@ class ViewHierarchyAnimator {
duration,
ephemeral = false,
animateChildren = animateChildren,
- excludedViews = excludedViews
+ excludedViews = excludedViews,
)
}
@@ -111,7 +111,7 @@ class ViewHierarchyAnimator {
interpolator: Interpolator = DEFAULT_INTERPOLATOR,
duration: Long = DEFAULT_DURATION,
animateChildren: Boolean = true,
- excludedViews: Set<View> = emptySet()
+ excludedViews: Set<View> = emptySet(),
): Boolean {
return animate(
rootView,
@@ -119,7 +119,7 @@ class ViewHierarchyAnimator {
duration,
ephemeral = true,
animateChildren = animateChildren,
- excludedViews = excludedViews
+ excludedViews = excludedViews,
)
}
@@ -129,7 +129,7 @@ class ViewHierarchyAnimator {
duration: Long,
ephemeral: Boolean,
animateChildren: Boolean,
- excludedViews: Set<View> = emptySet()
+ excludedViews: Set<View> = emptySet(),
): Boolean {
if (
!occupiesSpace(
@@ -137,7 +137,7 @@ class ViewHierarchyAnimator {
rootView.left,
rootView.top,
rootView.right,
- rootView.bottom
+ rootView.bottom,
)
) {
return false
@@ -149,7 +149,7 @@ class ViewHierarchyAnimator {
listener,
recursive = true,
animateChildren = animateChildren,
- excludedViews = excludedViews
+ excludedViews = excludedViews,
)
return true
}
@@ -164,7 +164,7 @@ class ViewHierarchyAnimator {
private fun createUpdateListener(
interpolator: Interpolator,
duration: Long,
- ephemeral: Boolean
+ ephemeral: Boolean,
): View.OnLayoutChangeListener {
return createListener(interpolator, duration, ephemeral)
}
@@ -196,9 +196,9 @@ class ViewHierarchyAnimator {
*
* @param includeFadeIn true if the animator should also fade in the view and child views.
* @param fadeInInterpolator the interpolator to use when fading in the view. Unused if
- * [includeFadeIn] is false.
- * @param onAnimationEnd an optional runnable that will be run once the animation
- * finishes successfully. Will not be run if the animation is cancelled.
+ * [includeFadeIn] is false.
+ * @param onAnimationEnd an optional runnable that will be run once the animation finishes,
+ * regardless of whether the animation is cancelled or finishes successfully.
*/
@JvmOverloads
fun animateAddition(
@@ -217,7 +217,7 @@ class ViewHierarchyAnimator {
rootView.left,
rootView.top,
rootView.right,
- rootView.bottom
+ rootView.bottom,
)
) {
return false
@@ -241,7 +241,10 @@ class ViewHierarchyAnimator {
// First, fade in the container view
val containerDuration = duration / 6
createAndStartFadeInAnimator(
- rootView, containerDuration, startDelay = 0, interpolator = fadeInInterpolator
+ rootView,
+ containerDuration,
+ startDelay = 0,
+ interpolator = fadeInInterpolator,
)
// Then, fade in the child views
@@ -253,7 +256,7 @@ class ViewHierarchyAnimator {
childDuration,
// Wait until the container fades in before fading in the children
startDelay = containerDuration,
- interpolator = fadeInInterpolator
+ interpolator = fadeInInterpolator,
)
}
// For now, we don't recursively fade in additional sub views (e.g. grandchild
@@ -264,7 +267,7 @@ class ViewHierarchyAnimator {
rootView,
duration / 2,
startDelay = 0,
- interpolator = fadeInInterpolator
+ interpolator = fadeInInterpolator,
)
}
@@ -323,7 +326,7 @@ class ViewHierarchyAnimator {
previousLeft: Int,
previousTop: Int,
previousRight: Int,
- previousBottom: Int
+ previousBottom: Int,
) {
if (view == null) return
@@ -353,14 +356,14 @@ class ViewHierarchyAnimator {
startTop,
startRight,
startBottom,
- ignorePreviousValues
+ ignorePreviousValues,
)
val endValues =
mapOf(
Bound.LEFT to left,
Bound.TOP to top,
Bound.RIGHT to right,
- Bound.BOTTOM to bottom
+ Bound.BOTTOM to bottom,
)
val boundsToAnimate = mutableSetOf<Bound>()
@@ -396,8 +399,8 @@ class ViewHierarchyAnimator {
* added on the side(s) of the [destination], the translation of those margins can be
* included by specifying [includeMargins].
*
- * @param onAnimationEnd an optional runnable that will be run once the animation finishes
- * successfully. Will not be run if the animation is cancelled.
+ * @param onAnimationEnd an optional runnable that will be run once the animation finishes,
+ * regardless of whether the animation is cancelled or finishes successfully.
*/
@JvmOverloads
fun animateRemoval(
@@ -414,7 +417,7 @@ class ViewHierarchyAnimator {
rootView.left,
rootView.top,
rootView.right,
- rootView.bottom
+ rootView.bottom,
)
) {
return false
@@ -458,7 +461,7 @@ class ViewHierarchyAnimator {
Bound.LEFT to rootView.left,
Bound.TOP to rootView.top,
Bound.RIGHT to rootView.right,
- Bound.BOTTOM to rootView.bottom
+ Bound.BOTTOM to rootView.bottom,
)
val endValues =
processEndValuesForRemoval(
@@ -550,7 +553,7 @@ class ViewHierarchyAnimator {
destination: Hotspot,
endValues: Map<Bound, Int>,
interpolator: Interpolator,
- duration: Long
+ duration: Long,
) {
for (i in 0 until rootView.childCount) {
val child = rootView.getChildAt(i)
@@ -559,7 +562,7 @@ class ViewHierarchyAnimator {
Bound.LEFT to child.left,
Bound.TOP to child.top,
Bound.RIGHT to child.right,
- Bound.BOTTOM to child.bottom
+ Bound.BOTTOM to child.bottom,
)
val childEndValues =
processChildEndValuesForRemoval(
@@ -569,7 +572,7 @@ class ViewHierarchyAnimator {
child.right,
child.bottom,
endValues.getValue(Bound.RIGHT) - endValues.getValue(Bound.LEFT),
- endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP)
+ endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP),
)
val boundsToAnimate = mutableSetOf<Bound>()
@@ -587,7 +590,7 @@ class ViewHierarchyAnimator {
childEndValues,
interpolator,
duration,
- ephemeral = true
+ ephemeral = true,
)
}
}
@@ -601,7 +604,7 @@ class ViewHierarchyAnimator {
left: Int,
top: Int,
right: Int,
- bottom: Int
+ bottom: Int,
): Boolean {
return visibility != View.GONE && left != right && top != bottom
}
@@ -616,6 +619,7 @@ class ViewHierarchyAnimator {
* not newly introduced margins are included.
*
* Base case
+ *
* ```
* 1) origin=TOP
* x---------x x---------x x---------x x---------x x---------x
@@ -636,9 +640,11 @@ class ViewHierarchyAnimator {
* x-----x x-------x | |
* x---------x
* ```
+ *
* In case the start and end values differ in the direction of the origin, and
* [ignorePreviousValues] is false, the previous values are used and a translation is
* included in addition to the view expansion.
+ *
* ```
* origin=TOP_LEFT - (0,0,0,0) -> (30,30,70,70)
* x
@@ -660,7 +666,7 @@ class ViewHierarchyAnimator {
previousTop: Int,
previousRight: Int,
previousBottom: Int,
- ignorePreviousValues: Boolean
+ ignorePreviousValues: Boolean,
): Map<Bound, Int> {
val startLeft = if (ignorePreviousValues) newLeft else previousLeft
val startTop = if (ignorePreviousValues) newTop else previousTop
@@ -727,7 +733,7 @@ class ViewHierarchyAnimator {
Bound.LEFT to left,
Bound.TOP to top,
Bound.RIGHT to right,
- Bound.BOTTOM to bottom
+ Bound.BOTTOM to bottom,
)
}
@@ -777,18 +783,17 @@ class ViewHierarchyAnimator {
includeMargins: Boolean = false,
): Map<Bound, Int> {
val marginAdjustment =
- if (includeMargins &&
- (rootView.layoutParams is ViewGroup.MarginLayoutParams)) {
+ if (includeMargins && (rootView.layoutParams is ViewGroup.MarginLayoutParams)) {
val marginLp = rootView.layoutParams as ViewGroup.MarginLayoutParams
DimenHolder(
left = marginLp.leftMargin,
top = marginLp.topMargin,
right = marginLp.rightMargin,
- bottom = marginLp.bottomMargin
+ bottom = marginLp.bottomMargin,
)
- } else {
- DimenHolder(0, 0, 0, 0)
- }
+ } else {
+ DimenHolder(0, 0, 0, 0)
+ }
// These are the end values to use *if* this bound is part of the destination.
val endLeft = left - marginAdjustment.left
@@ -805,60 +810,69 @@ class ViewHierarchyAnimator {
// - If destination=BOTTOM_LEFT, then endBottom == endTop AND endLeft == endRight.
return when (destination) {
- Hotspot.TOP -> mapOf(
- Bound.TOP to endTop,
- Bound.BOTTOM to endTop,
- Bound.LEFT to left,
- Bound.RIGHT to right,
- )
- Hotspot.TOP_RIGHT -> mapOf(
- Bound.TOP to endTop,
- Bound.BOTTOM to endTop,
- Bound.RIGHT to endRight,
- Bound.LEFT to endRight,
- )
- Hotspot.RIGHT -> mapOf(
- Bound.RIGHT to endRight,
- Bound.LEFT to endRight,
- Bound.TOP to top,
- Bound.BOTTOM to bottom,
- )
- Hotspot.BOTTOM_RIGHT -> mapOf(
- Bound.BOTTOM to endBottom,
- Bound.TOP to endBottom,
- Bound.RIGHT to endRight,
- Bound.LEFT to endRight,
- )
- Hotspot.BOTTOM -> mapOf(
- Bound.BOTTOM to endBottom,
- Bound.TOP to endBottom,
- Bound.LEFT to left,
- Bound.RIGHT to right,
- )
- Hotspot.BOTTOM_LEFT -> mapOf(
- Bound.BOTTOM to endBottom,
- Bound.TOP to endBottom,
- Bound.LEFT to endLeft,
- Bound.RIGHT to endLeft,
- )
- Hotspot.LEFT -> mapOf(
- Bound.LEFT to endLeft,
- Bound.RIGHT to endLeft,
- Bound.TOP to top,
- Bound.BOTTOM to bottom,
- )
- Hotspot.TOP_LEFT -> mapOf(
- Bound.TOP to endTop,
- Bound.BOTTOM to endTop,
- Bound.LEFT to endLeft,
- Bound.RIGHT to endLeft,
- )
- Hotspot.CENTER -> mapOf(
- Bound.LEFT to (endLeft + endRight) / 2,
- Bound.RIGHT to (endLeft + endRight) / 2,
- Bound.TOP to (endTop + endBottom) / 2,
- Bound.BOTTOM to (endTop + endBottom) / 2,
- )
+ Hotspot.TOP ->
+ mapOf(
+ Bound.TOP to endTop,
+ Bound.BOTTOM to endTop,
+ Bound.LEFT to left,
+ Bound.RIGHT to right,
+ )
+ Hotspot.TOP_RIGHT ->
+ mapOf(
+ Bound.TOP to endTop,
+ Bound.BOTTOM to endTop,
+ Bound.RIGHT to endRight,
+ Bound.LEFT to endRight,
+ )
+ Hotspot.RIGHT ->
+ mapOf(
+ Bound.RIGHT to endRight,
+ Bound.LEFT to endRight,
+ Bound.TOP to top,
+ Bound.BOTTOM to bottom,
+ )
+ Hotspot.BOTTOM_RIGHT ->
+ mapOf(
+ Bound.BOTTOM to endBottom,
+ Bound.TOP to endBottom,
+ Bound.RIGHT to endRight,
+ Bound.LEFT to endRight,
+ )
+ Hotspot.BOTTOM ->
+ mapOf(
+ Bound.BOTTOM to endBottom,
+ Bound.TOP to endBottom,
+ Bound.LEFT to left,
+ Bound.RIGHT to right,
+ )
+ Hotspot.BOTTOM_LEFT ->
+ mapOf(
+ Bound.BOTTOM to endBottom,
+ Bound.TOP to endBottom,
+ Bound.LEFT to endLeft,
+ Bound.RIGHT to endLeft,
+ )
+ Hotspot.LEFT ->
+ mapOf(
+ Bound.LEFT to endLeft,
+ Bound.RIGHT to endLeft,
+ Bound.TOP to top,
+ Bound.BOTTOM to bottom,
+ )
+ Hotspot.TOP_LEFT ->
+ mapOf(
+ Bound.TOP to endTop,
+ Bound.BOTTOM to endTop,
+ Bound.LEFT to endLeft,
+ Bound.RIGHT to endLeft,
+ )
+ Hotspot.CENTER ->
+ mapOf(
+ Bound.LEFT to (endLeft + endRight) / 2,
+ Bound.RIGHT to (endLeft + endRight) / 2,
+ Bound.TOP to (endTop + endBottom) / 2,
+ Bound.BOTTOM to (endTop + endBottom) / 2,
+ )
}
}
@@ -887,7 +901,7 @@ class ViewHierarchyAnimator {
right: Int,
bottom: Int,
parentWidth: Int,
- parentHeight: Int
+ parentHeight: Int,
): Map<Bound, Int> {
val halfWidth = (right - left) / 2
val halfHeight = (bottom - top) / 2
@@ -945,7 +959,7 @@ class ViewHierarchyAnimator {
Bound.LEFT to endLeft,
Bound.TOP to endTop,
Bound.RIGHT to endRight,
- Bound.BOTTOM to endBottom
+ Bound.BOTTOM to endBottom,
)
}
@@ -954,7 +968,7 @@ class ViewHierarchyAnimator {
listener: View.OnLayoutChangeListener,
recursive: Boolean = false,
animateChildren: Boolean = true,
- excludedViews: Set<View> = emptySet()
+ excludedViews: Set<View> = emptySet(),
) {
if (excludedViews.contains(view)) return
@@ -973,7 +987,7 @@ class ViewHierarchyAnimator {
listener,
recursive = true,
animateChildren = animateChildren,
- excludedViews = excludedViews
+ excludedViews = excludedViews,
)
}
}
@@ -1027,7 +1041,7 @@ class ViewHierarchyAnimator {
PropertyValuesHolder.ofInt(
PROPERTIES[bound],
startValues.getValue(bound),
- endValues.getValue(bound)
+ endValues.getValue(bound),
)
)
}
@@ -1056,9 +1070,10 @@ class ViewHierarchyAnimator {
// listener.
recursivelyRemoveListener(view)
}
- if (!cancelled) {
- onAnimationEnd?.run()
- }
+ // Run the end runnable regardless of whether the animation was cancelled or
+ // not - this ensures critical actions (like removing a window) always occur
+ // (see b/344049884).
+ onAnimationEnd?.run()
}
override fun onAnimationCancel(animation: Animator) {
@@ -1077,17 +1092,19 @@ class ViewHierarchyAnimator {
view: View,
duration: Long,
startDelay: Long,
- interpolator: Interpolator
+ interpolator: Interpolator,
) {
val animator = ObjectAnimator.ofFloat(view, "alpha", 1f)
animator.startDelay = startDelay
animator.duration = duration
animator.interpolator = interpolator
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- view.setTag(R.id.tag_alpha_animator, null /* tag */)
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ view.setTag(R.id.tag_alpha_animator, null /* tag */)
+ }
}
- })
+ )
(view.getTag(R.id.tag_alpha_animator) as? ObjectAnimator)?.cancel()
view.setTag(R.id.tag_alpha_animator, animator)
@@ -1105,7 +1122,7 @@ class ViewHierarchyAnimator {
RIGHT,
BOTTOM_RIGHT,
BOTTOM,
- BOTTOM_LEFT
+ BOTTOM_LEFT,
}
private enum class Bound(val label: String, val overrideTag: Int) {
@@ -1147,14 +1164,10 @@ class ViewHierarchyAnimator {
};
abstract fun setValue(view: View, value: Int)
+
abstract fun getValue(view: View): Int
}
/** Simple data class to hold a set of dimens for left, top, right, bottom. */
- private data class DimenHolder(
- val left: Int,
- val top: Int,
- val right: Int,
- val bottom: Int,
- )
+ private data class DimenHolder(val left: Int, val top: Int, val right: Int, val bottom: Int)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 6d3039855077..87e9c427d695 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -42,7 +42,6 @@ import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.systemui.communal.shared.model.CommunalBackgroundType
@@ -124,7 +123,7 @@ val sceneTransitions = transitions {
}
timestampRange(
startMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_DELAY_MS,
- endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS
+ endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS,
) {
fade(Communal.Elements.Grid)
}
@@ -187,14 +186,13 @@ fun CommunalContainer(
) {
scene(
CommunalScenes.Blank,
- userActions =
- mapOf(Swipe(SwipeDirection.Start, fromSource = Edge.End) to CommunalScenes.Communal)
+ userActions = mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal),
) {
// This scene shows nothing only allowing for transitions to the communal scene.
Box(modifier = Modifier.fillMaxSize())
}
- val userActions = mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
+ val userActions = mapOf(Swipe.End to CommunalScenes.Blank)
scene(CommunalScenes.Communal, userActions = userActions) {
CommunalScene(
@@ -257,13 +255,9 @@ fun SceneScope.CommunalScene(
/** Default background of the hub, a single color */
@Composable
-private fun BoxScope.DefaultBackground(
- colors: CommunalColors,
-) {
+private fun BoxScope.DefaultBackground(colors: CommunalColors) {
val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
- Box(
- modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())),
- )
+ Box(modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())))
}
/** Experimental hub background, static linear gradient */
@@ -273,7 +267,7 @@ private fun BoxScope.StaticLinearGradient() {
Box(
Modifier.matchParentSize()
.background(
- Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer)),
+ Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer))
)
)
BackgroundTopScrim()
@@ -288,7 +282,7 @@ private fun BoxScope.AnimatedLinearGradient() {
.background(colors.primary)
.animatedRadialGradientBackground(
toColor = colors.primary,
- fromColor = colors.primaryContainer.copy(alpha = 0.6f)
+ fromColor = colors.primaryContainer.copy(alpha = 0.6f),
)
)
BackgroundTopScrim()
@@ -324,9 +318,9 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
durationMillis = ANIMATION_DURATION_MS,
easing = CubicBezierEasing(0.33f, 0f, 0.67f, 1f),
),
- repeatMode = RepeatMode.Reverse
+ repeatMode = RepeatMode.Reverse,
),
- label = "radial gradient center fraction"
+ label = "radial gradient center fraction",
)
// Offset to place the center of the gradients offscreen. This is applied to both the
@@ -337,16 +331,9 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
val gradientRadius = (size.width / 2) + offsetPx
val totalHeight = size.height + 2 * offsetPx
- val leftCenter =
- Offset(
- x = -offsetPx,
- y = totalHeight * centerFraction - offsetPx,
- )
+ val leftCenter = Offset(x = -offsetPx, y = totalHeight * centerFraction - offsetPx)
val rightCenter =
- Offset(
- x = offsetPx + size.width,
- y = totalHeight * (1f - centerFraction) - offsetPx,
- )
+ Offset(x = offsetPx + size.width, y = totalHeight * (1f - centerFraction) - offsetPx)
// Right gradient
drawCircle(
@@ -354,7 +341,7 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
Brush.radialGradient(
colors = listOf(fromColor, toColor),
center = rightCenter,
- radius = gradientRadius
+ radius = gradientRadius,
),
center = rightCenter,
radius = gradientRadius,
@@ -367,7 +354,7 @@ fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color):
Brush.radialGradient(
colors = listOf(fromColor, toColor),
center = leftCenter,
- radius = gradientRadius
+ radius = gradientRadius,
),
center = leftCenter,
radius = gradientRadius,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 9392b1afffa3..96e99b15363d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -66,13 +66,13 @@ constructor(
interactionHandler = interactionHandler,
dialogFactory = dialogFactory,
widgetSection = widgetSection,
- modifier = Modifier.element(Communal.Elements.Grid)
+ modifier = Modifier.element(Communal.Elements.Grid),
)
}
with(lockSection) {
LockIcon(
overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
- modifier = Modifier.element(Communal.Elements.LockIcon)
+ modifier = Modifier.element(Communal.Elements.LockIcon),
)
}
with(bottomAreaSection) {
@@ -80,17 +80,13 @@ constructor(
Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
)
}
- }
+ },
) { measurables, constraints ->
val communalGridMeasurable = measurables[0]
val lockIconMeasurable = measurables[1]
val bottomAreaMeasurable = measurables[2]
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
+ val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
val lockIconBounds =
@@ -109,14 +105,8 @@ constructor(
)
layout(constraints.maxWidth, constraints.maxHeight) {
- communalGridPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
+ communalGridPlaceable.place(x = 0, y = 0)
+ lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top)
bottomAreaPlaceable.place(
x = 0,
y = constraints.maxHeight - bottomAreaPlaceable.height,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 5e1ac1f30354..df1185cb1a6d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -807,7 +807,6 @@ private fun BoxScope.CommunalHubLazyGrid(
) {
ResizeableItemFrameViewModel()
}
-
if (viewModel.isEditMode && dragDropState != null) {
val isItemDragging = dragDropState.draggingItemKey == item.key
val outlineAlpha by
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 6e30575a684d..16002bc709fd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -59,7 +59,14 @@ internal constructor(
private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
- private val onResizeWidget: (id: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) -> Unit,
+ private val onResizeWidget:
+ (
+ id: Int,
+ spanY: Int,
+ widgetIdToRankMap: Map<Int, Int>,
+ componentName: ComponentName,
+ rank: Int,
+ ) -> Unit,
) {
var list = communalContent.toMutableStateList()
private set
@@ -105,7 +112,9 @@ internal constructor(
} else {
emptyMap()
}
- onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap)
+ val componentName = item.componentName
+ val rank = item.rank
+ onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap, componentName, rank)
}
/**
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
index f2f7c872b71c..0aef7f2c7063 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
@@ -36,6 +36,7 @@ import androidx.compose.ui.draganddrop.toAndroidDragEvent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
+import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.util.WidgetPickerIntentUtils
@@ -82,9 +83,7 @@ internal fun rememberDragAndDropTargetState(
* @see DragEvent
*/
@Composable
-internal fun Modifier.dragAndDropTarget(
- dragDropTargetState: DragAndDropTargetState,
-): Modifier {
+internal fun Modifier.dragAndDropTarget(dragDropTargetState: DragAndDropTargetState): Modifier {
val state by rememberUpdatedState(dragDropTargetState)
return this then
@@ -113,7 +112,7 @@ internal fun Modifier.dragAndDropTarget(
override fun onEnded(event: DragAndDropEvent) {
state.onEnded()
}
- }
+ },
)
}
@@ -146,6 +145,7 @@ internal class DragAndDropTargetState(
*/
private var placeHolder = CommunalContentModel.WidgetPlaceholder()
private var placeHolderIndex: Int? = null
+ private var previousTargetItemKey: Any? = null
internal val scrollChannel = Channel<Float>()
@@ -164,7 +164,19 @@ internal class DragAndDropTargetState(
.filter { item -> contentListState.isItemEditable(item.index) }
.firstItemAtOffset(dragOffset - contentOffset)
- if (targetItem != null) {
+ if (
+ targetItem != null &&
+ (!communalWidgetResizing() || targetItem.key != previousTargetItemKey)
+ ) {
+ if (communalWidgetResizing()) {
+ // Keep track of the previous target item, to avoid rapidly oscillating between
+ // items if the target item doesn't visually move as a result of the index change.
+ // In this case, even after the index changes, we'd still be colliding with the
+ // element, so it would be selected as the target item the next time this function
+ // runs again, which would trigger us to revert the index change we recently made.
+ previousTargetItemKey = targetItem.key
+ }
+
var scrollIndex: Int? = null
var scrollOffset: Int? = null
if (placeHolderIndex == state.firstVisibleItemIndex) {
@@ -183,8 +195,9 @@ internal class DragAndDropTargetState(
// this is needed to neutralize automatic keeping the first item first.
scope.launch { state.scrollToItem(scrollIndex, scrollOffset) }
}
- } else {
+ } else if (targetItem == null) {
computeAutoscroll(dragOffset).takeIf { it != 0f }?.let { scrollChannel.trySend(it) }
+ previousTargetItemKey = null
}
}
@@ -198,7 +211,7 @@ internal class DragAndDropTargetState(
contentListState.onSaveList(
newItemComponentName = componentName,
newItemUser = user,
- newItemIndex = dropIndex
+ newItemIndex = dropIndex,
)
return@let true
}
@@ -208,6 +221,7 @@ internal class DragAndDropTargetState(
fun onEnded() {
placeHolderIndex = null
+ previousTargetItemKey = null
contentListState.list.remove(placeHolder)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 6fc51e4d0f65..e78862e0e922 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -53,6 +53,7 @@ import com.android.systemui.notifications.ui.composable.ConstrainedNotificationS
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
@@ -84,7 +85,7 @@ constructor(
stackScrollLayout: NotificationStackScrollLayout,
sharedNotificationContainerBinder: SharedNotificationContainerBinder,
private val keyguardRootViewModel: KeyguardRootViewModel,
- private val configurationState: ConfigurationState,
+ @ShadeDisplayAware private val configurationState: ConfigurationState,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
@@ -127,7 +128,7 @@ constructor(
}
val burnIn = rememberBurnIn(clockInteractor)
AnimatedVisibility(
- visibleState = transitionState,
+ visibleState = transitionState,
enter = fadeIn(),
exit = fadeOut(),
modifier =
@@ -150,7 +151,7 @@ constructor(
)
}
}
- },
+ }
)
}
}
@@ -172,7 +173,7 @@ constructor(
areNotificationsVisible: Boolean,
isShadeLayoutWide: Boolean,
burnInParams: BurnInParameters?,
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
) {
if (!areNotificationsVisible) {
return
@@ -192,10 +193,7 @@ constructor(
if (burnInParams == null) {
it
} else {
- it.burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- )
+ it.burnInAware(viewModel = aodBurnInViewModel, params = burnInParams)
}
},
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 5b996704eb12..2af5ffaee7ed 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -18,23 +18,27 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.layout.layoutId
import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
+import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
-import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
@@ -53,6 +57,8 @@ constructor(
private val statusBarIconController: StatusBarIconController,
private val shadeSession: SaveableSession,
private val stackScrollView: Lazy<NotificationScrollView>,
+ private val clockSection: DefaultClockSection,
+ private val clockInteractor: KeyguardClockInteractor,
) : Overlay {
override val key = Overlays.NotificationsShade
@@ -80,13 +86,28 @@ constructor(
OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
Column {
- ExpandedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = tintedIconManagerFactory::create,
- createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
- statusBarIconController = statusBarIconController,
- modifier = Modifier.padding(horizontal = 16.dp),
- )
+ if (viewModel.showHeader) {
+ val burnIn = rememberBurnIn(clockInteractor)
+
+ CollapsedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController =
+ batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier =
+ Modifier.element(NotificationsShade.Elements.StatusBar)
+ .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
+ )
+
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ }
NotificationScrollingStack(
shadeSession = shadeSession,
@@ -110,3 +131,9 @@ constructor(
}
}
}
+
+object NotificationsShade {
+ object Elements {
+ val StatusBar = ElementKey("NotificationsShadeStatusBar")
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 2a91bd8b1d73..26c827a5417c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -43,6 +43,7 @@ import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.TileGrid
@@ -53,8 +54,11 @@ import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -67,6 +71,8 @@ constructor(
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
+ private val notificationStackScrollView: Lazy<NotificationScrollView>,
+ private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
) : Overlay {
override val key = Overlays.QuickSettingsShade
@@ -98,6 +104,14 @@ constructor(
ShadeBody(viewModel = viewModel.quickSettingsContainerViewModel)
}
+
+ SnoozeableHeadsUpNotificationSpace(
+ stackScrollView = notificationStackScrollView.get(),
+ viewModel =
+ rememberViewModel("QuickSettingsShadeOverlay") {
+ notificationsPlaceholderViewModelFactory.create()
+ },
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 67f412ed27ac..2cde6787f730 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -105,13 +105,17 @@ val SceneContainerTransitions = transitions {
// Overlay transitions
+ // TODO(b/376659778): Remove this transition once nested STLs are supported.
+ from(Scenes.Gone, to = Overlays.NotificationsShade) {
+ toNotificationsShadeTransition(translateClock = true)
+ }
to(Overlays.NotificationsShade) { toNotificationsShadeTransition() }
to(Overlays.QuickSettingsShade) { toQuickSettingsShadeTransition() }
from(Overlays.NotificationsShade, to = Overlays.QuickSettingsShade) {
notificationsShadeToQuickSettingsShadeTransition()
}
from(Scenes.Gone, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
- toNotificationsShadeTransition(durationScale = 0.9)
+ toNotificationsShadeTransition(translateClock = true, durationScale = 0.9)
}
from(Scenes.Gone, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
toQuickSettingsShadeTransition(durationScale = 0.9)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 23c4f12cb0ae..6bdb36331709 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -21,30 +21,42 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.notifications.ui.composable.NotificationsShade
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
-import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.toNotificationsShadeTransition(durationScale: Double = 1.0) {
+fun TransitionBuilder.toNotificationsShadeTransition(
+ translateClock: Boolean = false,
+ durationScale: Double = 1.0,
+) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
+ // Ensure the clock isn't clipped by the shade outline during the transition from lockscreen.
+ sharedElement(
+ ClockElementKeys.smallClockElementKey,
+ elevateInContent = Overlays.NotificationsShade,
+ )
scaleSize(OverlayShade.Elements.Panel, height = 0f)
+ // TODO(b/376659778): This is a temporary hack to have a shared element transition with the
+ // lockscreen clock. Remove once nested STLs are supported.
+ if (!translateClock) {
+ translate(ClockElementKeys.smallClockElementKey)
+ }
+ // Avoid translating the status bar with the shade panel.
+ translate(NotificationsShade.Elements.StatusBar)
+ // Slide in the shade panel from the top edge.
translate(OverlayShade.Elements.Panel, Edge.Top)
fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
-
- fractionRange(start = .5f) {
- fade(ShadeHeader.Elements.Clock)
- fade(ShadeHeader.Elements.ExpandedContent)
- fade(ShadeHeader.Elements.PrivacyChip)
- fade(Notifications.Elements.NotificationScrim)
- }
+ fractionRange(start = .5f) { fade(Notifications.Elements.NotificationScrim) }
}
private val DefaultDuration = 300.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
index e9b7335197b0..163f4b36f472 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -59,6 +59,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -98,19 +99,20 @@ fun SystemUIDialogFactory.create(
theme: Int = SystemUIDialog.DEFAULT_THEME,
dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
@GravityInt dialogGravity: Int? = null,
+ dialogDelegate: DialogDelegate<SystemUIDialog> =
+ object : DialogDelegate<SystemUIDialog> {
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ super.onCreate(dialog, savedInstanceState)
+ dialogGravity?.let { dialog.window?.setGravity(it) }
+ }
+ },
content: @Composable (SystemUIDialog) -> Unit,
): ComponentSystemUIDialog {
return create(
context = context,
theme = theme,
dismissOnDeviceLock = dismissOnDeviceLock,
- delegate =
- object : DialogDelegate<SystemUIDialog> {
- override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
- super.onCreate(dialog, savedInstanceState)
- dialogGravity?.let { dialog.window?.setGravity(it) }
- }
- },
+ delegate = dialogDelegate,
content = content,
)
}
@@ -285,9 +287,12 @@ private fun DragHandle(dialog: Dialog) {
Surface(
modifier =
Modifier.padding(top = 16.dp, bottom = 6.dp)
- .semantics { contentDescription = dragHandleContentDescription }
+ .semantics {
+ contentDescription = dragHandleContentDescription
+ hideFromAccessibility()
+ }
.clickable { dialog.dismiss() },
- color = MaterialTheme.colorScheme.outlineVariant,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
shape = MaterialTheme.shapes.extraLarge,
) {
Box(Modifier.size(width = 32.dp, height = 4.dp))
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 7872ffad6cec..c33d655fe52b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -25,7 +25,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.content.Content
-import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.OnStopScope
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
@@ -36,12 +35,11 @@ internal typealias SuspendedValue<T> = suspend () -> T
internal interface DraggableHandler {
/**
- * Start a drag in the given [startedPosition], with the given [overSlop] and number of
- * [pointersDown].
+ * Start a drag with the given [pointersDown] and [overSlop].
*
* The returned [DragController] should be used to continue or stop the drag.
*/
- fun onDragStarted(startedPosition: Offset?, overSlop: Float, pointersDown: Int): DragController
+ fun onDragStarted(pointersDown: PointersInfo.PointersDown?, overSlop: Float): DragController
}
/**
@@ -96,7 +94,7 @@ internal class DraggableHandlerImpl(
* Note: if this returns true, then [onDragStarted] will be called with overSlop equal to 0f,
* indicating that the transition should be intercepted.
*/
- internal fun shouldImmediatelyIntercept(startedPosition: Offset?): Boolean {
+ internal fun shouldImmediatelyIntercept(pointersDown: PointersInfo.PointersDown?): Boolean {
// We don't intercept the touch if we are not currently driving the transition.
val dragController = dragController
if (dragController?.isDrivingTransition != true) {
@@ -107,7 +105,7 @@ internal class DraggableHandlerImpl(
// Only intercept the current transition if one of the 2 swipes results is also a transition
// between the same pair of contents.
- val swipes = computeSwipes(startedPosition, pointersDown = 1)
+ val swipes = computeSwipes(pointersDown)
val fromContent = layoutImpl.content(swipeAnimation.currentContent)
val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent)
val currentScene = layoutImpl.state.currentScene
@@ -125,9 +123,8 @@ internal class DraggableHandlerImpl(
}
override fun onDragStarted(
- startedPosition: Offset?,
+ pointersDown: PointersInfo.PointersDown?,
overSlop: Float,
- pointersDown: Int,
): DragController {
if (overSlop == 0f) {
val oldDragController = dragController
@@ -153,7 +150,7 @@ internal class DraggableHandlerImpl(
return updateDragController(swipes, swipeAnimation)
}
- val swipes = computeSwipes(startedPosition, pointersDown)
+ val swipes = computeSwipes(pointersDown)
val fromContent = layoutImpl.contentForUserActions()
swipes.updateSwipesResults(fromContent)
@@ -190,8 +187,7 @@ internal class DraggableHandlerImpl(
return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation)
}
- internal fun resolveSwipeSource(startedPosition: Offset?): SwipeSource.Resolved? {
- if (startedPosition == null) return null
+ internal fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
return layoutImpl.swipeSourceDetector.source(
layoutSize = layoutImpl.lastSize,
position = startedPosition.round(),
@@ -200,57 +196,44 @@ internal class DraggableHandlerImpl(
)
}
- internal fun resolveSwipe(
- pointersDown: Int,
- fromSource: SwipeSource.Resolved?,
- isUpOrLeft: Boolean,
- ): Swipe.Resolved {
- return Swipe.Resolved(
- direction =
- when (orientation) {
- Orientation.Horizontal ->
- if (isUpOrLeft) {
- SwipeDirection.Resolved.Left
- } else {
- SwipeDirection.Resolved.Right
- }
-
- Orientation.Vertical ->
- if (isUpOrLeft) {
- SwipeDirection.Resolved.Up
- } else {
- SwipeDirection.Resolved.Down
- }
- },
- pointerCount = pointersDown,
- fromSource = fromSource,
+ private fun computeSwipes(pointersDown: PointersInfo.PointersDown?): Swipes {
+ val fromSource = pointersDown?.let { resolveSwipeSource(it.startedPosition) }
+ return Swipes(
+ upOrLeft = resolveSwipe(orientation, isUpOrLeft = true, pointersDown, fromSource),
+ downOrRight = resolveSwipe(orientation, isUpOrLeft = false, pointersDown, fromSource),
)
}
+}
- private fun computeSwipes(startedPosition: Offset?, pointersDown: Int): Swipes {
- val fromSource = resolveSwipeSource(startedPosition)
- val upOrLeft = resolveSwipe(pointersDown, fromSource, isUpOrLeft = true)
- val downOrRight = resolveSwipe(pointersDown, fromSource, isUpOrLeft = false)
- return if (fromSource == null) {
- Swipes(
- upOrLeft = null,
- downOrRight = null,
- upOrLeftNoSource = upOrLeft,
- downOrRightNoSource = downOrRight,
- )
- } else {
- Swipes(
- upOrLeft = upOrLeft,
- downOrRight = downOrRight,
- upOrLeftNoSource = upOrLeft.copy(fromSource = null),
- downOrRightNoSource = downOrRight.copy(fromSource = null),
- )
- }
- }
+private fun resolveSwipe(
+ orientation: Orientation,
+ isUpOrLeft: Boolean,
+ pointersDown: PointersInfo.PointersDown?,
+ fromSource: SwipeSource.Resolved?,
+): Swipe.Resolved {
+ return Swipe.Resolved(
+ direction =
+ when (orientation) {
+ Orientation.Horizontal ->
+ if (isUpOrLeft) {
+ SwipeDirection.Resolved.Left
+ } else {
+ SwipeDirection.Resolved.Right
+ }
- companion object {
- private const val TAG = "DraggableHandlerImpl"
- }
+ Orientation.Vertical ->
+ if (isUpOrLeft) {
+ SwipeDirection.Resolved.Up
+ } else {
+ SwipeDirection.Resolved.Down
+ }
+ },
+ // If the number of pointers is not specified, 1 is assumed.
+ pointerCount = pointersDown?.count ?: 1,
+ // Resolves the pointer type only if all pointers are of the same type.
+ pointersType = pointersDown?.countByType?.keys?.singleOrNull(),
+ fromSource = fromSource,
+ )
}
/** @param swipes The [Swipes] associated to the current gesture. */
@@ -498,24 +481,14 @@ private class DragControllerImpl(
}
/** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */
-internal class Swipes(
- val upOrLeft: Swipe.Resolved?,
- val downOrRight: Swipe.Resolved?,
- val upOrLeftNoSource: Swipe.Resolved?,
- val downOrRightNoSource: Swipe.Resolved?,
-) {
+internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resolved) {
/** The [UserActionResult] associated to up and down swipes. */
var upOrLeftResult: UserActionResult? = null
var downOrRightResult: UserActionResult? = null
fun computeSwipesResults(fromContent: Content): Pair<UserActionResult?, UserActionResult?> {
- val userActions = fromContent.userActions
- fun result(swipe: Swipe.Resolved?): UserActionResult? {
- return userActions[swipe ?: return null]
- }
-
- val upOrLeftResult = result(upOrLeft) ?: result(upOrLeftNoSource)
- val downOrRightResult = result(downOrRight) ?: result(downOrRightNoSource)
+ val upOrLeftResult = fromContent.findActionResultBestMatch(swipe = upOrLeft)
+ val downOrRightResult = fromContent.findActionResultBestMatch(swipe = downOrRight)
return upOrLeftResult to downOrRightResult
}
@@ -569,49 +542,15 @@ internal class NestedScrollHandlerImpl(
val connection: PriorityNestedScrollConnection = nestedScrollConnection()
- private fun PointersInfo.resolveSwipe(isUpOrLeft: Boolean): Swipe.Resolved {
- return draggableHandler.resolveSwipe(
- pointersDown = pointersDown,
- fromSource = draggableHandler.resolveSwipeSource(startedPosition),
- isUpOrLeft = isUpOrLeft,
- )
- }
-
private fun nestedScrollConnection(): PriorityNestedScrollConnection {
// If we performed a long gesture before entering priority mode, we would have to avoid
// moving on to the next scene.
var canChangeScene = false
- var _lastPointersInfo: PointersInfo? = null
- fun pointersInfo(): PointersInfo {
- return checkNotNull(_lastPointersInfo) {
- "PointersInfo should be initialized before the transition begins."
- }
- }
+ var lastPointersDown: PointersInfo.PointersDown? = null
- fun hasNextScene(amount: Float): Boolean {
- val transitionState = layoutState.transitionState
- val scene = transitionState.currentScene
- val fromScene = layoutImpl.scene(scene)
- val resolvedSwipe =
- when {
- amount < 0f -> pointersInfo().resolveSwipe(isUpOrLeft = true)
- amount > 0f -> pointersInfo().resolveSwipe(isUpOrLeft = false)
- else -> null
- }
- val nextScene =
- resolvedSwipe?.let {
- fromScene.userActions[it]
- ?: if (it.fromSource != null) {
- fromScene.userActions[it.copy(fromSource = null)]
- } else null
- }
- if (nextScene != null) return true
-
- if (transitionState !is TransitionState.Idle) return false
-
- val overscrollSpec = layoutImpl.state.transitions.overscrollSpec(scene, orientation)
- return overscrollSpec != null
+ fun shouldEnableSwipes(): Boolean {
+ return layoutImpl.contentForUserActions().shouldEnableSwipes(orientation)
}
var isIntercepting = false
@@ -619,13 +558,24 @@ internal class NestedScrollHandlerImpl(
return PriorityNestedScrollConnection(
orientation = orientation,
canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
+ val pointersDown: PointersInfo.PointersDown? =
+ when (val info = pointersInfoOwner.pointersInfo()) {
+ PointersInfo.MouseWheel -> {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection false
+ }
+
+ is PointersInfo.PointersDown -> info
+ null -> null
+ }
+
canChangeScene =
if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
val canInterceptSwipeTransition =
canChangeScene &&
offsetAvailable != 0f &&
- draggableHandler.shouldImmediatelyIntercept(startedPosition = null)
+ draggableHandler.shouldImmediatelyIntercept(pointersDown)
if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
val threshold = layoutImpl.transitionInterceptionThreshold
@@ -636,13 +586,7 @@ internal class NestedScrollHandlerImpl(
return@PriorityNestedScrollConnection false
}
- val pointersInfo = pointersInfoOwner.pointersInfo()
-
- if (pointersInfo.isMouseWheel) {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
- _lastPointersInfo = pointersInfo
+ lastPointersDown = pointersDown
// If the current swipe transition is *not* closed to 0f or 1f, then we want the
// scroll events to intercept the current transition to continue the scene
@@ -661,28 +605,33 @@ internal class NestedScrollHandlerImpl(
val isZeroOffset =
if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
- val pointersInfo = pointersInfoOwner.pointersInfo()
- if (pointersInfo.isMouseWheel) {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
- _lastPointersInfo = pointersInfo
+ val pointersDown: PointersInfo.PointersDown? =
+ when (val info = pointersInfoOwner.pointersInfo()) {
+ PointersInfo.MouseWheel -> {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection false
+ }
+
+ is PointersInfo.PointersDown -> info
+ null -> null
+ }
+ lastPointersDown = pointersDown
val canStart =
when (behavior) {
NestedScrollBehavior.EdgeNoPreview -> {
canChangeScene = isZeroOffset
- isZeroOffset && hasNextScene(offsetAvailable)
+ isZeroOffset && shouldEnableSwipes()
}
NestedScrollBehavior.EdgeWithPreview -> {
canChangeScene = isZeroOffset
- hasNextScene(offsetAvailable)
+ shouldEnableSwipes()
}
NestedScrollBehavior.EdgeAlways -> {
canChangeScene = true
- hasNextScene(offsetAvailable)
+ shouldEnableSwipes()
}
}
@@ -703,14 +652,19 @@ internal class NestedScrollHandlerImpl(
// We could start an overscroll animation
canChangeScene = false
- val pointersInfo = pointersInfoOwner.pointersInfo()
- if (pointersInfo.isMouseWheel) {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
- _lastPointersInfo = pointersInfo
+ val pointersDown: PointersInfo.PointersDown? =
+ when (val info = pointersInfoOwner.pointersInfo()) {
+ PointersInfo.MouseWheel -> {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection false
+ }
+
+ is PointersInfo.PointersDown -> info
+ null -> null
+ }
+ lastPointersDown = pointersDown
- val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
+ val canStart = behavior.canStartOnPostFling && shouldEnableSwipes()
if (canStart) {
isIntercepting = false
}
@@ -718,12 +672,10 @@ internal class NestedScrollHandlerImpl(
canStart
},
onStart = { firstScroll ->
- val pointersInfo = pointersInfo()
scrollController(
dragController =
draggableHandler.onDragStarted(
- pointersDown = pointersInfo.pointersDown,
- startedPosition = pointersInfo.startedPosition,
+ pointersDown = lastPointersDown,
overSlop = if (isIntercepting) 0f else firstScroll,
),
canChangeScene = canChangeScene,
@@ -741,8 +693,7 @@ private fun scrollController(
): ScrollController {
return object : ScrollController {
override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
- val pointersInfo = pointersInfoOwner.pointersInfo()
- if (pointersInfo.isMouseWheel) {
+ if (pointersInfoOwner.pointersInfo() == PointersInfo.MouseWheel) {
// Do not support mouse wheel interactions
return 0f
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 63c5d7aed3e1..d976e8e62f3c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -52,6 +52,7 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.SharedElementTransformation
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.drawInContainer
import com.android.compose.ui.util.lerp
@@ -187,6 +188,7 @@ private fun Modifier.maybeElevateInContent(
state.transformationSpec
.transformations(key, content.key)
.shared
+ ?.transformation
?.elevateInContent == content.key &&
isSharedElement(stateByContent, state) &&
isSharedElementEnabled(key, state) &&
@@ -691,8 +693,8 @@ private fun prepareInterruption(
val fromState = updateStateInContent(transition.fromContent)
val toState = updateStateInContent(transition.toContent)
- reconcileStates(element, previousTransition)
- reconcileStates(element, transition)
+ val previousUniqueState = reconcileStates(element, previousTransition, previousState = null)
+ reconcileStates(element, transition, previousState = previousUniqueState)
// Remove the interruption values to all contents but the content(s) where the element will be
// placed, to make sure that interruption deltas are computed only right after this interruption
@@ -719,12 +721,32 @@ private fun prepareInterruption(
/**
* Reconcile the state of [element] in the formContent and toContent of [transition] so that the
* values before interruption have their expected values, taking shared transitions into account.
+ *
+ * @return the unique state this element had during [transition], `null` if it had multiple
+ * different states (i.e. the shared animation was disabled).
*/
-private fun reconcileStates(element: Element, transition: TransitionState.Transition) {
- val fromContentState = element.stateByContent[transition.fromContent] ?: return
- val toContentState = element.stateByContent[transition.toContent] ?: return
+private fun reconcileStates(
+ element: Element,
+ transition: TransitionState.Transition,
+ previousState: Element.State?,
+): Element.State? {
+ fun reconcileWithPreviousState(state: Element.State) {
+ if (previousState != null && state.offsetBeforeInterruption == Offset.Unspecified) {
+ state.updateValuesBeforeInterruption(previousState)
+ }
+ }
+
+ val fromContentState = element.stateByContent[transition.fromContent]
+ val toContentState = element.stateByContent[transition.toContent]
+
+ if (fromContentState == null || toContentState == null) {
+ return (fromContentState ?: toContentState)
+ ?.also { reconcileWithPreviousState(it) }
+ ?.takeIf { it.offsetBeforeInterruption != Offset.Unspecified }
+ }
+
if (!isSharedElementEnabled(element.key, transition)) {
- return
+ return null
}
if (
@@ -733,13 +755,19 @@ private fun reconcileStates(element: Element, transition: TransitionState.Transi
) {
// Element is shared and placed in fromContent only.
toContentState.updateValuesBeforeInterruption(fromContentState)
- } else if (
+ return fromContentState
+ }
+
+ if (
toContentState.offsetBeforeInterruption != Offset.Unspecified &&
fromContentState.offsetBeforeInterruption == Offset.Unspecified
) {
// Element is shared and placed in toContent only.
fromContentState.updateValuesBeforeInterruption(toContentState)
+ return toContentState
}
+
+ return null
}
private fun Element.State.selfUpdateValuesBeforeInterruption() {
@@ -901,7 +929,7 @@ private fun shouldPlaceElement(
}
val sharedTransformation = sharedElementTransformation(element.key, transition)
- if (sharedTransformation?.enabled == false) {
+ if (sharedTransformation?.transformation?.enabled == false) {
return true
}
@@ -954,13 +982,13 @@ private fun isSharedElementEnabled(
element: ElementKey,
transition: TransitionState.Transition,
): Boolean {
- return sharedElementTransformation(element, transition)?.enabled ?: true
+ return sharedElementTransformation(element, transition)?.transformation?.enabled ?: true
}
internal fun sharedElementTransformation(
element: ElementKey,
transition: TransitionState.Transition,
-): SharedElementTransformation? {
+): TransformationWithRange<SharedElementTransformation>? {
val transformationSpec = transition.transformationSpec
val sharedInFromContent =
transformationSpec.transformations(element, transition.fromContent).shared
@@ -1244,7 +1272,7 @@ private inline fun <T> computeValue(
element: Element,
transition: TransitionState.Transition?,
contentValue: (Element.State) -> T,
- transformation: (ElementTransformations) -> PropertyTransformation<T>?,
+ transformation: (ElementTransformations) -> TransformationWithRange<PropertyTransformation<T>>?,
currentValue: () -> T,
isSpecified: (T) -> Boolean,
lerp: (T, T, Float) -> T,
@@ -1280,7 +1308,7 @@ private inline fun <T> computeValue(
checkNotNull(if (currentContent == toContent) toState else fromState)
val idleValue = contentValue(overscrollState)
val targetValue =
- with(propertySpec) {
+ with(propertySpec.transformation) {
layoutImpl.propertyTransformationScope.transform(
currentContent,
element.key,
@@ -1375,7 +1403,7 @@ private inline fun <T> computeValue(
val idleValue = contentValue(contentState)
val isEntering = content == toContent
val previewTargetValue =
- with(previewTransformation) {
+ with(previewTransformation.transformation) {
layoutImpl.propertyTransformationScope.transform(
content,
element.key,
@@ -1386,7 +1414,7 @@ private inline fun <T> computeValue(
val targetValueOrNull =
transformation?.let { transformation ->
- with(transformation) {
+ with(transformation.transformation) {
layoutImpl.propertyTransformationScope.transform(
content,
element.key,
@@ -1461,7 +1489,7 @@ private inline fun <T> computeValue(
val idleValue = contentValue(contentState)
val targetValue =
- with(transformation) {
+ with(transformation.transformation) {
layoutImpl.propertyTransformationScope.transform(
content,
element.key,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 8613f6da0f62..160326792f81 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -33,6 +33,7 @@ import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.changedToDown
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
@@ -52,6 +53,7 @@ import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastFilter
import androidx.compose.ui.util.fastFirstOrNull
+import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastSumBy
import com.android.compose.ui.util.SpaceVectorConverter
import kotlin.coroutines.cancellation.CancellationException
@@ -78,8 +80,8 @@ import kotlinx.coroutines.launch
@Stable
internal fun Modifier.multiPointerDraggable(
orientation: Orientation,
- startDragImmediately: (startedPosition: Offset) -> Boolean,
- onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
+ onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
onFirstPointerDown: () -> Unit = {},
swipeDetector: SwipeDetector = DefaultSwipeDetector,
dispatcher: NestedScrollDispatcher,
@@ -97,9 +99,9 @@ internal fun Modifier.multiPointerDraggable(
private data class MultiPointerDraggableElement(
private val orientation: Orientation,
- private val startDragImmediately: (startedPosition: Offset) -> Boolean,
+ private val startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
private val onDragStarted:
- (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
private val onFirstPointerDown: () -> Unit,
private val swipeDetector: SwipeDetector,
private val dispatcher: NestedScrollDispatcher,
@@ -125,9 +127,8 @@ private data class MultiPointerDraggableElement(
internal class MultiPointerDraggableNode(
orientation: Orientation,
- var startDragImmediately: (startedPosition: Offset) -> Boolean,
- var onDragStarted:
- (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ var startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
+ var onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
var onFirstPointerDown: () -> Unit,
swipeDetector: SwipeDetector = DefaultSwipeDetector,
private val dispatcher: NestedScrollDispatcher,
@@ -183,17 +184,29 @@ internal class MultiPointerDraggableNode(
pointerInput.onPointerEvent(pointerEvent, pass, bounds)
}
+ private var lastPointerEvent: PointerEvent? = null
private var startedPosition: Offset? = null
- private var pointersDown: Int = 0
- private var isMouseWheel: Boolean = false
+ private var countPointersDown: Int = 0
- internal fun pointersInfo(): PointersInfo {
- return PointersInfo(
- // This may be null, i.e. when the user uses TalkBack
+ internal fun pointersInfo(): PointersInfo? {
+ // This may be null, i.e. when the user uses TalkBack
+ val lastPointerEvent = lastPointerEvent ?: return null
+
+ if (lastPointerEvent.type == PointerEventType.Scroll) return PointersInfo.MouseWheel
+
+ val startedPosition = startedPosition ?: return null
+
+ return PointersInfo.PointersDown(
startedPosition = startedPosition,
- // We could have 0 pointers during fling or for other reasons.
- pointersDown = pointersDown.coerceAtLeast(1),
- isMouseWheel = isMouseWheel,
+ count = countPointersDown,
+ countByType =
+ buildMap {
+ lastPointerEvent.changes.fastForEach { change ->
+ if (!change.pressed) return@fastForEach
+ val newValue = (get(change.type) ?: 0) + 1
+ put(change.type, newValue)
+ }
+ },
)
}
@@ -212,12 +225,12 @@ internal class MultiPointerDraggableNode(
if (pointerEvent.type == PointerEventType.Enter) continue
val changes = pointerEvent.changes
- pointersDown = changes.countDown()
- isMouseWheel = pointerEvent.type == PointerEventType.Scroll
+ lastPointerEvent = pointerEvent
+ countPointersDown = changes.countDown()
when {
// There are no more pointers down.
- pointersDown == 0 -> {
+ countPointersDown == 0 -> {
startedPosition = null
// In case of multiple events with 0 pointers down (not pressed) we may have
@@ -285,8 +298,8 @@ internal class MultiPointerDraggableNode(
detectDragGestures(
orientation = orientation,
startDragImmediately = startDragImmediately,
- onDragStart = { startedPosition, overSlop, pointersDown ->
- onDragStarted(startedPosition, overSlop, pointersDown)
+ onDragStart = { pointersDown, overSlop ->
+ onDragStarted(pointersDown, overSlop)
},
onDrag = { controller, amount ->
dispatchScrollEvents(
@@ -435,9 +448,8 @@ internal class MultiPointerDraggableNode(
*/
private suspend fun AwaitPointerEventScope.detectDragGestures(
orientation: Orientation,
- startDragImmediately: (startedPosition: Offset) -> Boolean,
- onDragStart:
- (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
+ onDragStart: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
onDrag: (controller: DragController, dragAmount: Float) -> Unit,
onDragEnd: (controller: DragController) -> Unit,
onDragCancel: (controller: DragController) -> Unit,
@@ -462,8 +474,14 @@ internal class MultiPointerDraggableNode(
.first()
var overSlop = 0f
+ var lastPointersDown: PointersInfo.PointersDown =
+ checkNotNull(pointersInfo()) {
+ "We should have pointers down, last event: $currentEvent"
+ }
+ as PointersInfo.PointersDown
+
val drag =
- if (startDragImmediately(consumablePointer.position)) {
+ if (startDragImmediately(lastPointersDown)) {
consumablePointer.consume()
consumablePointer
} else {
@@ -488,14 +506,19 @@ internal class MultiPointerDraggableNode(
consumablePointer.id,
onSlopReached,
)
- }
+ } ?: return
+ lastPointersDown =
+ checkNotNull(pointersInfo()) {
+ "We should have pointers down, last event: $currentEvent"
+ }
+ as PointersInfo.PointersDown
// Make sure that overSlop is not 0f. This can happen when the user drags by exactly
// the touch slop. However, the overSlop we pass to onDragStarted() is used to
// compute the direction we are dragging in, so overSlop should never be 0f unless
// we intercept an ongoing swipe transition (i.e. startDragImmediately() returned
// true).
- if (drag != null && overSlop == 0f) {
+ if (overSlop == 0f) {
val delta = (drag.position - consumablePointer.position).toFloat()
check(delta != 0f) { "delta is equal to 0" }
overSlop = delta.sign
@@ -503,49 +526,38 @@ internal class MultiPointerDraggableNode(
drag
}
- if (drag != null) {
- val controller =
- onDragStart(
- // The startedPosition is the starting position when a gesture begins (when the
- // first pointer touches the screen), not the point where we begin dragging.
- // For example, this could be different if one of our children intercepts the
- // gesture first and then we do.
- requireNotNull(startedPosition),
- overSlop,
- pointersDown,
+ val controller = onDragStart(lastPointersDown, overSlop)
+
+ val successful: Boolean
+ try {
+ onDrag(controller, overSlop)
+
+ successful =
+ drag(
+ initialPointerId = drag.id,
+ hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
+ onDrag = {
+ onDrag(controller, it.positionChange().toFloat())
+ it.consume()
+ },
+ onIgnoredEvent = {
+ // We are still dragging an object, but this event is not of interest to the
+ // caller.
+ // This event will not trigger the onDrag event, but we will consume the
+ // event to prevent another pointerInput from interrupting the current
+ // gesture just because the event was ignored.
+ it.consume()
+ },
)
+ } catch (t: Throwable) {
+ onDragCancel(controller)
+ throw t
+ }
- val successful: Boolean
- try {
- onDrag(controller, overSlop)
-
- successful =
- drag(
- initialPointerId = drag.id,
- hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
- onDrag = {
- onDrag(controller, it.positionChange().toFloat())
- it.consume()
- },
- onIgnoredEvent = {
- // We are still dragging an object, but this event is not of interest to
- // the caller.
- // This event will not trigger the onDrag event, but we will consume the
- // event to prevent another pointerInput from interrupting the current
- // gesture just because the event was ignored.
- it.consume()
- },
- )
- } catch (t: Throwable) {
- onDragCancel(controller)
- throw t
- }
-
- if (successful) {
- onDragEnd(controller)
- } else {
- onDragCancel(controller)
- }
+ if (successful) {
+ onDragEnd(controller)
+ } else {
+ onDragCancel(controller)
}
}
@@ -655,11 +667,40 @@ internal class MultiPointerDraggableNode(
}
internal fun interface PointersInfoOwner {
- fun pointersInfo(): PointersInfo
+ /**
+ * Provides information about the pointers interacting with this composable.
+ *
+ * @return A [PointersInfo] object containing details about the pointers, including the starting
+ * position and the number of pointers down, or `null` if there are no pointers down.
+ */
+ fun pointersInfo(): PointersInfo?
}
-internal data class PointersInfo(
- val startedPosition: Offset?,
- val pointersDown: Int,
- val isMouseWheel: Boolean,
-)
+internal sealed interface PointersInfo {
+ /**
+ * Holds information about pointer interactions within a composable.
+ *
+ * This class stores details such as the starting position of a gesture, the number of pointers
+ * down, and whether the last pointer event was a mouse wheel scroll.
+ *
+ * @param startedPosition The starting position of the gesture. This is the position where the
+ * first pointer touched the screen, not necessarily the point where dragging begins. This may
+ * be different from the initial touch position if a child composable intercepts the gesture
+ * before this one.
+ * @param count The number of pointers currently down.
+ * @param countByType Provide a map of pointer types to the count of pointers of that type
+ * currently down/pressed.
+ */
+ data class PointersDown(
+ val startedPosition: Offset,
+ val count: Int,
+ val countByType: Map<PointerType, Int>,
+ ) : PointersInfo {
+ init {
+ check(count > 0) { "We should have at least 1 pointer down, $count instead" }
+ }
+ }
+
+ /** Indicates whether the last pointer event was a mouse wheel scroll. */
+ data object MouseWheel : PointersInfo
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index 077927dfe0a2..5bf77ae9b23c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -233,6 +233,12 @@ sealed interface ObservableTransitionState {
(to == null || this.toContent == to)
}
+ fun isTransitioningSets(from: Set<ContentKey>? = null, to: Set<ContentKey>? = null): Boolean {
+ return this is Transition &&
+ (from == null || from.contains(this.fromContent)) &&
+ (to == null || to.contains(this.toContent))
+ }
+
/** Whether we are transitioning from [content] to [other], or from [other] to [content]. */
fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean {
return isTransitioning(from = content, to = other) ||
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 504240390674..dbf7d7b29834 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -27,6 +27,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Density
@@ -404,9 +405,11 @@ data object Back : UserAction() {
}
/** The user swiped on the container. */
-data class Swipe(
+data class Swipe
+private constructor(
val direction: SwipeDirection,
val pointerCount: Int = 1,
+ val pointersType: PointerType? = null,
val fromSource: SwipeSource? = null,
) : UserAction() {
companion object {
@@ -416,12 +419,49 @@ data class Swipe(
val Down = Swipe(SwipeDirection.Down)
val Start = Swipe(SwipeDirection.Start)
val End = Swipe(SwipeDirection.End)
+
+ fun Left(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Left, pointerCount, pointersType, fromSource)
+
+ fun Up(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Up, pointerCount, pointersType, fromSource)
+
+ fun Right(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Right, pointerCount, pointersType, fromSource)
+
+ fun Down(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Down, pointerCount, pointersType, fromSource)
+
+ fun Start(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.Start, pointerCount, pointersType, fromSource)
+
+ fun End(
+ pointerCount: Int = 1,
+ pointersType: PointerType? = null,
+ fromSource: SwipeSource? = null,
+ ) = Swipe(SwipeDirection.End, pointerCount, pointersType, fromSource)
}
override fun resolve(layoutDirection: LayoutDirection): UserAction.Resolved {
return Resolved(
direction = direction.resolve(layoutDirection),
pointerCount = pointerCount,
+ pointersType = pointersType,
fromSource = fromSource?.resolve(layoutDirection),
)
}
@@ -431,6 +471,7 @@ data class Swipe(
val direction: SwipeDirection.Resolved,
val pointerCount: Int,
val fromSource: SwipeSource.Resolved?,
+ val pointersType: PointerType?,
) : UserAction.Resolved()
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index e1e2411da080..72b29ee8848a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -25,17 +25,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
-import androidx.compose.ui.util.fastFilter
import androidx.compose.ui.util.fastForEach
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.SharedElementTransformation
-import com.android.compose.animation.scene.transition.link.LinkedTransition
-import com.android.compose.animation.scene.transition.link.StateLink
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
/**
@@ -236,7 +232,6 @@ fun MutableSceneTransitionLayoutState(
canShowOverlay: (OverlayKey) -> Boolean = { true },
canHideOverlay: (OverlayKey) -> Boolean = { true },
canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
- stateLinks: List<StateLink> = emptyList(),
): MutableSceneTransitionLayoutState {
return MutableSceneTransitionLayoutStateImpl(
initialScene,
@@ -246,7 +241,6 @@ fun MutableSceneTransitionLayoutState(
canShowOverlay,
canHideOverlay,
canReplaceOverlay,
- stateLinks,
)
}
@@ -258,10 +252,7 @@ internal class MutableSceneTransitionLayoutStateImpl(
internal val canChangeScene: (SceneKey) -> Boolean = { true },
internal val canShowOverlay: (OverlayKey) -> Boolean = { true },
internal val canHideOverlay: (OverlayKey) -> Boolean = { true },
- internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ ->
- true
- },
- private val stateLinks: List<StateLink> = emptyList(),
+ internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
) : MutableSceneTransitionLayoutState {
private val creationThread: Thread = Thread.currentThread()
@@ -364,20 +355,11 @@ internal class MutableSceneTransitionLayoutStateImpl(
checkThread()
try {
- // Keep a reference to the previous transition (if any).
- val previousTransition = currentTransition
-
// Start the transition.
startTransitionInternal(transition, chain)
- // Handle transition links.
- previousTransition?.let { cancelActiveTransitionLinks(it) }
- if (stateLinks.isNotEmpty()) {
- coroutineScope { setupTransitionLinks(transition) }
- }
-
// Run the transition until it is finished.
- transition.run()
+ transition.runInternal()
} finally {
finishTransition(transition)
}
@@ -471,42 +453,6 @@ internal class MutableSceneTransitionLayoutStateImpl(
)
}
- private fun cancelActiveTransitionLinks(transition: TransitionState.Transition) {
- transition.activeTransitionLinks.forEach { (link, linkedTransition) ->
- link.target.finishTransition(linkedTransition)
- }
- transition.activeTransitionLinks.clear()
- }
-
- private fun CoroutineScope.setupTransitionLinks(transition: TransitionState.Transition) {
- stateLinks.fastForEach { stateLink ->
- val matchingLinks =
- stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) }
- if (matchingLinks.isEmpty()) return@fastForEach
- if (matchingLinks.size > 1) error("More than one link matched.")
-
- val targetCurrentScene = stateLink.target.transitionState.currentScene
- val matchingLink = matchingLinks[0]
-
- if (!matchingLink.targetIsInValidState(targetCurrentScene)) return@fastForEach
-
- val linkedTransition =
- LinkedTransition(
- originalTransition = transition,
- fromScene = targetCurrentScene,
- toScene = matchingLink.targetTo,
- key = matchingLink.targetTransitionKey,
- )
-
- // Start with UNDISPATCHED so that startTransition is called directly and the new linked
- // transition is observable directly.
- launch(start = CoroutineStart.UNDISPATCHED) {
- stateLink.target.startTransition(linkedTransition)
- }
- transition.activeTransitionLinks[stateLink] = linkedTransition
- }
- }
-
/**
* Notify that [transition] was finished and that it settled to its
* [currentScene][TransitionState.currentScene]. This will do nothing if [transition] was
@@ -535,9 +481,6 @@ internal class MutableSceneTransitionLayoutStateImpl(
// Mark this transition as finished.
finishedTransitions.add(transition)
- // Finish all linked transitions.
- finishActiveTransitionLinks(transition)
-
// Keep a reference to the last transition, in case we remove all transitions and should
// settle to Idle.
val lastTransition = transitionStates.last()
@@ -584,13 +527,6 @@ internal class MutableSceneTransitionLayoutStateImpl(
transitionStates = listOf(TransitionState.Idle(scene, currentOverlays))
}
- private fun finishActiveTransitionLinks(transition: TransitionState.Transition) {
- for ((link, linkedTransition) in transition.activeTransitionLinks) {
- link.target.finishTransition(linkedTransition)
- }
- transition.activeTransitionLinks.clear()
- }
-
/**
* Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap
* to the closest scene.
@@ -764,7 +700,8 @@ internal class MutableSceneTransitionLayoutStateImpl(
return@fastForEach
}
- state.transformationSpec.transformations.fastForEach { transformation ->
+ state.transformationSpec.transformations.fastForEach { transformationWithRange ->
+ val transformation = transformationWithRange.transformation
if (
transformation is SharedElementTransformation &&
transformation.elevateInContent != null
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 8866fbfbf194..b083f79aebf5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -33,10 +33,10 @@ import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.OverscrollTranslate
import com.android.compose.animation.scene.transformation.PropertyTransformation
-import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.animation.scene.transformation.Translate
/** The transitions configuration of a [SceneTransitionLayout]. */
@@ -233,7 +233,7 @@ interface TransformationSpec {
val distance: UserActionDistance?
/** The list of [Transformation] applied to elements during this transition. */
- val transformations: List<Transformation>
+ val transformations: List<TransformationWithRange<*>>
companion object {
internal val Empty =
@@ -325,7 +325,7 @@ internal class TransformationSpecImpl(
override val progressSpec: AnimationSpec<Float>,
override val swipeSpec: SpringSpec<Float>?,
override val distance: UserActionDistance?,
- override val transformations: List<Transformation>,
+ override val transformations: List<TransformationWithRange<*>>,
) : TransformationSpec {
private val cache = mutableMapOf<ElementKey, MutableMap<ContentKey, ElementTransformations>>()
@@ -340,59 +340,65 @@ internal class TransformationSpecImpl(
element: ElementKey,
content: ContentKey,
): ElementTransformations {
- var shared: SharedElementTransformation? = null
- var offset: PropertyTransformation<Offset>? = null
- var size: PropertyTransformation<IntSize>? = null
- var drawScale: PropertyTransformation<Scale>? = null
- var alpha: PropertyTransformation<Float>? = null
-
- fun <T> onPropertyTransformation(
- root: PropertyTransformation<T>,
- current: PropertyTransformation<T> = root,
- ) {
- when (current) {
+ var shared: TransformationWithRange<SharedElementTransformation>? = null
+ var offset: TransformationWithRange<PropertyTransformation<Offset>>? = null
+ var size: TransformationWithRange<PropertyTransformation<IntSize>>? = null
+ var drawScale: TransformationWithRange<PropertyTransformation<Scale>>? = null
+ var alpha: TransformationWithRange<PropertyTransformation<Float>>? = null
+
+ transformations.fastForEach { transformationWithRange ->
+ val transformation = transformationWithRange.transformation
+ if (!transformation.matcher.matches(element, content)) {
+ return@fastForEach
+ }
+
+ when (transformation) {
+ is SharedElementTransformation -> {
+ throwIfNotNull(shared, element, name = "shared")
+ shared =
+ transformationWithRange
+ as TransformationWithRange<SharedElementTransformation>
+ }
is Translate,
is OverscrollTranslate,
is EdgeTranslate,
is AnchoredTranslate -> {
throwIfNotNull(offset, element, name = "offset")
- offset = root as PropertyTransformation<Offset>
+ offset =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<Offset>>
}
is ScaleSize,
is AnchoredSize -> {
throwIfNotNull(size, element, name = "size")
- size = root as PropertyTransformation<IntSize>
+ size =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<IntSize>>
}
is DrawScale -> {
throwIfNotNull(drawScale, element, name = "drawScale")
- drawScale = root as PropertyTransformation<Scale>
+ drawScale =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<Scale>>
}
is Fade -> {
throwIfNotNull(alpha, element, name = "alpha")
- alpha = root as PropertyTransformation<Float>
- }
- is RangedPropertyTransformation -> onPropertyTransformation(root, current.delegate)
- }
- }
-
- transformations.fastForEach { transformation ->
- if (!transformation.matcher.matches(element, content)) {
- return@fastForEach
- }
-
- when (transformation) {
- is SharedElementTransformation -> {
- throwIfNotNull(shared, element, name = "shared")
- shared = transformation
+ alpha =
+ transformationWithRange
+ as TransformationWithRange<PropertyTransformation<Float>>
}
- is PropertyTransformation<*> -> onPropertyTransformation(transformation)
+ else -> error("Unknown transformation: $transformation")
}
}
return ElementTransformations(shared, offset, size, drawScale, alpha)
}
- private fun throwIfNotNull(previous: Transformation?, element: ElementKey, name: String) {
+ private fun throwIfNotNull(
+ previous: TransformationWithRange<*>?,
+ element: ElementKey,
+ name: String,
+ ) {
if (previous != null) {
error("$element has multiple $name transformations")
}
@@ -401,9 +407,9 @@ internal class TransformationSpecImpl(
/** The transformations of an element during a transition. */
internal class ElementTransformations(
- val shared: SharedElementTransformation?,
- val offset: PropertyTransformation<Offset>?,
- val size: PropertyTransformation<IntSize>?,
- val drawScale: PropertyTransformation<Scale>?,
- val alpha: PropertyTransformation<Float>?,
+ val shared: TransformationWithRange<SharedElementTransformation>?,
+ val offset: TransformationWithRange<PropertyTransformation<Offset>>?,
+ val size: TransformationWithRange<PropertyTransformation<IntSize>>?,
+ val drawScale: TransformationWithRange<PropertyTransformation<Scale>>?,
+ val alpha: TransformationWithRange<PropertyTransformation<Float>>?,
)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index fdf01cce396b..a448ee49d944 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -19,7 +19,6 @@ package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
import androidx.compose.ui.input.pointer.PointerEvent
@@ -57,7 +56,7 @@ private fun DraggableHandlerImpl.contentForSwipes(): Content {
}
/** Whether swipe should be enabled in the given [orientation]. */
-private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
+internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
if (userActions.isEmpty()) {
return false
}
@@ -65,6 +64,52 @@ private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
return userActions.keys.any { it is Swipe.Resolved && it.direction.orientation == orientation }
}
+/**
+ * Finds the best matching [UserActionResult] for the given [swipe] within this [Content].
+ * Prioritizes actions with matching [Swipe.Resolved.fromSource].
+ *
+ * @param swipe The swipe to match against.
+ * @return The best matching [UserActionResult], or `null` if no match is found.
+ */
+internal fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? {
+ var bestPoints = Int.MIN_VALUE
+ var bestMatch: UserActionResult? = null
+ userActions.forEach { (actionSwipe, actionResult) ->
+ if (
+ actionSwipe !is Swipe.Resolved ||
+ // The direction must match.
+ actionSwipe.direction != swipe.direction ||
+ // The number of pointers down must match.
+ actionSwipe.pointerCount != swipe.pointerCount ||
+ // The action requires a specific fromSource.
+ (actionSwipe.fromSource != null && actionSwipe.fromSource != swipe.fromSource) ||
+ // The action requires a specific pointerType.
+ (actionSwipe.pointersType != null && actionSwipe.pointersType != swipe.pointersType)
+ ) {
+ // This action is not eligible.
+ return@forEach
+ }
+
+ val sameFromSource = actionSwipe.fromSource == swipe.fromSource
+ val samePointerType = actionSwipe.pointersType == swipe.pointersType
+ // Prioritize actions with a perfect match.
+ if (sameFromSource && samePointerType) {
+ return actionResult
+ }
+
+ var points = 0
+ if (sameFromSource) points++
+ if (samePointerType) points++
+
+ // Otherwise, keep track of the best eligible action.
+ if (points > bestPoints) {
+ bestPoints = points
+ bestMatch = actionResult
+ }
+ }
+ return bestMatch
+}
+
private data class SwipeToSceneElement(
val draggableHandler: DraggableHandlerImpl,
val swipeDetector: SwipeDetector,
@@ -155,10 +200,10 @@ private class SwipeToSceneNode(
override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()
- private fun startDragImmediately(startedPosition: Offset): Boolean {
+ private fun startDragImmediately(pointersDown: PointersInfo.PointersDown): Boolean {
// Immediately start the drag if the user can't swipe in the other direction and the gesture
// handler can intercept it.
- return !canOppositeSwipe() && draggableHandler.shouldImmediatelyIntercept(startedPosition)
+ return !canOppositeSwipe() && draggableHandler.shouldImmediatelyIntercept(pointersDown)
}
private fun canOppositeSwipe(): Boolean {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 269d91b02e7d..e461f9ccc295 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -34,12 +34,11 @@ import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.OverscrollTranslate
-import com.android.compose.animation.scene.transformation.PropertyTransformation
-import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.animation.scene.transformation.Translate
internal fun transitionsImpl(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -158,7 +157,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
}
internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder {
- val transformations = mutableListOf<Transformation>()
+ val transformations = mutableListOf<TransformationWithRange<*>>()
private var range: TransformationRange? = null
protected var reversed = false
override var distance: UserActionDistance? = null
@@ -174,19 +173,13 @@ internal abstract class BaseTransitionBuilderImpl : BaseTransitionBuilder {
range = null
}
- protected fun transformation(transformation: PropertyTransformation<*>) {
- val transformation =
- if (range != null) {
- RangedPropertyTransformation(transformation, range!!)
- } else {
- transformation
- }
-
+ protected fun transformation(transformation: Transformation) {
+ val transformationWithRange = TransformationWithRange(transformation, range)
transformations.add(
if (reversed) {
- transformation.reversed()
+ transformationWithRange.reversed()
} else {
- transformation
+ transformationWithRange
}
)
}
@@ -264,7 +257,7 @@ internal class TransitionBuilderImpl(override val transition: TransitionState.Tr
"(${transition.toContent.debugName})"
}
- transformations.add(SharedElementTransformation(matcher, enabled, elevateInContent))
+ transformation(SharedElementTransformation(matcher, enabled, elevateInContent))
}
override fun timestampRange(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index 7d29a68e3a8d..e3118d67b434 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -18,6 +18,7 @@ package com.android.compose.animation.scene.content.state
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Stable
@@ -34,8 +35,6 @@ import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransformationSpec
import com.android.compose.animation.scene.TransformationSpecImpl
import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.transition.link.LinkedTransition
-import com.android.compose.animation.scene.transition.link.StateLink
import kotlinx.coroutines.launch
/** The state associated to a [SceneTransitionLayout] at some specific point in time. */
@@ -280,8 +279,8 @@ sealed interface TransitionState {
*/
private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null
- /** The map of active links that connects this transition to other transitions. */
- internal val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+ /** Whether this transition was already started. */
+ private var wasStarted = false
init {
check(fromContent != toContent)
@@ -328,7 +327,7 @@ sealed interface TransitionState {
}
/** Run this transition and return once it is finished. */
- abstract suspend fun run()
+ protected abstract suspend fun run()
/**
* Freeze this transition state so that neither [currentScene] nor [currentOverlays] will
@@ -341,6 +340,13 @@ sealed interface TransitionState {
*/
abstract fun freezeAndAnimateToCurrentState()
+ internal suspend fun runInternal() {
+ check(!wasStarted) { "A Transition can be started only once." }
+ wasStarted = true
+
+ run()
+ }
+
internal fun updateOverscrollSpecs(
fromSpec: OverscrollSpecImpl?,
toSpec: OverscrollSpecImpl?,
@@ -376,7 +382,7 @@ sealed interface TransitionState {
val progressSpec =
spring(
stiffness = swipeSpec.stiffness,
- dampingRatio = swipeSpec.dampingRatio,
+ dampingRatio = Spring.DampingRatioNoBouncy,
visibilityThreshold = ProgressVisibilityThreshold,
)
animatable.animateTo(0f, progressSpec)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 5936d2595465..0ddeb7c7445f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -33,7 +33,7 @@ internal class AnchoredSize(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: IntSize,
+ idleValue: IntSize,
): IntSize {
fun anchorSizeIn(content: ContentKey): IntSize {
val size =
@@ -45,8 +45,8 @@ internal class AnchoredSize(
)
return IntSize(
- width = if (anchorWidth) size.width else value.width,
- height = if (anchorHeight) size.height else value.height,
+ width = if (anchorWidth) size.width else idleValue.width,
+ height = if (anchorHeight) size.height else idleValue.height,
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 0a59dfe515fc..47508b41633c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -31,7 +31,7 @@ internal class AnchoredTranslate(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Offset,
+ idleValue: Offset,
): Offset {
fun throwException(content: ContentKey?): Nothing {
throwMissingAnchorException(
@@ -51,9 +51,9 @@ internal class AnchoredTranslate(
val offset = anchorToOffset - anchorFromOffset
return if (content == transition.toContent) {
- Offset(value.x - offset.x, value.y - offset.y)
+ Offset(idleValue.x - offset.x, idleValue.y - offset.y)
} else {
- Offset(value.x + offset.x, value.y + offset.y)
+ Offset(idleValue.x + offset.x, idleValue.y + offset.y)
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 7223dad43a2e..8488ae5178b0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -33,12 +33,11 @@ internal class DrawScale(
private val scaleY: Float,
private val pivot: Offset = Offset.Unspecified,
) : PropertyTransformation<Scale> {
-
override fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Scale,
+ idleValue: Scale,
): Scale {
return Scale(scaleX, scaleY, pivot)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 4ae07c541011..884aae4b8b1a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -33,37 +33,37 @@ internal class EdgeTranslate(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Offset,
+ idleValue: Offset,
): Offset {
val sceneSize =
content.targetSize()
?: error("Content ${content.debugName} does not have a target size")
- val elementSize = element.targetSize(content) ?: return value
+ val elementSize = element.targetSize(content) ?: return idleValue
return when (edge.resolve(layoutDirection)) {
Edge.Resolved.Top ->
if (startsOutsideLayoutBounds) {
- Offset(value.x, -elementSize.height.toFloat())
+ Offset(idleValue.x, -elementSize.height.toFloat())
} else {
- Offset(value.x, 0f)
+ Offset(idleValue.x, 0f)
}
Edge.Resolved.Left ->
if (startsOutsideLayoutBounds) {
- Offset(-elementSize.width.toFloat(), value.y)
+ Offset(-elementSize.width.toFloat(), idleValue.y)
} else {
- Offset(0f, value.y)
+ Offset(0f, idleValue.y)
}
Edge.Resolved.Bottom ->
if (startsOutsideLayoutBounds) {
- Offset(value.x, sceneSize.height.toFloat())
+ Offset(idleValue.x, sceneSize.height.toFloat())
} else {
- Offset(value.x, (sceneSize.height - elementSize.height).toFloat())
+ Offset(idleValue.x, (sceneSize.height - elementSize.height).toFloat())
}
Edge.Resolved.Right ->
if (startsOutsideLayoutBounds) {
- Offset(sceneSize.width.toFloat(), value.y)
+ Offset(sceneSize.width.toFloat(), idleValue.y)
} else {
- Offset((sceneSize.width - elementSize.width).toFloat(), value.y)
+ Offset((sceneSize.width - elementSize.width).toFloat(), idleValue.y)
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index c11ec977fe2b..ef769e7d0c19 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -27,7 +27,7 @@ internal class Fade(override val matcher: ElementMatcher) : PropertyTransformati
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Float,
+ idleValue: Float,
): Float {
// Return the alpha value of [element] either when it starts fading in or when it finished
// fading out, which is `0` in both cases.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index a159a5b5b2bd..ef3654b65b0a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -36,11 +36,11 @@ internal class ScaleSize(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: IntSize,
+ idleValue: IntSize,
): IntSize {
return IntSize(
- width = (value.width * width).roundToInt(),
- height = (value.height * height).roundToInt(),
+ width = (idleValue.width * width).roundToInt(),
+ height = (idleValue.height * height).roundToInt(),
)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index d38067d9af38..74a3ead3fbd7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -36,14 +36,6 @@ sealed interface Transformation {
*/
val matcher: ElementMatcher
- /**
- * The range during which the transformation is applied. If it is `null`, then the
- * transformation will be applied throughout the whole scene transition.
- */
- // TODO(b/240432457): Move this back to PropertyTransformation.
- val range: TransformationRange?
- get() = null
-
/*
* Reverse this transformation. This is called when we use Transition(from = A, to = B) when
* animating from B to A and there is no Transition(from = B, to = A) defined.
@@ -66,14 +58,14 @@ interface PropertyTransformation<T> : Transformation {
* - the value at progress = 100% for elements that are leaving the layout (i.e. elements in the
* content we are transitioning from).
*
- * The returned value will be interpolated using the [transition] progress and [value], the
+ * The returned value will be interpolated using the [transition] progress and [idleValue], the
* value of the property when we are idle.
*/
fun PropertyTransformationScope.transform(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: T,
+ idleValue: T,
): T
}
@@ -82,20 +74,15 @@ interface PropertyTransformationScope : Density, ElementStateScope {
val layoutDirection: LayoutDirection
}
-/**
- * A [PropertyTransformation] associated to a range. This is a helper class so that normal
- * implementations of [PropertyTransformation] don't have to take care of reversing their range when
- * they are reversed.
- */
-internal class RangedPropertyTransformation<T>(
- val delegate: PropertyTransformation<T>,
- override val range: TransformationRange,
-) : PropertyTransformation<T> by delegate {
- override fun reversed(): Transformation {
- return RangedPropertyTransformation(
- delegate.reversed() as PropertyTransformation<T>,
- range.reversed(),
- )
+/** A pair consisting of a [transformation] and optional [range]. */
+class TransformationWithRange<out T : Transformation>(
+ val transformation: T,
+ val range: TransformationRange?,
+) {
+ fun reversed(): TransformationWithRange<T> {
+ if (range == null) return this
+
+ return TransformationWithRange(transformation = transformation, range = range.reversed())
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index af0a6edfa2fb..356ed9969458 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -35,9 +35,9 @@ internal class Translate(
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
- value: Offset,
+ idleValue: Offset,
): Offset {
- return Offset(value.x + x.toPx(), value.y + y.toPx())
+ return Offset(idleValue.x + x.toPx(), idleValue.y + y.toPx())
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
deleted file mode 100644
index 42ba9ba95e07..000000000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compose.animation.scene.transition.link
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.content.state.TransitionState
-
-/** A linked transition which is driven by a [originalTransition]. */
-internal class LinkedTransition(
- private val originalTransition: TransitionState.Transition,
- fromScene: SceneKey,
- toScene: SceneKey,
- override val key: TransitionKey? = null,
-) : TransitionState.Transition.ChangeScene(fromScene, toScene) {
-
- override val currentScene: SceneKey
- get() {
- return when (originalTransition.currentScene) {
- originalTransition.fromContent -> fromScene
- originalTransition.toContent -> toScene
- else -> error("Original currentScene is neither FromScene nor ToScene")
- }
- }
-
- override val isInitiatedByUserInput: Boolean
- get() = originalTransition.isInitiatedByUserInput
-
- override val isUserInputOngoing: Boolean
- get() = originalTransition.isUserInputOngoing
-
- override val progress: Float
- get() = originalTransition.progress
-
- override val progressVelocity: Float
- get() = originalTransition.progressVelocity
-
- override suspend fun run() {
- originalTransition.run()
- }
-
- override fun freezeAndAnimateToCurrentState() {
- originalTransition.freezeAndAnimateToCurrentState()
- }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
deleted file mode 100644
index 2aec5091c3e5..000000000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compose.animation.scene.transition.link
-
-import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.SceneTransitionLayoutState
-import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.content.state.TransitionState
-
-/** A link between a source (implicit) and [target] `SceneTransitionLayoutState`. */
-class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<TransitionLink>) {
-
- internal val target = target as MutableSceneTransitionLayoutStateImpl
-
- /**
- * Links two transitions (source and target) together.
- *
- * `null` can be passed to indicate that any SceneKey should match. e.g. passing `null`, `null`,
- * `null`, `SceneA` means that any transition at the source will trigger a transition in the
- * target to `SceneA` from any current scene.
- */
- class TransitionLink(
- val sourceFrom: ContentKey?,
- val sourceTo: ContentKey?,
- val targetFrom: SceneKey?,
- val targetTo: SceneKey,
- val targetTransitionKey: TransitionKey? = null,
- ) {
- init {
- if (
- (sourceFrom != null && sourceFrom == sourceTo) ||
- (targetFrom != null && targetFrom == targetTo)
- )
- error("From and To can't be the same")
- }
-
- internal fun isMatchingLink(transition: TransitionState.Transition): Boolean {
- return (sourceFrom == null || sourceFrom == transition.fromContent) &&
- (sourceTo == null || sourceTo == transition.toContent)
- }
-
- internal fun targetIsInValidState(targetCurrentContent: ContentKey): Boolean {
- return (targetFrom == null || targetFrom == targetCurrentContent) &&
- targetTo != targetCurrentContent
- }
- }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index e924ebfd2a8d..20a0b390a037 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -207,6 +207,9 @@ class PriorityNestedScrollConnection(
}
override suspend fun onPreFling(available: Velocity): Velocity {
+ // Note: This method may be called multiple times. Due to NestedScrollDispatcher, the order
+ // of method calls (pre/post scroll/fling) cannot be guaranteed.
+ if (isStopping) return Velocity.Zero
val controller = currentController ?: return Velocity.Zero
// If in priority mode and can stop on pre-fling phase, stop the scroll.
@@ -219,6 +222,9 @@ class PriorityNestedScrollConnection(
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ // Note: This method may be called multiple times. Due to NestedScrollDispatcher, the order
+ // of method calls (pre/post scroll/fling) cannot be guaranteed.
+ if (isStopping) return Velocity.Zero
val availableFloat = available.toFloat()
val controller = currentController
@@ -315,6 +321,7 @@ class PriorityNestedScrollConnection(
* @return The consumed velocity.
*/
suspend fun stop(velocity: Float): Velocity {
+ if (isStopping) return Velocity.Zero
val controller = requireController(isStopping = false)
return coroutineScope {
try {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index f24d93f0d79d..7e6f3a88fab1 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -23,6 +23,7 @@ import androidx.compose.material3.Text
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
+import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
@@ -51,6 +52,18 @@ import org.junit.runner.RunWith
private const val SCREEN_SIZE = 100f
private val LAYOUT_SIZE = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt())
+private fun pointersDown(
+ startedPosition: Offset = Offset.Zero,
+ pointersDown: Int = 1,
+ pointersDownByType: Map<PointerType, Int> = mapOf(PointerType.Touch to pointersDown),
+): PointersInfo.PointersDown {
+ return PointersInfo.PointersDown(
+ startedPosition = startedPosition,
+ count = pointersDown,
+ countByType = pointersDownByType,
+ )
+}
+
@RunWith(AndroidJUnit4::class)
class DraggableHandlerTest {
private class TestGestureScope(val testScope: MonotonicClockTestScope) {
@@ -86,10 +99,7 @@ class DraggableHandlerTest {
scene(
key = SceneC,
userActions =
- mapOf(
- Swipe.Up to SceneB,
- Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA,
- ),
+ mapOf(Swipe.Up to SceneB, Swipe.Up(fromSource = Edge.Bottom) to SceneA),
) {
Text("SceneC")
}
@@ -126,9 +136,7 @@ class DraggableHandlerTest {
val draggableHandler = layoutImpl.draggableHandler(Orientation.Vertical)
val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal)
- var pointerInfoOwner: () -> PointersInfo = {
- PointersInfo(startedPosition = Offset.Zero, pointersDown = 1, isMouseWheel = false)
- }
+ var pointerInfoOwner: () -> PointersInfo = { pointersDown() }
fun nestedScrollConnection(
nestedScrollBehavior: NestedScrollBehavior,
@@ -211,42 +219,34 @@ class DraggableHandlerTest {
}
fun onDragStarted(
- startedPosition: Offset = Offset.Zero,
+ pointersInfo: PointersInfo.PointersDown = pointersDown(),
overSlop: Float,
- pointersDown: Int = 1,
expectedConsumedOverSlop: Float = overSlop,
): DragController {
// overSlop should be 0f only if the drag gesture starts with startDragImmediately
if (overSlop == 0f) error("Consider using onDragStartedImmediately()")
return onDragStarted(
draggableHandler = draggableHandler,
- startedPosition = startedPosition,
+ pointersInfo = pointersInfo,
overSlop = overSlop,
- pointersDown = pointersDown,
expectedConsumedOverSlop = expectedConsumedOverSlop,
)
}
fun onDragStartedImmediately(
- startedPosition: Offset = Offset.Zero,
- pointersDown: Int = 1,
+ pointersInfo: PointersInfo.PointersDown = pointersDown()
): DragController {
- return onDragStarted(draggableHandler, startedPosition, overSlop = 0f, pointersDown)
+ return onDragStarted(draggableHandler, pointersInfo, overSlop = 0f)
}
fun onDragStarted(
draggableHandler: DraggableHandler,
- startedPosition: Offset = Offset.Zero,
+ pointersInfo: PointersInfo.PointersDown = pointersDown(),
overSlop: Float = 0f,
- pointersDown: Int = 1,
expectedConsumedOverSlop: Float = overSlop,
): DragController {
val dragController =
- draggableHandler.onDragStarted(
- startedPosition = startedPosition,
- overSlop = overSlop,
- pointersDown = pointersDown,
- )
+ draggableHandler.onDragStarted(pointersDown = pointersInfo, overSlop = overSlop)
// MultiPointerDraggable will always call onDelta with the initial overSlop right after
dragController.onDragDelta(pixels = overSlop, expectedConsumedOverSlop)
@@ -528,7 +528,8 @@ class DraggableHandlerTest {
mapOf(Swipe.Up to UserActionResult(SceneB, isIrreversible = true), Swipe.Down to SceneC)
val dragController =
onDragStarted(
- startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f),
+ pointersInfo =
+ pointersDown(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f)),
overSlop = up(fractionOfScreen = 0.2f),
)
assertTransition(
@@ -554,7 +555,7 @@ class DraggableHandlerTest {
// Start dragging from the bottom
onDragStarted(
- startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE),
+ pointersInfo = pointersDown(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE)),
overSlop = up(fractionOfScreen = 0.1f),
)
assertTransition(
@@ -1051,8 +1052,8 @@ class DraggableHandlerTest {
navigateToSceneC()
// Swipe up from the middle to transition to scene B.
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- onDragStarted(startedPosition = middle, overSlop = up(0.1f))
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
+ onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
assertTransition(
currentScene = SceneC,
fromScene = SceneC,
@@ -1067,7 +1068,7 @@ class DraggableHandlerTest {
// should intercept it. Because it is intercepted, the overSlop passed to onDragStarted()
// should be 0f.
assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
- onDragStartedImmediately(startedPosition = middle)
+ onDragStartedImmediately(pointersInfo = middle)
// We should have intercepted the transition, so the transition should be the same object.
assertTransition(
@@ -1083,9 +1084,9 @@ class DraggableHandlerTest {
// Start a new gesture from the bottom of the screen. Because swiping up from the bottom of
// C leads to scene A (and not B), the previous transitions is *not* intercepted and we
// instead animate from C to A.
- val bottom = Offset(SCREEN_SIZE / 2, SCREEN_SIZE)
+ val bottom = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2, SCREEN_SIZE))
assertThat(draggableHandler.shouldImmediatelyIntercept(bottom)).isFalse()
- onDragStarted(startedPosition = bottom, overSlop = up(0.1f))
+ onDragStarted(pointersInfo = bottom, overSlop = up(0.1f))
assertTransition(
currentScene = SceneC,
@@ -1102,8 +1103,8 @@ class DraggableHandlerTest {
navigateToSceneC()
// Swipe up from the middle to transition to scene B.
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- onDragStarted(startedPosition = middle, overSlop = up(0.1f))
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
+ onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
assertTransition(fromScene = SceneC, toScene = SceneB, isUserInputOngoing = true)
// The current transition can be intercepted.
@@ -1119,15 +1120,15 @@ class DraggableHandlerTest {
@Test
fun interruptedTransitionCanNotBeImmediatelyIntercepted() = runGestureTest {
- assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isFalse()
+ assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse()
onDragStarted(overSlop = up(0.1f))
- assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue()
+ assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue()
layoutState.startTransitionImmediately(
animationScope = testScope.backgroundScope,
transition(SceneA, SceneB),
)
- assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isFalse()
+ assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse()
}
@Test
@@ -1159,7 +1160,7 @@ class DraggableHandlerTest {
assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
// Intercept the transition and swipe down back to scene A.
- assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue()
+ assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue()
val dragController2 = onDragStartedImmediately()
// Block the transition when the user release their finger.
@@ -1203,9 +1204,7 @@ class DraggableHandlerTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
// Drag from the **top** of the screen
- pointerInfoOwner = {
- PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = false)
- }
+ pointerInfoOwner = { pointersDown() }
assertIdle(currentScene = SceneC)
nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
@@ -1222,20 +1221,14 @@ class DraggableHandlerTest {
advanceUntilIdle()
// Drag from the **bottom** of the screen
- pointerInfoOwner = {
- PointersInfo(
- startedPosition = Offset(0f, SCREEN_SIZE),
- pointersDown = 1,
- isMouseWheel = false,
- )
- }
+ pointerInfoOwner = { pointersDown(startedPosition = Offset(0f, SCREEN_SIZE)) }
assertIdle(currentScene = SceneC)
nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
assertTransition(
currentScene = SceneC,
fromScene = SceneC,
- // userAction: Swipe(SwipeDirection.Up, fromSource = Edge.Bottom) to SceneA
+ // userAction: Swipe.Up(fromSource = Edge.Bottom) to SceneA
toScene = SceneA,
progress = 0.1f,
)
@@ -1248,9 +1241,7 @@ class DraggableHandlerTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
// Use mouse wheel
- pointerInfoOwner = {
- PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = true)
- }
+ pointerInfoOwner = { PointersInfo.MouseWheel }
assertIdle(currentScene = SceneC)
nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
@@ -1260,8 +1251,8 @@ class DraggableHandlerTest {
@Test
fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest {
// Swipe up from the middle to transition to scene B.
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.1f))
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
assertTransition(fromScene = SceneA, toScene = SceneB, isUserInputOngoing = true)
dragController.onDragStoppedAnimateLater(velocity = 0f)
@@ -1274,10 +1265,10 @@ class DraggableHandlerTest {
layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) }
// Swipe up to scene B at progress = 200%.
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
val dragController =
onDragStarted(
- startedPosition = middle,
+ pointersInfo = middle,
overSlop = up(2f),
// Overscroll is disabled, it will scroll up to 100%
expectedConsumedOverSlop = up(1f),
@@ -1305,8 +1296,8 @@ class DraggableHandlerTest {
layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) }
// Swipe up to scene B at progress = 200%.
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.99f))
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.99f))
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.99f)
// Release the finger.
@@ -1351,9 +1342,9 @@ class DraggableHandlerTest {
overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
}
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.5f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.5f))
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneB)
@@ -1383,9 +1374,9 @@ class DraggableHandlerTest {
overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
}
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(startedPosition = middle, overSlop = down(0.5f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = down(0.5f))
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneC)
@@ -1414,9 +1405,9 @@ class DraggableHandlerTest {
overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
}
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(startedPosition = middle, overSlop = up(1.5f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1.5f))
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneB)
@@ -1446,9 +1437,9 @@ class DraggableHandlerTest {
overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
}
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- val dragController = onDragStarted(startedPosition = middle, overSlop = down(1.5f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1.5f))
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneC)
@@ -1480,8 +1471,8 @@ class DraggableHandlerTest {
mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB))
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- val dragController = onDragStarted(startedPosition = middle, overSlop = down(1f))
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1f))
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneB)
@@ -1513,8 +1504,8 @@ class DraggableHandlerTest {
mutableUserActionsA = mapOf(Swipe.Down to UserActionResult(SceneC))
- val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
- val dragController = onDragStarted(startedPosition = middle, overSlop = up(1f))
+ val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
+ val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1f))
val transition = assertThat(transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
assertThat(transition).hasToScene(SceneC)
@@ -1685,4 +1676,33 @@ class DraggableHandlerTest {
assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
}
+
+ @Test
+ fun replaceOverlayNestedScroll() = runGestureTest {
+ layoutState.showOverlay(OverlayA, animationScope = testScope)
+ advanceUntilIdle()
+
+ // Initial state.
+ assertThat(layoutState.transitionState).isIdle()
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+ assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA)
+
+ // Swipe down to replace overlay A by overlay B.
+
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview)
+ nestedScroll.scroll(downOffset(0.1f))
+ val transition = assertThat(layoutState.transitionState).isReplaceOverlayTransition()
+ assertThat(transition).hasCurrentScene(SceneA)
+ assertThat(transition).hasFromOverlay(OverlayA)
+ assertThat(transition).hasToOverlay(OverlayB)
+ assertThat(transition).hasCurrentOverlays(OverlayA)
+ assertThat(transition).hasProgress(0.1f)
+
+ nestedScroll.preFling(Velocity(0f, velocityThreshold))
+ advanceUntilIdle()
+ // Commit the gesture. The overlays are instantly swapped in the set of current overlays.
+ assertThat(layoutState.transitionState).isIdle()
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+ assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index ee807e6a7ede..a301856d024f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -869,10 +869,7 @@ class ElementTest {
state = state,
modifier = Modifier.size(layoutWidth, layoutHeight),
) {
- scene(
- SceneA,
- userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB),
- ) {
+ scene(SceneA, userActions = mapOf(Swipe.Down(pointerCount = 2) to SceneB)) {
Box(
Modifier
// A scrollable that does not consume the scroll gesture
@@ -2641,4 +2638,58 @@ class ElementTest {
assertWithMessage("Frame $i didn't replace Foo").that(numberOfPlacements).isEqualTo(0)
}
}
+
+ @Test
+ fun interruption_considerPreviousUniqueState() {
+ @Composable
+ fun SceneScope.Foo(modifier: Modifier = Modifier) {
+ Box(modifier.element(TestElements.Foo).size(50.dp))
+ }
+
+ val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } }
+ scene(SceneB) { Box(Modifier.fillMaxSize()) }
+ scene(SceneC) {
+ Box(Modifier.fillMaxSize()) { Foo(Modifier.offset(x = 100.dp, y = 100.dp)) }
+ }
+ }
+ }
+
+ // During A => B, Foo disappears and stays in its original position.
+ scope.launch { state.startTransition(transition(SceneA, SceneB)) }
+ rule
+ .onNode(isElement(TestElements.Foo))
+ .assertSizeIsEqualTo(50.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+
+ // Interrupt A => B by B => C.
+ var interruptionProgress by mutableFloatStateOf(1f)
+ scope.launch {
+ state.startTransition(
+ transition(SceneB, SceneC, interruptionProgress = { interruptionProgress })
+ )
+ }
+
+ // During B => C, Foo appears again. It is still at (0, 0) when the interruption progress is
+ // 100%, and converges to its position (100, 100) in C.
+ rule
+ .onNode(isElement(TestElements.Foo))
+ .assertSizeIsEqualTo(50.dp)
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+
+ interruptionProgress = 0.5f
+ rule
+ .onNode(isElement(TestElements.Foo))
+ .assertSizeIsEqualTo(50.dp)
+ .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+ interruptionProgress = 0f
+ rule
+ .onNode(isElement(TestElements.Foo))
+ .assertSizeIsEqualTo(50.dp)
+ .assertPositionInRootIsEqualTo(100.dp, 100.dp)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 3df608717414..5ec74f8d2260 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -98,7 +98,7 @@ class MultiPointerDraggableTest {
Modifier.multiPointerDraggable(
orientation = Orientation.Vertical,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
started = true
SimpleDragController(
onDrag = { dragged = true },
@@ -167,7 +167,7 @@ class MultiPointerDraggableTest {
orientation = Orientation.Vertical,
// We want to start a drag gesture immediately
startDragImmediately = { true },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
started = true
SimpleDragController(
onDrag = { dragged = true },
@@ -239,7 +239,7 @@ class MultiPointerDraggableTest {
.multiPointerDraggable(
orientation = Orientation.Vertical,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
started = true
SimpleDragController(
onDrag = { dragged = true },
@@ -358,7 +358,7 @@ class MultiPointerDraggableTest {
.multiPointerDraggable(
orientation = Orientation.Vertical,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
started = true
SimpleDragController(
onDrag = { dragged = true },
@@ -463,7 +463,7 @@ class MultiPointerDraggableTest {
.multiPointerDraggable(
orientation = Orientation.Vertical,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
verticalStarted = true
SimpleDragController(
onDrag = { verticalDragged = true },
@@ -475,7 +475,7 @@ class MultiPointerDraggableTest {
.multiPointerDraggable(
orientation = Orientation.Horizontal,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
horizontalStarted = true
SimpleDragController(
onDrag = { horizontalDragged = true },
@@ -574,7 +574,7 @@ class MultiPointerDraggableTest {
return swipeConsume
}
},
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
started = true
SimpleDragController(
onDrag = { /* do nothing */ },
@@ -668,7 +668,7 @@ class MultiPointerDraggableTest {
.multiPointerDraggable(
orientation = Orientation.Vertical,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
SimpleDragController(
onDrag = { consumedOnDrag = it },
onStop = { consumedOnDragStop = it },
@@ -739,7 +739,7 @@ class MultiPointerDraggableTest {
.multiPointerDraggable(
orientation = Orientation.Vertical,
startDragImmediately = { false },
- onDragStarted = { _, _, _ ->
+ onDragStarted = { _, _ ->
SimpleDragController(
onDrag = { /* do nothing */ },
onStop = {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index a2b263b9f9ed..2b7090876bad 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -26,10 +26,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
-import com.android.compose.animation.scene.TestScenes.SceneD
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
-import com.android.compose.animation.scene.transition.link.StateLink
import com.android.compose.animation.scene.transition.seekToScene
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.TestSceneTransition
@@ -42,6 +40,7 @@ import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Rule
@@ -132,147 +131,6 @@ class SceneTransitionLayoutStateTest {
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
}
- private fun setupLinkedStates(
- parentInitialScene: SceneKey = SceneC,
- childInitialScene: SceneKey = SceneA,
- sourceFrom: SceneKey? = SceneA,
- sourceTo: SceneKey? = SceneB,
- targetFrom: SceneKey? = SceneC,
- targetTo: SceneKey = SceneD,
- ): Pair<MutableSceneTransitionLayoutStateImpl, MutableSceneTransitionLayoutStateImpl> {
- val parentState = MutableSceneTransitionLayoutState(parentInitialScene)
- val link =
- listOf(
- StateLink(
- parentState,
- listOf(StateLink.TransitionLink(sourceFrom, sourceTo, targetFrom, targetTo)),
- )
- )
- val childState = MutableSceneTransitionLayoutState(childInitialScene, stateLinks = link)
- return Pair(
- parentState as MutableSceneTransitionLayoutStateImpl,
- childState as MutableSceneTransitionLayoutStateImpl,
- )
- }
-
- @Test
- fun linkedTransition_startsLinkAndFinishesLinkInToState() = runTest {
- val (parentState, childState) = setupLinkedStates()
-
- val childTransition = transition(SceneA, SceneB)
-
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
- assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
- }
-
- @Test
- fun linkedTransition_transitiveLink() = runTest {
- val parentParentState =
- MutableSceneTransitionLayoutState(SceneB) as MutableSceneTransitionLayoutStateImpl
- val parentLink =
- listOf(
- StateLink(
- parentParentState,
- listOf(StateLink.TransitionLink(SceneC, SceneD, SceneB, SceneC)),
- )
- )
- val parentState =
- MutableSceneTransitionLayoutState(SceneC, stateLinks = parentLink)
- as MutableSceneTransitionLayoutStateImpl
- val link =
- listOf(
- StateLink(
- parentState,
- listOf(StateLink.TransitionLink(SceneA, SceneB, SceneC, SceneD)),
- )
- )
- val childState =
- MutableSceneTransitionLayoutState(SceneA, stateLinks = link)
- as MutableSceneTransitionLayoutStateImpl
-
- val childTransition = transition(SceneA, SceneB)
-
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
- assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
- assertThat(parentParentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- }
-
- @Test
- fun linkedTransition_linkProgressIsEqual() = runTest {
- val (parentState, childState) = setupLinkedStates()
-
- var progress = 0f
- val childTransition = transition(SceneA, SceneB, progress = { progress })
-
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(parentState.currentTransition?.progress).isEqualTo(0f)
-
- progress = .5f
- assertThat(parentState.currentTransition?.progress).isEqualTo(.5f)
- }
-
- @Test
- fun linkedTransition_reverseTransitionIsNotLinked() = runTest {
- val (parentState, childState) = setupLinkedStates()
-
- val childTransition = transition(SceneB, SceneA, current = { SceneB })
-
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- }
-
- @Test
- fun linkedTransition_startsLinkAndFinishesLinkInFromState() = runTest {
- val (parentState, childState) = setupLinkedStates()
-
- val childTransition = transition(SceneA, SceneB, current = { SceneA })
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- }
-
- @Test
- fun linkedTransition_startsLinkButLinkedStateIsTakenOver() = runTest {
- val (parentState, childState) = setupLinkedStates()
-
- val childTransition = transition(SceneA, SceneB)
- val parentTransition = transition(SceneC, SceneA)
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- parentState.startTransitionImmediately(animationScope = backgroundScope, parentTransition)
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
- assertThat(parentState.transitionState).isEqualTo(parentTransition)
- }
-
@Test
fun setTargetScene_withTransitionKey() = runMonotonicClockTest {
val transitionkey = TransitionKey(debugName = "foo")
@@ -393,51 +251,6 @@ class SceneTransitionLayoutStateTest {
assertThat(state.isTransitioning()).isTrue()
}
- @Test
- fun linkedTransition_fuzzyLinksAreMatchedAndStarted() = runTest {
- val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
- val childTransition = transition(SceneA, SceneB)
-
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
- assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
- }
-
- @Test
- fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() = runTest {
- val (parentState, childState) =
- setupLinkedStates(SceneC, SceneA, SceneA, null, null, SceneD)
-
- val childTransition = transition(SceneA, SceneB, current = { SceneA })
-
- val job =
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
- assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
-
- childTransition.finish()
- job.join()
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- }
-
- @Test
- fun linkedTransition_fuzzyLinksAreNotMatched() = runTest {
- val (parentState, childState) =
- setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD)
- val childTransition = transition(SceneA, SceneB)
-
- childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
- assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
- assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
- }
-
private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB(
progress: () -> Float,
sceneTransitions: SceneTransitions,
@@ -777,4 +590,15 @@ class SceneTransitionLayoutStateTest {
assertThat(transition.progressTo(SceneA)).isEqualTo(1f - 0.2f)
assertThrows(IllegalArgumentException::class.java) { transition.progressTo(SceneC) }
}
+
+ @Test
+ fun transitionCanBeStartedOnlyOnce() = runTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val transition = transition(from = SceneA, to = SceneB)
+
+ state.startTransitionImmediately(backgroundScope, transition)
+ assertThrows(IllegalStateException::class.java) {
+ runBlocking { state.startTransition(transition) }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 2bc9b3826548..97a96a4333cb 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -38,6 +38,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
@@ -61,6 +62,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.TestScenes.SceneD
import com.android.compose.animation.scene.subjects.assertThat
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -126,14 +128,21 @@ class SwipeToSceneTest {
if (swipesEnabled())
mapOf(
Swipe.Down to SceneA,
- Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB,
- Swipe(SwipeDirection.Right, fromSource = Edge.Left) to SceneB,
- Swipe(SwipeDirection.Down, fromSource = Edge.Top) to SceneB,
+ Swipe.Down(pointerCount = 2) to SceneB,
+ Swipe.Down(pointersType = PointerType.Mouse) to SceneD,
+ Swipe.Down(fromSource = Edge.Top) to SceneB,
+ Swipe.Right(fromSource = Edge.Left) to SceneB,
)
else emptyMap(),
) {
Box(Modifier.fillMaxSize())
}
+ scene(
+ key = SceneD,
+ userActions = if (swipesEnabled()) mapOf(Swipe.Up to SceneC) else emptyMap(),
+ ) {
+ Box(Modifier.fillMaxSize())
+ }
}
}
@@ -502,6 +511,45 @@ class SwipeToSceneTest {
}
@Test
+ fun mousePointerSwipe() {
+ // Start at scene C.
+ val layoutState = layoutState(SceneC)
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ TestContent(layoutState)
+ }
+
+ assertThat(layoutState.transitionState).isIdle()
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
+
+ rule.onRoot().performMouseInput {
+ enter(middle)
+ press()
+ moveBy(Offset(0f, touchSlop + 10.dp.toPx()), 1_000)
+ }
+
+ // We are transitioning to D because we are moving the mouse while the primary button is
+ // pressed.
+ val transition = assertThat(layoutState.transitionState).isSceneTransition()
+ assertThat(transition).hasFromScene(SceneC)
+ assertThat(transition).hasToScene(SceneD)
+
+ rule.onRoot().performMouseInput {
+ release()
+ exit(middle)
+ }
+ // Release the mouse primary button and wait for the animation to end. We are back to C
+ // because we only swiped 10dp.
+ rule.waitForIdle()
+ assertThat(layoutState.transitionState).isIdle()
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
+ }
+
+ @Test
fun mouseWheel_pointerInputApi_ignoredByStl() {
val layoutState = layoutState()
var touchSlop = 0f
@@ -526,7 +574,7 @@ class SwipeToSceneTest {
rule.setContent {
touchSlop = LocalViewConfiguration.current.touchSlop
SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) {
- scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ scene(SceneA, userActions = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneB)) {
Box(
Modifier.fillMaxSize()
// A scrollable that does not consume the scroll gesture
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index d66d6b3ab219..d31711496ff0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -28,8 +28,8 @@ import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.OverscrollTranslate
-import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
+import com.android.compose.animation.scene.transformation.TransformationWithRange
import com.android.compose.test.transition
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
@@ -310,7 +310,8 @@ class TransitionDslTest {
}
val overscrollSpec = transitions.overscrollSpecs.single()
- val transformation = overscrollSpec.transformationSpec.transformations.single()
+ val transformation =
+ overscrollSpec.transformationSpec.transformations.single().transformation
assertThat(transformation).isInstanceOf(OverscrollTranslate::class.java)
}
@@ -344,7 +345,7 @@ class TransitionDslTest {
companion object {
private val TRANSFORMATION_RANGE =
- Correspondence.transforming<Transformation, TransformationRange?>(
+ Correspondence.transforming<TransformationWithRange<*>, TransformationRange?>(
{ it?.range },
"has range equal to",
)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 54428404bd0c..91079b89a56c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -29,6 +29,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -262,4 +264,16 @@ class PriorityNestedScrollConnectionTest {
scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
assertThat(isStarted).isEqualTo(true)
}
+
+ @Test
+ fun handleMultipleOnPreFlingCalls() = runTest {
+ startPriorityModePostScroll()
+
+ coroutineScope {
+ launch { scrollConnection.onPreFling(available = Velocity.Zero) }
+ launch { scrollConnection.onPreFling(available = Velocity.Zero) }
+ }
+
+ assertThat(lastStop).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
index f39dd676fb6e..95ef2ce821e1 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
@@ -65,4 +65,6 @@ val EmptyTestTransitions = transitions {
}
from(TestScenes.SceneC, to = TestScenes.SceneA) { spec = snap() }
+
+ from(TestScenes.SceneC, to = TestScenes.SceneD) { spec = snap() }
}
diff --git a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
new file mode 100644
index 000000000000..651e401681c6
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="keyguard_smartspace_top_offset">0dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sw600dp/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000000..10e630d44488
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sw600dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!-- For portrait direction in unfold foldable device, we don't need keyguard_smartspace_top_offset-->
+ <dimen name="keyguard_smartspace_top_offset">0dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
index c574d1fc674b..7feea6e5e8dd 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/res/values/dimens.xml
@@ -33,4 +33,10 @@
<dimen name="small_clock_height">114dp</dimen>
<dimen name="small_clock_padding_top">28dp</dimen>
<dimen name="clock_padding_start">28dp</dimen>
+
+ <!-- When large clock is showing, offset the smartspace by this amount -->
+ <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
+ <!--Dimens used in both lockscreen preview and smartspace -->
+ <dimen name="date_weather_view_height">24dp</dimen>
+ <dimen name="enhanced_smartspace_height">104dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 7014826f6262..300a3e204582 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -55,7 +55,6 @@ class DefaultClockController(
private val layoutInflater: LayoutInflater,
private val resources: Resources,
private val settings: ClockSettings?,
- private val hasStepClockAnimation: Boolean = false,
private val migratedClocks: Boolean = false,
messageBuffers: ClockMessageBuffers? = null,
) : ClockController {
@@ -197,12 +196,11 @@ class DefaultClockController(
views[0].id =
resources.getIdentifier("lockscreen_clock_view_large", "id", ctx.packageName)
}
- override val config =
- ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation)
+ override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
init {
view.migratedClocks = migratedClocks
- view.hasCustomPositionUpdatedAnimation = hasStepClockAnimation
+ view.hasCustomPositionUpdatedAnimation = true
animations = LargeClockAnimations(view, 0f, 0f)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index e9b58b01291f..be4ebdfcb992 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -38,7 +38,6 @@ class DefaultClockProvider(
val ctx: Context,
val layoutInflater: LayoutInflater,
val resources: Resources,
- private val hasStepClockAnimation: Boolean = false,
private val migratedClocks: Boolean = false,
private val isClockReactiveVariantsEnabled: Boolean = false,
) : ClockProvider {
@@ -75,7 +74,6 @@ class DefaultClockProvider(
layoutInflater,
resources,
settings,
- hasStepClockAnimation,
migratedClocks,
messageBuffers,
)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index a4782acaed9b..ee21ea6ee126 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -55,10 +55,7 @@ class FlexClockFaceController(
override val view: View
get() = layerController.view
- override val config =
- ClockFaceConfig(
- hasCustomPositionUpdatedAnimation = false // TODO(b/364673982)
- )
+ override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
override var theme = ThemeConfig(true, assets.seedColor)
@@ -96,6 +93,19 @@ class FlexClockFaceController(
layerController.view.layoutParams = lp
}
+ /** See documentation at [FlexClockView.offsetGlyphsForStepClockAnimation]. */
+ private fun offsetGlyphsForStepClockAnimation(
+ clockStartLeft: Int,
+ direction: Int,
+ fraction: Float
+ ) {
+ (view as? FlexClockView)?.offsetGlyphsForStepClockAnimation(
+ clockStartLeft,
+ direction,
+ fraction,
+ )
+ }
+
override val layout: ClockFaceLayout =
DefaultClockFaceLayout(view).apply {
views[0].id =
@@ -248,10 +258,12 @@ class FlexClockFaceController(
override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
layerController.animations.onPositionUpdated(fromLeft, direction, fraction)
+ if (isLargeClock) offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction)
}
override fun onPositionUpdated(distance: Float, fraction: Float) {
layerController.animations.onPositionUpdated(distance, fraction)
+ // TODO(b/378128811) port stepping animation
}
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 0b55a6e3ffa1..593eba9d05cc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -19,6 +19,7 @@ package com.android.systemui.shared.clocks.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Point
+import android.util.MathUtils.constrainedMap
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
@@ -39,7 +40,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
val digitLeftTopMap = mutableMapOf<Int, Point>()
var maxSingleDigitSize = Point(-1, -1)
val lockscreenTranslate = Point(0, 0)
- val aodTranslate = Point(0, 0)
+ var aodTranslate = Point(0, 0)
init {
setWillNotDraw(false)
@@ -50,6 +51,8 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
)
}
+ private val digitOffsets = mutableMapOf<Int, Float>()
+
override fun addView(child: View?) {
super.addView(child)
(child as SimpleDigitalClockTextView).digitTranslateAnimator =
@@ -64,8 +67,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
maxSingleDigitSize.y = max(maxSingleDigitSize.y, textView.measuredHeight)
}
val textView = digitalClockTextViewMap[R.id.HOUR_FIRST_DIGIT]!!
- aodTranslate.x = -(maxSingleDigitSize.x * AOD_HORIZONTAL_TRANSLATE_RATIO).toInt()
- aodTranslate.y = (maxSingleDigitSize.y * AOD_VERTICAL_TRANSLATE_RATIO).toInt()
+ aodTranslate = Point(0, 0)
return Point(
((maxSingleDigitSize.x + abs(aodTranslate.x)) * 2),
((maxSingleDigitSize.y + abs(aodTranslate.y)) * 2),
@@ -77,7 +79,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitSize.x, 0)
digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitSize.y)
digitLeftTopMap[R.id.MINUTE_SECOND_DIGIT] = Point(maxSingleDigitSize)
- digitLeftTopMap.forEach { _, point ->
+ digitLeftTopMap.forEach { (_, point) ->
point.x += abs(aodTranslate.x)
point.y += abs(aodTranslate.y)
}
@@ -90,11 +92,17 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
- digitalClockTextViewMap.forEach { (id, _) ->
- val textView = digitalClockTextViewMap[id]!!
- canvas.translate(digitLeftTopMap[id]!!.x.toFloat(), digitLeftTopMap[id]!!.y.toFloat())
+ digitalClockTextViewMap.forEach { (id, textView) ->
+ // save canvas location in anticipation of restoration later
+ canvas.save()
+ val xTranslateAmount =
+ digitOffsets.getOrDefault(id, 0f) + digitLeftTopMap[id]!!.x.toFloat()
+ // move canvas to location that the textView would like
+ canvas.translate(xTranslateAmount, digitLeftTopMap[id]!!.y.toFloat())
+ // draw the textView at the location of the canvas above
textView.draw(canvas)
- canvas.translate(-digitLeftTopMap[id]!!.x.toFloat(), -digitLeftTopMap[id]!!.y.toFloat())
+ // reset the canvas location back to 0 without drawing
+ canvas.restore()
}
}
@@ -158,12 +166,107 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
}
}
+ /**
+ * Offsets the textViews of the clock for the step clock animation.
+ *
+ * The animation makes the textViews of the clock move at different speeds, when the clock is
+ * moving horizontally.
+ *
+ * @param clockStartLeft the [getLeft] position of the clock, before it started moving.
+ * @param clockMoveDirection the direction in which it is moving. A positive number means right,
+ * and negative means left.
+ * @param moveFraction fraction of the clock movement. 0 means it is at the beginning, and 1
+ * means it finished moving.
+ */
+ fun offsetGlyphsForStepClockAnimation(
+ clockStartLeft: Int,
+ clockMoveDirection: Int,
+ moveFraction: Float,
+ ) {
+ val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0
+ // The sign of moveAmountDeltaForDigit is already set here
+ // we can interpret (left - clockStartLeft) as (destinationPosition - originPosition)
+ // so we no longer need to multiply direct sign to moveAmountDeltaForDigit
+ val currentMoveAmount = left - clockStartLeft
+ for (i in 0 until NUM_DIGITS) {
+ val mapIndexToId =
+ when (i) {
+ 0 -> R.id.HOUR_FIRST_DIGIT
+ 1 -> R.id.HOUR_SECOND_DIGIT
+ 2 -> R.id.MINUTE_FIRST_DIGIT
+ 3 -> R.id.MINUTE_SECOND_DIGIT
+ else -> -1
+ }
+ val digitFraction =
+ getDigitFraction(
+ digit = i,
+ isMovingToCenter = isMovingToCenter,
+ fraction = moveFraction,
+ )
+ // left here is the final left position after the animation is done
+ val moveAmountForDigit = currentMoveAmount * digitFraction
+ var moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
+ if (isMovingToCenter && moveAmountForDigit < 0) moveAmountDeltaForDigit *= -1
+ digitOffsets[mapIndexToId] = moveAmountDeltaForDigit
+ invalidate()
+ }
+ }
+
+ private val moveToCenterDelays: List<Int>
+ get() = if (isLayoutRtl) MOVE_LEFT_DELAYS else MOVE_RIGHT_DELAYS
+
+ private val moveToSideDelays: List<Int>
+ get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS
+
+ private fun getDigitFraction(digit: Int, isMovingToCenter: Boolean, fraction: Float): Float {
+ // The delay for the digit, in terms of fraction.
+ // (i.e. the digit should not move during 0.0 - 0.1).
+ val delays = if (isMovingToCenter) moveToCenterDelays else moveToSideDelays
+ val digitInitialDelay = delays[digit] * MOVE_DIGIT_STEP
+ return MOVE_INTERPOLATOR.getInterpolation(
+ constrainedMap(
+ /* rangeMin= */ 0.0f,
+ /* rangeMax= */ 1.0f,
+ /* valueMin= */ digitInitialDelay,
+ /* valueMax= */ digitInitialDelay + AVAILABLE_ANIMATION_TIME,
+ /* value= */ fraction,
+ )
+ )
+ }
+
companion object {
val AOD_TRANSITION_DURATION = 750L
val CHARGING_TRANSITION_DURATION = 300L
- val AOD_HORIZONTAL_TRANSLATE_RATIO = 0.15F
- val AOD_VERTICAL_TRANSLATE_RATIO = 0.075F
+ // Calculate the positions of all of the digits...
+ // Offset each digit by, say, 0.1
+ // This means that each digit needs to move over a slice of "fractions", i.e. digit 0 should
+ // move from 0.0 - 0.7, digit 1 from 0.1 - 0.8, digit 2 from 0.2 - 0.9, and digit 3
+ // from 0.3 - 1.0.
+ private const val NUM_DIGITS = 4
+
+ // Delays. Each digit's animation should have a slight delay, so we get a nice
+ // "stepping" effect. When moving right, the second digit of the hour should move first.
+ // When moving left, the first digit of the hour should move first. The lists encode
+ // the delay for each digit (hour[0], hour[1], minute[0], minute[1]), to be multiplied
+ // by delayMultiplier.
+ private val MOVE_LEFT_DELAYS = listOf(0, 1, 2, 3)
+ private val MOVE_RIGHT_DELAYS = listOf(1, 0, 3, 2)
+
+ // How much delay to apply to each subsequent digit. This is measured in terms of "fraction"
+ // (i.e. a value of 0.1 would cause a digit to wait until fraction had hit 0.1, or 0.2 etc
+ // before moving).
+ //
+ // The current specs dictate that each digit should have a 33ms gap between them. The
+ // overall time is 1s right now.
+ private const val MOVE_DIGIT_STEP = 0.033f
+
+ // Constants for the animation
+ private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED
+
+ // Total available transition time for each digit, taking into account the step. If step is
+ // 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
+ private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1)
// Use the sign of targetTranslation to control the direction of digit translation
fun updateDirectionalTargetTranslate(id: Int, targetTranslation: Point): Point {
@@ -173,17 +276,14 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me
outPoint.x *= -1
outPoint.y *= -1
}
-
R.id.HOUR_SECOND_DIGIT -> {
outPoint.x *= 1
outPoint.y *= -1
}
-
R.id.MINUTE_FIRST_DIGIT -> {
outPoint.x *= -1
outPoint.y *= 1
}
-
R.id.MINUTE_SECOND_DIGIT -> {
outPoint.x *= 1
outPoint.y *= 1
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index c0899e3006a6..5c84f2d04ccc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -148,7 +148,11 @@ open class SimpleDigitalClockTextView(
lsFontVariation = ClockFontAxisSetting.toFVar(axes + OPTICAL_SIZE_AXIS)
lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
typeface = lockScreenPaint.typeface
- textAnimator.setTextStyle(fvar = lsFontVariation, animate = true)
+
+ lockScreenPaint.getTextBounds(text, 0, text.length, textBounds)
+ targetTextBounds.set(textBounds)
+
+ textAnimator.setTextStyle(fvar = lsFontVariation, animate = false)
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
recomputeMaxSingleDigitSizes()
requestLayout()
@@ -201,7 +205,7 @@ open class SimpleDigitalClockTextView(
} else {
textBounds.height() + 2 * lockScreenPaint.strokeWidth.toInt()
},
- MeasureSpec.getMode(measuredHeight),
+ MeasureSpec.getMode(measuredHeightAndState),
)
}
@@ -215,10 +219,10 @@ open class SimpleDigitalClockTextView(
} else {
max(
textBounds.width() + 2 * lockScreenPaint.strokeWidth.toInt(),
- MeasureSpec.getSize(measuredWidth),
+ MeasureSpec.getSize(measuredWidthAndState),
)
},
- MeasureSpec.getMode(measuredWidth),
+ MeasureSpec.getMode(measuredWidthAndState),
)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
index 2a87452b0b6a..052e60e7ac9a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
@@ -16,102 +16,22 @@
package com.android.systemui.shared.settings.data.repository
-import android.content.ContentResolver
-import android.database.ContentObserver
import android.provider.Settings
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.withContext
-/**
- * Defines interface for classes that can provide access to data from [Settings.Secure].
- * This repository doesn't guarantee to provide value across different users. For that
- * see: [UserAwareSecureSettingsRepository]
- */
+/** Defines interface for classes that can provide access to data from [Settings.Secure]. */
interface SecureSettingsRepository {
/** Returns a [Flow] tracking the value of a setting as an [Int]. */
- fun intSetting(
- name: String,
- defaultValue: Int = 0,
- ): Flow<Int>
+ fun intSetting(name: String, defaultValue: Int = 0): Flow<Int>
+
+ /** Returns a [Flow] tracking the value of a setting as a [Boolean]. */
+ fun boolSetting(name: String, defaultValue: Boolean = false): Flow<Boolean>
/** Updates the value of the setting with the given name. */
- suspend fun setInt(
- name: String,
- value: Int,
- )
+ suspend fun setInt(name: String, value: Int)
- suspend fun getInt(
- name: String,
- defaultValue: Int = 0,
- ): Int
+ suspend fun getInt(name: String, defaultValue: Int = 0): Int
suspend fun getString(name: String): String?
}
-
-class SecureSettingsRepositoryImpl(
- private val contentResolver: ContentResolver,
- private val backgroundDispatcher: CoroutineDispatcher,
-) : SecureSettingsRepository {
-
- override fun intSetting(
- name: String,
- defaultValue: Int,
- ): Flow<Int> {
- return callbackFlow {
- val observer =
- object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- trySend(Unit)
- }
- }
-
- contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(name),
- /* notifyForDescendants= */ false,
- observer,
- )
- send(Unit)
-
- awaitClose { contentResolver.unregisterContentObserver(observer) }
- }
- .map { Settings.Secure.getInt(contentResolver, name, defaultValue) }
- // The above work is done on the background thread (which is important for accessing
- // settings through the content resolver).
- .flowOn(backgroundDispatcher)
- }
-
- override suspend fun setInt(name: String, value: Int) {
- withContext(backgroundDispatcher) {
- Settings.Secure.putInt(
- contentResolver,
- name,
- value,
- )
- }
- }
-
- override suspend fun getInt(name: String, defaultValue: Int): Int {
- return withContext(backgroundDispatcher) {
- Settings.Secure.getInt(
- contentResolver,
- name,
- defaultValue,
- )
- }
- }
-
- override suspend fun getString(name: String): String? {
- return withContext(backgroundDispatcher) {
- Settings.Secure.getString(
- contentResolver,
- name,
- )
- }
- }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepositoryImpl.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepositoryImpl.kt
new file mode 100644
index 000000000000..9f37959663d7
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepositoryImpl.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.shared.settings.data.repository
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+/**
+ * Simple implementation of [SecureSettingsRepository].
+ *
+ * This repository doesn't guarantee to provide value across different users, and therefore
+ * shouldn't be used in SystemUI. For that see: [UserAwareSecureSettingsRepository]
+ */
+// TODO: b/377244768 - Move to Theme/WallpaperPicker, away from SystemUI.
+class SecureSettingsRepositoryImpl(
+ private val contentResolver: ContentResolver,
+ private val backgroundDispatcher: CoroutineDispatcher,
+) : SecureSettingsRepository {
+
+ override fun intSetting(name: String, defaultValue: Int): Flow<Int> {
+ return callbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(name),
+ /* notifyForDescendants= */ false,
+ observer,
+ )
+ send(Unit)
+
+ awaitClose { contentResolver.unregisterContentObserver(observer) }
+ }
+ .map { Settings.Secure.getInt(contentResolver, name, defaultValue) }
+ // The above work is done on the background thread (which is important for accessing
+ // settings through the content resolver).
+ .flowOn(backgroundDispatcher)
+ }
+
+ override fun boolSetting(name: String, defaultValue: Boolean): Flow<Boolean> {
+ return intSetting(name, if (defaultValue) 1 else 0).map { it != 0 }
+ }
+
+ override suspend fun setInt(name: String, value: Int) {
+ withContext(backgroundDispatcher) { Settings.Secure.putInt(contentResolver, name, value) }
+ }
+
+ override suspend fun getInt(name: String, defaultValue: Int): Int {
+ return withContext(backgroundDispatcher) {
+ Settings.Secure.getInt(contentResolver, name, defaultValue)
+ }
+ }
+
+ override suspend fun getString(name: String): String? {
+ return withContext(backgroundDispatcher) {
+ Settings.Secure.getString(contentResolver, name)
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt
index afe82fb2a5fa..b5f991cdb9b4 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt
@@ -16,102 +16,22 @@
package com.android.systemui.shared.settings.data.repository
-import android.content.ContentResolver
-import android.database.ContentObserver
import android.provider.Settings
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.withContext
-/**
- * Defines interface for classes that can provide access to data from [Settings.System]. This
- * repository doesn't guarantee to provide value across different users. For that see:
- * [UserAwareSecureSettingsRepository] which does that for secure settings.
- */
+/** Interface for classes that can provide access to data from [Settings.System]. */
interface SystemSettingsRepository {
/** Returns a [Flow] tracking the value of a setting as an [Int]. */
- fun intSetting(
- name: String,
- defaultValue: Int = 0,
- ): Flow<Int>
+ fun intSetting(name: String, defaultValue: Int = 0): Flow<Int>
+
+ /** Returns a [Flow] tracking the value of a setting as a [Boolean]. */
+ fun boolSetting(name: String, defaultValue: Boolean = false): Flow<Boolean>
/** Updates the value of the setting with the given name. */
- suspend fun setInt(
- name: String,
- value: Int,
- )
+ suspend fun setInt(name: String, value: Int)
- suspend fun getInt(
- name: String,
- defaultValue: Int = 0,
- ): Int
+ suspend fun getInt(name: String, defaultValue: Int = 0): Int
suspend fun getString(name: String): String?
}
-
-class SystemSettingsRepositoryImpl(
- private val contentResolver: ContentResolver,
- private val backgroundDispatcher: CoroutineDispatcher,
-) : SystemSettingsRepository {
-
- override fun intSetting(
- name: String,
- defaultValue: Int,
- ): Flow<Int> {
- return callbackFlow {
- val observer =
- object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- trySend(Unit)
- }
- }
-
- contentResolver.registerContentObserver(
- Settings.System.getUriFor(name),
- /* notifyForDescendants= */ false,
- observer,
- )
- send(Unit)
-
- awaitClose { contentResolver.unregisterContentObserver(observer) }
- }
- .map { Settings.System.getInt(contentResolver, name, defaultValue) }
- // The above work is done on the background thread (which is important for accessing
- // settings through the content resolver).
- .flowOn(backgroundDispatcher)
- }
-
- override suspend fun setInt(name: String, value: Int) {
- withContext(backgroundDispatcher) {
- Settings.System.putInt(
- contentResolver,
- name,
- value,
- )
- }
- }
-
- override suspend fun getInt(name: String, defaultValue: Int): Int {
- return withContext(backgroundDispatcher) {
- Settings.System.getInt(
- contentResolver,
- name,
- defaultValue,
- )
- }
- }
-
- override suspend fun getString(name: String): String? {
- return withContext(backgroundDispatcher) {
- Settings.System.getString(
- contentResolver,
- name,
- )
- }
- }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryImpl.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryImpl.kt
new file mode 100644
index 000000000000..8485d4db2d20
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryImpl.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.settings.data.repository
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+/**
+ * Defines interface for classes that can provide access to data from [Settings.System].
+ *
+ * This repository doesn't guarantee to provide value across different users. For that see:
+ * [UserAwareSystemSettingsRepository].
+ */
+// TODO: b/377244768 - Move to Theme/WallpaperPicker, away from SystemUI.
+class SystemSettingsRepositoryImpl(
+ private val contentResolver: ContentResolver,
+ private val backgroundDispatcher: CoroutineDispatcher,
+) : SystemSettingsRepository {
+
+ override fun intSetting(name: String, defaultValue: Int): Flow<Int> {
+ return callbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+
+ contentResolver.registerContentObserver(
+ Settings.System.getUriFor(name),
+ /* notifyForDescendants= */ false,
+ observer,
+ )
+ send(Unit)
+
+ awaitClose { contentResolver.unregisterContentObserver(observer) }
+ }
+ .map { Settings.System.getInt(contentResolver, name, defaultValue) }
+ // The above work is done on the background thread (which is important for accessing
+ // settings through the content resolver).
+ .flowOn(backgroundDispatcher)
+ }
+
+ override fun boolSetting(name: String, defaultValue: Boolean): Flow<Boolean> {
+ return intSetting(name, if (defaultValue) 1 else 0).map { it != 0 }
+ }
+
+ override suspend fun setInt(name: String, value: Int) {
+ withContext(backgroundDispatcher) { Settings.System.putInt(contentResolver, name, value) }
+ }
+
+ override suspend fun getInt(name: String, defaultValue: Int): Int {
+ return withContext(backgroundDispatcher) {
+ Settings.System.getInt(contentResolver, name, defaultValue)
+ }
+ }
+
+ override suspend fun getString(name: String): String? {
+ return withContext(backgroundDispatcher) {
+ Settings.System.getString(contentResolver, name)
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
index 37b97926afa4..21d8d4648824 100644
--- a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
@@ -28,6 +28,10 @@ class FakeSecureSettingsRepository : SecureSettingsRepository {
return settings.map { it.getOrDefault(name, defaultValue.toString()) }.map { it.toInt() }
}
+ override fun boolSetting(name: String, defaultValue: Boolean): Flow<Boolean> {
+ return intSetting(name, if (defaultValue) 1 else 0).map { it != 0 }
+ }
+
override suspend fun setInt(name: String, value: Int) {
settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() }
}
diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt
index 7da2b40fd57a..f6c053f5b1dc 100644
--- a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt
+++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt
@@ -28,6 +28,10 @@ class FakeSystemSettingsRepository : SystemSettingsRepository {
return settings.map { it.getOrDefault(name, defaultValue.toString()) }.map { it.toInt() }
}
+ override fun boolSetting(name: String, defaultValue: Boolean): Flow<Boolean> {
+ return intSetting(name, if (defaultValue) 1 else 0).map { it != 0 }
+ }
+
override suspend fun setInt(name: String, value: Int) {
settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() }
}
diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md
index ade5171d6415..c18d1f143b85 100644
--- a/packages/SystemUI/docs/demo_mode.md
+++ b/packages/SystemUI/docs/demo_mode.md
@@ -35,6 +35,7 @@ Commands are sent as string extras with key ```command``` (required). Possible v
| | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
| | ```wifi``` | | ```show``` to show icon, any other value to hide
| | | ```level``` | Sets wifi level (null or 0-4)
+| | | ```hotspot``` | Sets the wifi to be from an Instant Hotspot. Values: ```none```, ```unknown```, ```phone```, ```tablet```, ```laptop```, ```watch```, ```auto```. (See `DemoModeWifiDataSource.kt`.)
| | ```mobile``` | | ```show``` to show icon, any other value to hide
| | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
| | | ```level``` | Sets mobile signal strength level (null or 0-4)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
deleted file mode 100644
index dd58ea7db2bc..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2021 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.keyguard;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class KeyguardDisplayManagerTest extends SysuiTestCase {
-
- @Mock
- private NavigationBarController mNavigationBarController;
- @Mock
- private ConnectedDisplayKeyguardPresentation.Factory
- mConnectedDisplayKeyguardPresentationFactory;
- @Mock
- private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
- @Mock
- private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
- @Mock
- private KeyguardStateController mKeyguardStateController;
-
- private Executor mMainExecutor = Runnable::run;
- private Executor mBackgroundExecutor = Runnable::run;
- private KeyguardDisplayManager mManager;
- private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- // The default and secondary displays are both in the default group
- private Display mDefaultDisplay;
- private Display mSecondaryDisplay;
-
- // This display is in a different group from the default and secondary displays.
- private Display mAlwaysUnlockedDisplay;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
- mDisplayTracker, mMainExecutor, mBackgroundExecutor, mDeviceStateHelper,
- mKeyguardStateController, mConnectedDisplayKeyguardPresentationFactory));
- doReturn(mConnectedDisplayKeyguardPresentation).when(
- mConnectedDisplayKeyguardPresentationFactory).create(any());
- doReturn(mConnectedDisplayKeyguardPresentation).when(mManager)
- .createPresentation(any());
- mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
- new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
- mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(),
- Display.DEFAULT_DISPLAY + 1,
- new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
-
- DisplayInfo alwaysUnlockedDisplayInfo = new DisplayInfo();
- alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2;
- alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED;
- mAlwaysUnlockedDisplay = new Display(DisplayManagerGlobal.getInstance(),
- Display.DEFAULT_DISPLAY,
- alwaysUnlockedDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- }
-
- @Test
- public void testShow_defaultDisplayOnly() {
- mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay});
- mManager.show();
- verify(mManager, never()).createPresentation(any());
- }
-
- @Test
- public void testShow_includeSecondaryDisplay() {
- mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
- mManager.show();
- verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
- }
-
- @Test
- public void testShow_includeAlwaysUnlockedDisplay() {
- mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
-
- mManager.show();
- verify(mManager, never()).createPresentation(any());
- }
-
- @Test
- public void testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
- mDisplayTracker.setAllDisplays(
- new Display[]{mDefaultDisplay, mSecondaryDisplay, mAlwaysUnlockedDisplay});
-
- mManager.show();
- verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
- }
-
- @Test
- public void testShow_concurrentDisplayActive_occluded() {
- mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
-
- when(mDeviceStateHelper.isConcurrentDisplayActive(mSecondaryDisplay)).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(true);
- verify(mManager, never()).createPresentation(eq(mSecondaryDisplay));
- }
-
- @Test
- public void testShow_presentationCreated() {
- when(mManager.createPresentation(any())).thenCallRealMethod();
- mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
-
- mManager.show();
-
- verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay));
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
new file mode 100644
index 000000000000..57a67973f34f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard
+
+import android.hardware.display.DisplayManagerGlobal
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display
+import android.view.DisplayAdjustments
+import android.view.DisplayInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardDisplayManager.DeviceStateHelper
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.data.repository.FakeShadePositionRepository
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import java.util.concurrent.Executor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
+class KeyguardDisplayManagerTest : SysuiTestCase() {
+ @Mock private val navigationBarController = mock(NavigationBarController::class.java)
+ @Mock
+ private val presentationFactory = mock(ConnectedDisplayKeyguardPresentation.Factory::class.java)
+ @Mock
+ private val connectedDisplayKeyguardPresentation =
+ mock(ConnectedDisplayKeyguardPresentation::class.java)
+ @Mock private val deviceStateHelper = mock(DeviceStateHelper::class.java)
+ @Mock private val keyguardStateController = mock(KeyguardStateController::class.java)
+ private val shadePositionRepository = FakeShadePositionRepository()
+
+ private val mainExecutor = Executor { it.run() }
+ private val backgroundExecutor = Executor { it.run() }
+ private lateinit var manager: KeyguardDisplayManager
+ private val displayTracker = FakeDisplayTracker(mContext)
+ // The default and secondary displays are both in the default group
+ private lateinit var defaultDisplay: Display
+ private lateinit var secondaryDisplay: Display
+
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+
+ // This display is in a different group from the default and secondary displays.
+ private lateinit var alwaysUnlockedDisplay: Display
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ manager =
+ KeyguardDisplayManager(
+ mContext,
+ { navigationBarController },
+ displayTracker,
+ mainExecutor,
+ backgroundExecutor,
+ deviceStateHelper,
+ keyguardStateController,
+ presentationFactory,
+ { shadePositionRepository },
+ testScope.backgroundScope,
+ )
+ whenever(presentationFactory.create(any())).doReturn(connectedDisplayKeyguardPresentation)
+
+ defaultDisplay =
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY,
+ DisplayInfo(),
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
+ )
+ secondaryDisplay =
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY + 1,
+ DisplayInfo(),
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
+ )
+
+ val alwaysUnlockedDisplayInfo = DisplayInfo()
+ alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2
+ alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED
+ alwaysUnlockedDisplay =
+ Display(
+ DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY,
+ alwaysUnlockedDisplayInfo,
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
+ )
+ }
+
+ @Test
+ fun testShow_defaultDisplayOnly() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay)
+ manager.show()
+ verify(presentationFactory, never()).create(any())
+ }
+
+ @Test
+ fun testShow_includeSecondaryDisplay() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+ manager.show()
+ verify(presentationFactory).create(eq(secondaryDisplay))
+ }
+
+ @Test
+ fun testShow_includeAlwaysUnlockedDisplay() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, alwaysUnlockedDisplay)
+
+ manager.show()
+ verify(presentationFactory, never()).create(any())
+ }
+
+ @Test
+ fun testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
+ displayTracker.allDisplays =
+ arrayOf(defaultDisplay, secondaryDisplay, alwaysUnlockedDisplay)
+
+ manager.show()
+ verify(presentationFactory).create(eq(secondaryDisplay))
+ }
+
+ @Test
+ fun testShow_concurrentDisplayActive_occluded() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+ whenever(deviceStateHelper.isConcurrentDisplayActive(secondaryDisplay)).thenReturn(true)
+ whenever(keyguardStateController.isOccluded).thenReturn(true)
+ verify(presentationFactory, never()).create(eq(secondaryDisplay))
+ }
+
+ @Test
+ fun testShow_presentationCreated() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+ manager.show()
+
+ verify(presentationFactory).create(eq(secondaryDisplay))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun show_shadeMovesDisplay_newPresentationCreated() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+ // Shade in the default display, we expect the presentation to be in the secondary only
+ shadePositionRepository.setDisplayId(defaultDisplay.displayId)
+
+ manager.show()
+
+ verify(presentationFactory).create(eq(secondaryDisplay))
+ verify(presentationFactory, never()).create(eq(defaultDisplay))
+ reset(presentationFactory)
+ whenever(presentationFactory.create(any())).thenReturn(connectedDisplayKeyguardPresentation)
+
+ // Let's move it to the secondary display. We expect it will be added in the default
+ // one.
+ shadePositionRepository.setDisplayId(secondaryDisplay.displayId)
+ testScope.advanceUntilIdle()
+
+ verify(presentationFactory).create(eq(defaultDisplay))
+ reset(presentationFactory)
+ whenever(presentationFactory.create(any())).thenReturn(connectedDisplayKeyguardPresentation)
+
+ // Let's move it back! it should be re-created (it means it was removed before)
+ shadePositionRepository.setDisplayId(defaultDisplay.displayId)
+ testScope.advanceUntilIdle()
+
+ verify(presentationFactory).create(eq(secondaryDisplay))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun show_shadeInSecondaryDisplay_defaultOneHasPresentation() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+ shadePositionRepository.setDisplayId(secondaryDisplay.displayId)
+
+ manager.show()
+
+ verify(presentationFactory).create(eq(defaultDisplay))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun show_shadeInDefaultDisplay_secondaryOneHasPresentation() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+ shadePositionRepository.setDisplayId(defaultDisplay.displayId)
+
+ manager.show()
+
+ verify(presentationFactory).create(eq(secondaryDisplay))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt
new file mode 100644
index 000000000000..8635bb0e8ab2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/OnTeardownRuleTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.JUnitCore
+
+@Suppress("JUnitMalformedDeclaration")
+@SmallTest
+class OnTeardownRuleTest : SysuiTestCase() {
+ // None of these inner classes should be run except as part of this utilities-testing test
+ class HasTeardown {
+ @get:Rule val teardownRule = OnTeardownRule()
+
+ @Before
+ fun setUp() {
+ teardownWasRun = false
+ teardownRule.onTeardown { teardownWasRun = true }
+ }
+
+ @Test fun doTest() {}
+
+ companion object {
+ var teardownWasRun = false
+ }
+ }
+
+ @Test
+ fun teardownRuns() {
+ val result = JUnitCore().run(HasTeardown::class.java)
+ assertThat(result.failures).isEmpty()
+ assertThat(HasTeardown.teardownWasRun).isTrue()
+ }
+
+ class FirstTeardownFails {
+ @get:Rule val teardownRule = OnTeardownRule()
+
+ @Before
+ fun setUp() {
+ teardownWasRun = false
+ teardownRule.onTeardown { fail("One fails") }
+ teardownRule.onTeardown { teardownWasRun = true }
+ }
+
+ @Test fun doTest() {}
+
+ companion object {
+ var teardownWasRun = false
+ }
+ }
+
+ @Test
+ fun allTeardownsRun() {
+ val result = JUnitCore().run(FirstTeardownFails::class.java)
+ assertThat(result.failures.map { it.message }).isEqualTo(listOf("One fails"))
+ assertThat(FirstTeardownFails.teardownWasRun).isTrue()
+ }
+
+ class ThreeTeardowns {
+ @get:Rule val teardownRule = OnTeardownRule()
+
+ @Before
+ fun setUp() {
+ messages.clear()
+ }
+
+ @Test
+ fun doTest() {
+ teardownRule.onTeardown { messages.add("A") }
+ teardownRule.onTeardown { messages.add("B") }
+ teardownRule.onTeardown { messages.add("C") }
+ }
+
+ companion object {
+ val messages = mutableListOf<String>()
+ }
+ }
+
+ @Test
+ fun reverseOrder() {
+ val result = JUnitCore().run(ThreeTeardowns::class.java)
+ assertThat(result.failures).isEmpty()
+ assertThat(ThreeTeardowns.messages).isEqualTo(listOf("C", "B", "A"))
+ }
+
+ class TryToDoABadThing {
+ @get:Rule val teardownRule = OnTeardownRule()
+
+ @Test
+ fun doTest() {
+ teardownRule.onTeardown {
+ teardownRule.onTeardown {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ @Test
+ fun prohibitTeardownDuringTeardown() {
+ val result = JUnitCore().run(TryToDoABadThing::class.java)
+ assertThat(result.failures.map { it.message })
+ .isEqualTo(listOf("Cannot add new teardown routines after test complete."))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index c74d340ee325..b087ecf1a488 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.accessibility;
import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -190,8 +189,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Test
public void showMagnificationButton_delayedShowButton() throws RemoteException {
- // magnification settings panel should not be showing
- assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+ when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(false);
mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -237,8 +235,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Test
public void removeMagnificationButton_delayingShowButton_doNotShowButtonAfterTimeout()
throws RemoteException {
- // magnification settings panel should not be showing
- assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+ when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(false);
mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 000000000000..a2001e66e55b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 25696bffdd66..f41d5c8eeb23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.view.Choreographer.FrameCallback;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.systemGestures;
@@ -28,14 +27,8 @@ import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityActi
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItems;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.AdditionalAnswers.returnsSecondArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -45,13 +38,17 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.util.Arrays.asList;
+
import android.animation.ValueAnimator;
import android.annotation.IdRes;
import android.annotation.Nullable;
@@ -63,38 +60,36 @@ import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.RegionIterator;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableLooper;
import android.testing.TestableResources;
-import android.text.TextUtils;
import android.util.Size;
+import android.view.AttachedSurfaceControl;
import android.view.Display;
-import android.view.IWindowSession;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.widget.FrameLayout;
+import android.window.InputTransferToken;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
@@ -109,8 +104,6 @@ import com.android.systemui.utils.os.FakeHandler;
import com.google.common.util.concurrent.AtomicDouble;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -118,31 +111,27 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
@LargeTest
@TestableLooper.RunWithLooper
@RunWith(AndroidJUnit4.class)
-@RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
public class WindowMagnificationControllerTest extends SysuiTestCase {
@Rule
// NOTE: pass 'null' to allow this test advances time on the main thread.
- public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(null);
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(/* test= */ null);
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
@Mock
- private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
- @Mock
private MirrorWindowControl mMirrorWindowControl;
@Mock
private WindowMagnifierCallback mWindowMagnifierCallback;
@@ -150,12 +139,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
IRemoteMagnificationAnimationCallback mAnimationCallback2;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ private SurfaceControl.Transaction mTransaction;
@Mock
private SecureSettings mSecureSettings;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private long mWaitAnimationDuration;
private long mWaitBounceEffectDuration;
@@ -170,11 +157,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private IWindowSession mWindowSessionSpy;
-
private View mSpyView;
private View.OnTouchListener mTouchListener;
+
private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+ // This list contains all SurfaceControlViewHosts created during a given test. If the
+ // magnification window is recreated during a test, the list will contain more than a single
+ // element.
+ private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>();
+ // The most recently created SurfaceControlViewHost.
+ private SurfaceControlViewHost mSurfaceControlViewHost;
private KosmosJavaAdapter mKosmos;
private FakeSharedPreferences mSharedPreferences;
@@ -196,15 +189,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
- mWindowSessionSpy = spy(WindowManagerGlobal.getWindowSession());
-
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- doAnswer(invocation -> {
- FrameCallback callback = invocation.getArgument(0);
- callback.doFrame(0);
- return null;
- }).when(mSfVsyncFrameProvider).postFrameCallback(
- any(FrameCallback.class));
mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin());
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
@@ -228,13 +213,20 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
mContext, mValueAnimator);
+ Supplier<SurfaceControlViewHost> scvhSupplier = () -> {
+ mSurfaceControlViewHost = spy(new SurfaceControlViewHost(
+ mContext, mContext.getDisplay(), new InputTransferToken(),
+ "WindowMagnification"));
+ ViewRootImpl viewRoot = mock(ViewRootImpl.class);
+ when(mSurfaceControlViewHost.getRootSurfaceControl()).thenReturn(viewRoot);
+ mSurfaceControlViewHosts.add(mSurfaceControlViewHost);
+ return mSurfaceControlViewHost;
+ };
+ mTransaction = spy(new SurfaceControl.Transaction());
mSharedPreferences = new FakeSharedPreferences();
when(mContext.getSharedPreferences(
eq("window_magnification_preferences"), anyInt()))
.thenReturn(mSharedPreferences);
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager = new
- ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture,
- /* isViewCaptureEnabled= */ false);
mWindowMagnificationController =
new WindowMagnificationController(
mContext,
@@ -245,10 +237,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- /* scvhSupplier= */ () -> null,
- mSfVsyncFrameProvider,
- /* globalWindowSessionSupplier= */ () -> mWindowSessionSpy,
- viewCaptureAwareWindowManager);
+ scvhSupplier);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
@@ -277,7 +266,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mSecureSettings).getIntForUser(
eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
/* def */ eq(1), /* userHandle= */ anyInt());
- assertTrue(mWindowMagnificationController.isDiagonalScrollingEnabled());
+ assertThat(mWindowMagnificationController.isDiagonalScrollingEnabled()).isTrue();
}
@Test
@@ -325,7 +314,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
- verify(mSfVsyncFrameProvider, atLeast(2)).postFrameCallback(any());
+ verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
+ eq(Surface.ROTATION_0));
}
@Test
@@ -342,10 +332,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
(eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertEquals(mWindowMagnificationController.getCenterX(),
- sourceBoundsCaptor.getValue().exactCenterX(), 0);
- assertEquals(mWindowMagnificationController.getCenterY(),
- sourceBoundsCaptor.getValue().exactCenterY(), 0);
+ assertThat(mWindowMagnificationController.getCenterX())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+ assertThat(mWindowMagnificationController.getCenterY())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
}
@Test
@@ -357,8 +347,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
// Wait for Rects updated.
waitForIdleSync();
- List<Rect> rects = mWindowManager.getAttachedView().getSystemGestureExclusionRects();
- assertFalse(rects.isEmpty());
+ List<Rect> rects = mSurfaceControlViewHost.getView().getSystemGestureExclusionRects();
+ assertThat(rects).isNotEmpty();
}
@Ignore("The default window size should be constrained after fixing b/288056772")
@@ -373,11 +363,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
final int halfScreenSize = screenSize / 2;
- WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
// The frame size should be the half of smaller value of window height/width unless it
//exceed the max frame size.
- assertTrue(params.width < halfScreenSize);
- assertTrue(params.height < halfScreenSize);
+ assertThat(params.width).isLessThan(halfScreenSize);
+ assertThat(params.height).isLessThan(halfScreenSize);
}
@Test
@@ -411,7 +401,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
verify(mMirrorWindowControl).destroyControl();
- assertFalse(hasMagnificationOverlapFlag());
+ assertThat(hasMagnificationOverlapFlag()).isFalse();
}
@Test
@@ -435,10 +425,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
- mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
});
- verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
+ waitForIdleSync();
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.moveWindowMagnifier(100f, 100f));
+
+ verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
+ eq(Surface.ROTATION_0));
}
@Test
@@ -455,6 +449,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
+ reset(mWindowMagnifierCallback);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
targetCenterX, targetCenterY, mAnimationCallback);
@@ -465,12 +460,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mAnimationCallback, never()).onResult(eq(false));
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertEquals(mWindowMagnificationController.getCenterX(),
- sourceBoundsCaptor.getValue().exactCenterX(), 0);
- assertEquals(mWindowMagnificationController.getCenterY(),
- sourceBoundsCaptor.getValue().exactCenterY(), 0);
- assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0);
- assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
+ assertThat(mWindowMagnificationController.getCenterX())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+ assertThat(mWindowMagnificationController.getCenterY())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
+ assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(targetCenterX);
+ assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(targetCenterY);
}
@Test
@@ -487,6 +482,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
+ reset(mWindowMagnifierCallback);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
centerX + 10, centerY + 10, mAnimationCallback);
@@ -505,12 +501,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
verify(mAnimationCallback, times(3)).onResult(eq(false));
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertEquals(mWindowMagnificationController.getCenterX(),
- sourceBoundsCaptor.getValue().exactCenterX(), 0);
- assertEquals(mWindowMagnificationController.getCenterY(),
- sourceBoundsCaptor.getValue().exactCenterY(), 0);
- assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0);
- assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0);
+ assertThat(mWindowMagnificationController.getCenterX())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+ assertThat(mWindowMagnificationController.getCenterY())
+ .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
+ assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(centerX + 40);
+ assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(centerY + 40);
}
@Test
@@ -521,10 +517,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
- assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
- final View mirrorView = mWindowManager.getAttachedView();
- assertNotNull(mirrorView);
- assertThat(mirrorView.getStateDescription().toString(), containsString("300"));
+ assertThat(mWindowMagnificationController.getScale()).isEqualTo(3.0f);
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ assertThat(mirrorView).isNotNull();
+ assertThat(mirrorView.getStateDescription().toString()).contains("300");
}
@Test
@@ -563,12 +559,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
ActivityInfo.CONFIG_ORIENTATION));
- assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
final PointF expectedCenter = new PointF(magnifiedCenter.y,
displayWidth - magnifiedCenter.x);
final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
mWindowMagnificationController.getCenterY());
- assertEquals(expectedCenter, actualCenter);
+ assertThat(actualCenter).isEqualTo(expectedCenter);
}
@Test
@@ -583,7 +579,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
ActivityInfo.CONFIG_ORIENTATION));
- assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
}
@Test
@@ -610,14 +606,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
// The ratio of center to window size should be the same.
- assertEquals(expectedRatio,
- mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
- 0);
- assertEquals(expectedRatio,
- mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
- 0);
+ assertThat(mWindowMagnificationController.getCenterX() / testWindowBounds.width())
+ .isEqualTo(expectedRatio);
+ assertThat(mWindowMagnificationController.getCenterY() / testWindowBounds.height())
+ .isEqualTo(expectedRatio);
}
+ @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@Test
public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
int newSmallestScreenWidthDp =
@@ -635,7 +630,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- // Change screen density and size to trigger restoring the preferred window size
+ // Screen density and size change
mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
final Rect testWindowBounds = new Rect(
mWindowManager.getCurrentWindowMetrics().getBounds());
@@ -648,12 +643,56 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
// wait for rect update
waitForIdleSync();
- WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
R.dimen.magnification_mirror_surface_margin);
// The width and height of the view include the magnification frame and the margins.
- assertTrue(params.width == (windowFrameSize + 2 * mirrorSurfaceMargin));
- assertTrue(params.height == (windowFrameSize + 2 * mirrorSurfaceMargin));
+ assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+ assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+ }
+
+ @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
+ @Test
+ public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() {
+ int newSmallestScreenWidthDp =
+ mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
+ int windowFrameSize = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+ Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
+ mSharedPreferences
+ .edit()
+ .putString(String.valueOf(newSmallestScreenWidthDp),
+ WindowMagnificationFrameSpec.serialize(
+ WindowMagnificationSettings.MagnificationSize.CUSTOM,
+ preferredWindowSize))
+ .commit();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ // Screen density and size change
+ mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
+ final Rect testWindowBounds = new Rect(
+ mWindowManager.getCurrentWindowMetrics().getBounds());
+ testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+ testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+ mWindowManager.setWindowBounds(testWindowBounds);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+ });
+
+ // wait for rect update
+ waitForIdleSync();
+ verify(mWindowMagnifierCallback).onWindowMagnifierBoundsRestored(
+ eq(mContext.getDisplayId()),
+ eq(WindowMagnificationSettings.MagnificationSize.CUSTOM));
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
+ final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+ R.dimen.magnification_mirror_surface_margin);
+ // The width and height of the view include the magnification frame and the margins.
+ assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+ assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
}
@Test
@@ -675,10 +714,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final int defaultWindowSize =
mWindowMagnificationController.getMagnificationWindowSizeFromIndex(
WindowMagnificationSettings.MagnificationSize.MEDIUM);
- WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+ ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- assertTrue(params.width == defaultWindowSize);
- assertTrue(params.height == defaultWindowSize);
+ assertThat(params.width).isEqualTo(defaultWindowSize);
+ assertThat(params.height).isEqualTo(defaultWindowSize);
}
@Test
@@ -695,9 +734,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
- verify(mWindowManager).removeView(any());
+ verify(mSurfaceControlViewHosts.get(0)).release();
verify(mMirrorWindowControl).destroyControl();
- verify(mWindowManager).addView(any(), any());
+ verify(mSurfaceControlViewHosts.get(1)).setView(any(), any());
verify(mMirrorWindowControl).showControl();
}
@@ -716,21 +755,30 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
Float.NaN);
});
- final View mirrorView = mWindowManager.getAttachedView();
- assertNotNull(mirrorView);
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ assertThat(mirrorView).isNotNull();
final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
- assertNotNull(nodeInfo.getContentDescription());
- assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
- assertThat(nodeInfo.getActionList(),
- hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null),
- new AccessibilityAction(R.id.accessibility_action_zoom_out, null),
- new AccessibilityAction(R.id.accessibility_action_move_right, null),
- new AccessibilityAction(R.id.accessibility_action_move_left, null),
- new AccessibilityAction(R.id.accessibility_action_move_down, null),
- new AccessibilityAction(R.id.accessibility_action_move_up, null)));
+ assertThat(nodeInfo.getContentDescription()).isNotNull();
+ assertThat(nodeInfo.getStateDescription().toString()).contains("250");
+ assertThat(nodeInfo.getActionList()).containsExactlyElementsIn(asList(
+ new AccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
+ mContext.getResources().getString(
+ R.string.magnification_open_settings_click_label)),
+ new AccessibilityAction(R.id.accessibility_action_zoom_in,
+ mContext.getString(R.string.accessibility_control_zoom_in)),
+ new AccessibilityAction(R.id.accessibility_action_zoom_out,
+ mContext.getString(R.string.accessibility_control_zoom_out)),
+ new AccessibilityAction(R.id.accessibility_action_move_right,
+ mContext.getString(R.string.accessibility_control_move_right)),
+ new AccessibilityAction(R.id.accessibility_action_move_left,
+ mContext.getString(R.string.accessibility_control_move_left)),
+ new AccessibilityAction(R.id.accessibility_action_move_down,
+ mContext.getString(R.string.accessibility_control_move_down)),
+ new AccessibilityAction(R.id.accessibility_action_move_up,
+ mContext.getString(R.string.accessibility_control_move_up))));
}
@Test
@@ -741,29 +789,34 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- final View mirrorView = mWindowManager.getAttachedView();
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null))
+ .isTrue();
// Minimum scale is 1.0.
verify(mWindowMagnifierCallback).onPerformScaleAction(
eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
- assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
+ assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null))
+ .isTrue();
verify(mWindowMagnifierCallback).onPerformScaleAction(
eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
// TODO: Verify the final state when the mirror surface is visible.
- assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
- assertTrue(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
+ assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null))
+ .isTrue();
+ assertThat(
+ mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null))
+ .isTrue();
+ assertThat(
+ mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null))
+ .isTrue();
+ assertThat(
+ mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null))
+ .isTrue();
verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
- assertTrue(mirrorView.performAccessibilityAction(
- AccessibilityAction.ACTION_CLICK.getId(), null));
+ assertThat(mirrorView.performAccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.getId(), null)).isTrue();
verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
}
@@ -775,7 +828,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
@@ -795,20 +848,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
View topRightCorner = getInternalView(R.id.top_right_corner);
View topLeftCorner = getInternalView(R.id.top_left_corner);
- assertEquals(View.VISIBLE, closeButton.getVisibility());
- assertEquals(View.VISIBLE, bottomRightCorner.getVisibility());
- assertEquals(View.VISIBLE, bottomLeftCorner.getVisibility());
- assertEquals(View.VISIBLE, topRightCorner.getVisibility());
- assertEquals(View.VISIBLE, topLeftCorner.getVisibility());
+ assertThat(closeButton.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(topRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(topLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
- final View mirrorView = mWindowManager.getAttachedView();
- mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(), null);
+ final View mirrorView = mSurfaceControlViewHost.getView();
+ mInstrumentation.runOnMainSync(() ->
+ mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
+ null));
- assertEquals(View.GONE, closeButton.getVisibility());
- assertEquals(View.GONE, bottomRightCorner.getVisibility());
- assertEquals(View.GONE, bottomLeftCorner.getVisibility());
- assertEquals(View.GONE, topRightCorner.getVisibility());
- assertEquals(View.GONE, topLeftCorner.getVisibility());
+ assertThat(closeButton.getVisibility()).isEqualTo(View.GONE);
+ assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.GONE);
+ assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.GONE);
+ assertThat(topRightCorner.getVisibility()).isEqualTo(View.GONE);
+ assertThat(topLeftCorner.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -828,7 +883,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -836,8 +891,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_increase_window_width, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -847,8 +904,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowWidth =
(int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(newWindowWidth, actualWindowWidth.get());
- assertEquals(startingHeight, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
+ assertThat(actualWindowHeight.get()).isEqualTo(startingHeight);
}
@Test
@@ -868,7 +925,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -876,8 +933,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_increase_window_height, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -887,8 +946,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowHeight =
(int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(startingWidth, actualWindowWidth.get());
- assertEquals(newWindowHeight, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(startingWidth);
+ assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
}
@Test
@@ -904,11 +963,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_increase_window_width, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_increase_window_width,
+ mContext.getString(
+ R.string.accessibility_control_increase_window_width)));
}
@Test
@@ -924,11 +986,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_increase_window_height, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_increase_window_height, null));
}
@Test
@@ -947,7 +1010,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -955,8 +1018,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_decrease_window_width, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -966,8 +1031,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowWidth =
(int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(newWindowWidth, actualWindowWidth.get());
- assertEquals(startingSize, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
+ assertThat(actualWindowHeight.get()).isEqualTo(startingSize);
}
@Test
@@ -987,7 +1052,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicInteger actualWindowHeight = new AtomicInteger();
final AtomicInteger actualWindowWidth = new AtomicInteger();
@@ -995,8 +1060,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mirrorView.performAccessibilityAction(
R.id.accessibility_action_decrease_window_height, null);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
@@ -1006,8 +1073,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
int newWindowHeight =
(int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+ 2 * mirrorSurfaceMargin;
- assertEquals(startingSize, actualWindowWidth.get());
- assertEquals(newWindowHeight, actualWindowHeight.get());
+ assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
+ assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
}
@Test
@@ -1023,15 +1090,16 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_decrease_window_width, null));
}
@Test
- public void windowHeightIsMin_noDecreaseWindowHeightA11yAcyion() {
+ public void windowHeightIsMin_noDecreaseWindowHeightA11yAction() {
int mMinWindowSize = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
final int startingSize = mMinWindowSize;
@@ -1043,11 +1111,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AccessibilityNodeInfo accessibilityNodeInfo =
mirrorView.createAccessibilityNodeInfo();
- assertFalse(accessibilityNodeInfo.getActionList().contains(
- new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null)));
+ assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+ new AccessibilityAction(
+ R.id.accessibility_action_decrease_window_height, null));
}
@Test
@@ -1057,8 +1126,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- assertEquals(getContext().getResources().getString(
- com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle());
+ assertThat(getAccessibilityWindowTitle()).isEqualTo(getContext().getResources().getString(
+ com.android.internal.R.string.android_system_label));
}
@Test
@@ -1073,14 +1142,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
Float.NaN);
});
- assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
+ assertThat(mWindowMagnificationController.getScale()).isEqualTo(Float.NaN);
}
@Test
public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
// the config orientation should not be undefined, since it would cause config.diff
// returning 0 and thus the orientation changed would not be detected
- assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation);
+ assertThat(mResources.getConfiguration().orientation).isNotEqualTo(ORIENTATION_UNDEFINED);
final Configuration config = mResources.getConfiguration();
config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
@@ -1091,7 +1160,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
Float.NaN, Float.NaN));
- assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
}
@Test
@@ -1119,7 +1188,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
});
- assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
+ assertThat(getAccessibilityWindowTitle()).isEqualTo(newA11yWindowTitle);
}
@Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
@@ -1134,7 +1203,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.onSingleTap(mSpyView);
});
- final View mirrorView = mWindowManager.getAttachedView();
+ final View mirrorView = mSurfaceControlViewHost.getView();
final AtomicDouble maxScaleX = new AtomicDouble();
advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
@@ -1142,10 +1211,10 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
// maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
final double oldMax = maxScaleX.get();
final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
- assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+ assertThat(maxScaleX.compareAndSet(oldMax, newMax)).isTrue();
});
- assertTrue(maxScaleX.get() > 1.0);
+ assertThat(maxScaleX.get()).isGreaterThan(1.0);
}
@Test
@@ -1174,30 +1243,23 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.updateWindowMagnificationInternal(
Float.NaN, Float.NaN, Float.NaN);
});
+ // Wait for Region updated.
+ waitForIdleSync();
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0);
});
-
// Wait for Region updated.
waitForIdleSync();
- final ArgumentCaptor<Region> tapExcludeRegionCapturer =
- ArgumentCaptor.forClass(Region.class);
- verify(mWindowSessionSpy, times(2))
- .updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
- Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
- RegionIterator iterator = new RegionIterator(tapExcludeRegion);
+ AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
+ // Verifying two times in: (1) enable window magnification (2) reposition drag handle
+ verify(viewRoot, times(2)).setTouchableRegion(any());
- final Rect topRect = new Rect();
- final Rect bottomRect = new Rect();
- assertTrue(iterator.next(topRect));
- assertTrue(iterator.next(bottomRect));
- assertFalse(iterator.next(new Rect()));
-
- assertEquals(topRect.right, bottomRect.right);
- assertNotEquals(topRect.left, bottomRect.left);
+ View dragButton = getInternalView(R.id.drag_handle);
+ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
+ assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.LEFT);
}
@Test
@@ -1210,29 +1272,23 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.updateWindowMagnificationInternal(
Float.NaN, Float.NaN, Float.NaN);
});
+ // Wait for Region updated.
+ waitForIdleSync();
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0);
});
-
// Wait for Region updated.
waitForIdleSync();
- final ArgumentCaptor<Region> tapExcludeRegionCapturer =
- ArgumentCaptor.forClass(Region.class);
- verify(mWindowSessionSpy).updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
- Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
- RegionIterator iterator = new RegionIterator(tapExcludeRegion);
-
- final Rect topRect = new Rect();
- final Rect bottomRect = new Rect();
- assertTrue(iterator.next(topRect));
- assertTrue(iterator.next(bottomRect));
- assertFalse(iterator.next(new Rect()));
+ AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
+ // Verifying one times in: (1) enable window magnification
+ verify(viewRoot).setTouchableRegion(any());
- assertEquals(topRect.left, bottomRect.left);
- assertNotEquals(topRect.right, bottomRect.right);
+ View dragButton = getInternalView(R.id.drag_handle);
+ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
+ assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.RIGHT);
}
@Test
@@ -1249,13 +1305,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final AtomicInteger actualWindowWidth = new AtomicInteger();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(expectedWindowHeight, actualWindowHeight.get());
- assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+ assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
}
@Test
@@ -1271,12 +1327,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
Float.NaN, Float.NaN);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(expectedWindowHeight, actualWindowHeight.get());
- assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+ assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
}
@Test
@@ -1292,12 +1348,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
minimumWindowSize - 10);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(minimumWindowSize, actualWindowHeight.get());
- assertEquals(minimumWindowSize, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(minimumWindowSize);
+ assertThat(actualWindowWidth.get()).isEqualTo(minimumWindowSize);
}
@Test
@@ -1311,12 +1367,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final AtomicInteger actualWindowWidth = new AtomicInteger();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(bounds.height(), actualWindowHeight.get());
- assertEquals(bounds.width(), actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(bounds.height());
+ assertThat(actualWindowWidth.get()).isEqualTo(bounds.width());
}
@Test
@@ -1342,12 +1398,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mWindowMagnificationController.changeMagnificationSize(
WindowMagnificationSettings.MagnificationSize.LARGE);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(expectedWindowHeight, actualWindowHeight.get());
- assertEquals(expectedWindowWidth, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+ assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
}
@Test
@@ -1376,12 +1434,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
() -> {
mWindowMagnificationController
.onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(startingSize + 1, actualWindowHeight.get());
- assertEquals(startingSize + 2, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
+ assertThat(actualWindowWidth.get()).isEqualTo(startingSize + 2);
}
@Test
@@ -1404,11 +1464,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.setEditMagnifierSizeMode(true);
mWindowMagnificationController
.onDrag(getInternalView(R.id.bottom_handle), 2f, 1f);
- actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
- actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+ actualWindowHeight.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().height);
+ actualWindowWidth.set(
+ mSurfaceControlViewHost.getView().getLayoutParams().width);
});
- assertEquals(startingSize + 1, actualWindowHeight.get());
- assertEquals(startingSize, actualWindowWidth.get());
+ assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
+ assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
}
@Test
@@ -1430,8 +1492,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
});
- assertTrue(magnificationCenterX.get() < bounds.right);
- assertTrue(magnificationCenterY.get() < bounds.bottom);
+ assertThat(magnificationCenterX.get()).isLessThan(bounds.right);
+ assertThat(magnificationCenterY.get()).isLessThan(bounds.bottom);
}
@Test
@@ -1451,13 +1513,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
dragButton.dispatchTouchEvent(
obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
- verify(mWindowManager).addView(any(View.class), any());
+ verify(mSurfaceControlViewHost).setView(any(View.class), any());
}
private <T extends View> T getInternalView(@IdRes int idRes) {
- View mirrorView = mWindowManager.getAttachedView();
+ View mirrorView = mSurfaceControlViewHost.getView();
T view = mirrorView.findViewById(idRes);
- assertNotNull(view);
+ assertThat(view).isNotNull();
return view;
}
@@ -1466,14 +1528,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
}
- private CharSequence getAccessibilityWindowTitle() {
- final View mirrorView = mWindowManager.getAttachedView();
+ private String getAccessibilityWindowTitle() {
+ final View mirrorView = mSurfaceControlViewHost.getView();
if (mirrorView == null) {
return null;
}
WindowManager.LayoutParams layoutParams =
(WindowManager.LayoutParams) mirrorView.getLayoutParams();
- return layoutParams.accessibilityTitle;
+ return layoutParams.accessibilityTitle.toString();
}
private boolean hasMagnificationOverlapFlag() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
index 176c3ac43936..2594472a9c8a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
@@ -22,12 +22,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -40,8 +41,6 @@ import org.mockito.junit.MockitoRule
@RunWith(AndroidJUnit4::class)
class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testDispatcher = kosmos.testDispatcher
- private val testScope = kosmos.testScope
private val secureSettings = kosmos.fakeSettings
@Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
@@ -55,8 +54,8 @@ class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
return UserA11yQsShortcutsRepository(
userId,
secureSettings,
- testScope.backgroundScope,
- testDispatcher,
+ kosmos.testScope.backgroundScope,
+ kosmos.testDispatcher,
)
}
}
@@ -69,13 +68,13 @@ class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
AccessibilityQsShortcutsRepositoryImpl(
a11yManager,
userA11yQsShortcutsRepositoryFactory,
- testDispatcher
+ kosmos.testDispatcher,
)
}
@Test
fun a11yQsShortcutTargetsForCorrectUsers() =
- testScope.runTest {
+ kosmos.runTest {
val user0 = 0
val targetsForUser0 = setOf("a", "b", "c")
val user1 = 1
@@ -94,7 +93,7 @@ class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
secureSettings.putStringForUser(
SETTING_NAME,
a11yQsTargets.joinToString(separator = ":"),
- forUser
+ forUser,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index a8c3af9488f0..8db82d58ecc5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -24,8 +24,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
-class
-ViewHierarchyAnimatorTest : SysuiTestCase() {
+class ViewHierarchyAnimatorTest : SysuiTestCase() {
companion object {
private const val TEST_DURATION = 1000L
private val TEST_INTERPOLATOR = Interpolators.LINEAR
@@ -49,9 +48,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
// animate()
- var success = ViewHierarchyAnimator.animate(
- rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
- )
+ var success =
+ ViewHierarchyAnimator.animate(
+ rootView,
+ interpolator = TEST_INTERPOLATOR,
+ duration = TEST_DURATION,
+ )
rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -64,9 +66,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.stopAnimating(rootView)
// animateNextUpdate()
- success = ViewHierarchyAnimator.animateNextUpdate(
- rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
- )
+ success =
+ ViewHierarchyAnimator.animateNextUpdate(
+ rootView,
+ interpolator = TEST_INTERPOLATOR,
+ duration = TEST_DURATION,
+ )
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
assertTrue(success)
@@ -79,9 +84,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// animateAddition()
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ interpolator = TEST_INTERPOLATOR,
+ duration = TEST_DURATION,
+ )
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
assertTrue(success)
@@ -93,9 +101,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// animateRemoval()
setUpRootWithChildren()
val child = rootView.getChildAt(0)
- success = ViewHierarchyAnimator.animateRemoval(
- child, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ child,
+ interpolator = TEST_INTERPOLATOR,
+ duration = TEST_DURATION,
+ )
assertTrue(success)
assertNotNull(child.getTag(R.id.tag_animator))
@@ -185,7 +196,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// Change all bounds.
rootView.measure(
View.MeasureSpec.makeMeasureSpec(190, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
)
rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
@@ -211,14 +222,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
fun animatesRootAndChildren_withExcludedViews() {
setUpRootWithChildren()
- val success = ViewHierarchyAnimator.animate(
- rootView,
- excludedViews = setOf(rootView.getChildAt(0))
- )
+ val success =
+ ViewHierarchyAnimator.animate(rootView, excludedViews = setOf(rootView.getChildAt(0)))
// Change all bounds.
rootView.measure(
- View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
)
rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
@@ -245,14 +254,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
fun animatesRootOnly() {
setUpRootWithChildren()
- val success = ViewHierarchyAnimator.animate(
- rootView,
- animateChildren = false
- )
+ val success = ViewHierarchyAnimator.animate(rootView, animateChildren = false)
// Change all bounds.
rootView.measure(
- View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
)
rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
@@ -351,10 +357,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
fun animatesAppearingViewsRespectingOrigin() {
// CENTER.
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- var success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.CENTER
- )
+ var success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.CENTER,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -364,10 +371,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// LEFT.
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.LEFT
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.LEFT,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -377,10 +385,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// TOP_LEFT.
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.TOP_LEFT
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -390,10 +399,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// TOP.
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.TOP
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.TOP,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -403,10 +413,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// TOP_RIGHT.
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.TOP_RIGHT
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -416,10 +427,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// RIGHT.
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.RIGHT
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.RIGHT,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -429,10 +441,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// BOTTOM_RIGHT.
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -442,10 +455,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// BOTTOM.
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.BOTTOM
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.BOTTOM,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -455,10 +469,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// BOTTOM_LEFT.
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -471,11 +486,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
fun animatesAppearingViewsRespectingMargins() {
// CENTER.
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- var success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.CENTER,
- includeMargins = true
- )
+ var success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.CENTER,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -485,10 +501,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// LEFT.
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView, origin = ViewHierarchyAnimator.Hotspot.LEFT,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.LEFT,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -498,11 +516,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// TOP_LEFT.
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -512,10 +531,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// TOP.
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView, origin = ViewHierarchyAnimator.Hotspot.TOP,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.TOP,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -525,11 +546,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// TOP_RIGHT.
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -539,11 +561,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// RIGHT.
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.RIGHT,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.RIGHT,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -553,11 +576,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// BOTTOM_RIGHT.
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -567,11 +591,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// BOTTOM.
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.BOTTOM,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.BOTTOM,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -581,11 +606,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// BOTTOM_LEFT.
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
- success = ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
- includeMargins = true
- )
+ success =
+ ViewHierarchyAnimator.animateAddition(
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
+ includeMargins = true,
+ )
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
assertTrue(success)
@@ -626,7 +652,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.animateAddition(
rootView,
includeFadeIn = true,
- fadeInInterpolator = Interpolators.LINEAR
+ fadeInInterpolator = Interpolators.LINEAR,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -641,7 +667,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.animateAddition(
rootView,
includeFadeIn = true,
- fadeInInterpolator = Interpolators.LINEAR
+ fadeInInterpolator = Interpolators.LINEAR,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -663,7 +689,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.animateAddition(
rootView,
includeFadeIn = true,
- fadeInInterpolator = Interpolators.LINEAR
+ fadeInInterpolator = Interpolators.LINEAR,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -680,7 +706,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.animateAddition(
rootView,
includeFadeIn = true,
- fadeInInterpolator = Interpolators.LINEAR
+ fadeInInterpolator = Interpolators.LINEAR,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -692,7 +718,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.animateAddition(
rootView,
includeFadeIn = true,
- fadeInInterpolator = Interpolators.LINEAR
+ fadeInInterpolator = Interpolators.LINEAR,
)
// THEN the alpha remains at its current value (it doesn't get reset to 0)
@@ -721,7 +747,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.animateAddition(
rootView,
includeFadeIn = false,
- fadeInInterpolator = Interpolators.LINEAR
+ fadeInInterpolator = Interpolators.LINEAR,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -738,10 +764,10 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val onAnimationEndRunnable = { runnableRun = true }
ViewHierarchyAnimator.animateAddition(
- rootView,
- origin = ViewHierarchyAnimator.Hotspot.CENTER,
- includeMargins = true,
- onAnimationEnd = onAnimationEndRunnable
+ rootView,
+ origin = ViewHierarchyAnimator.Hotspot.CENTER,
+ includeMargins = true,
+ onAnimationEnd = onAnimationEndRunnable,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -751,7 +777,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
}
@Test
- fun animateAddition_runnableDoesNotRunWhenAnimationCancelled() {
+ fun animateAddition_runnableRunsWhenAnimationCancelled() {
var runnableRun = false
val onAnimationEndRunnable = { runnableRun = true }
@@ -759,13 +785,13 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
rootView,
origin = ViewHierarchyAnimator.Hotspot.CENTER,
includeMargins = true,
- onAnimationEnd = onAnimationEndRunnable
+ onAnimationEnd = onAnimationEndRunnable,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
cancelAnimation(rootView)
- assertEquals(false, runnableRun)
+ assertEquals(true, runnableRun)
}
@Test
@@ -777,7 +803,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
rootView,
origin = ViewHierarchyAnimator.Hotspot.CENTER,
includeMargins = true,
- onAnimationEnd = onAnimationEndRunnable
+ onAnimationEnd = onAnimationEndRunnable,
)
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
@@ -791,11 +817,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
val child = rootView.getChildAt(0)
- val success = ViewHierarchyAnimator.animateRemoval(
- child,
- destination = ViewHierarchyAnimator.Hotspot.LEFT,
- interpolator = Interpolators.LINEAR
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ child,
+ destination = ViewHierarchyAnimator.Hotspot.LEFT,
+ interpolator = Interpolators.LINEAR,
+ )
assertTrue(success)
assertNotNull(child.getTag(R.id.tag_animator))
@@ -820,11 +847,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
rootView.addView(onlyChild)
forceLayout()
- val success = ViewHierarchyAnimator.animateRemoval(
- onlyChild,
- destination = ViewHierarchyAnimator.Hotspot.LEFT,
- interpolator = Interpolators.LINEAR
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ onlyChild,
+ destination = ViewHierarchyAnimator.Hotspot.LEFT,
+ interpolator = Interpolators.LINEAR,
+ )
assertTrue(success)
assertNotNull(onlyChild.getTag(R.id.tag_animator))
@@ -845,9 +873,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
var removedChild = rootView.getChildAt(0)
var remainingChild = rootView.getChildAt(1)
- var success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.CENTER
- )
+ var success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.CENTER,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -863,9 +893,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.LEFT
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.LEFT,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -881,9 +913,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -899,9 +933,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.TOP,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -917,9 +953,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -935,9 +973,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.RIGHT
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.RIGHT,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -953,9 +993,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -971,9 +1013,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.BOTTOM,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -989,9 +1033,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
setUpRootWithChildren()
removedChild = rootView.getChildAt(0)
remainingChild = rootView.getChildAt(1)
- success = ViewHierarchyAnimator.animateRemoval(
- removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT
- )
+ success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
+ )
// Ensure that the layout happens before the checks.
forceLayout()
@@ -1014,11 +1060,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalRight = removedChild.right
val originalBottom = removedChild.bottom
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.CENTER,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.CENTER,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1027,13 +1074,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val expectedX = ((originalLeft - M_LEFT) + (originalRight + M_RIGHT)) / 2
val expectedY = ((originalTop - M_TOP) + (originalBottom + M_BOTTOM)) / 2
- checkBounds(
- removedChild,
- l = expectedX,
- t = expectedY,
- r = expectedX,
- b = expectedY
- )
+ checkBounds(removedChild, l = expectedX, t = expectedY, r = expectedX, b = expectedY)
}
@Test
@@ -1044,11 +1085,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalTop = removedChild.top
val originalBottom = removedChild.bottom
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.LEFT,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.LEFT,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1059,7 +1101,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalLeft - M_LEFT,
t = originalTop,
r = originalLeft - M_LEFT,
- b = originalBottom
+ b = originalBottom,
)
}
@@ -1070,11 +1112,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalLeft = removedChild.left
val originalTop = removedChild.top
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1085,7 +1128,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalLeft - M_LEFT,
t = originalTop - M_TOP,
r = originalLeft - M_LEFT,
- b = originalTop - M_TOP
+ b = originalTop - M_TOP,
)
}
@@ -1097,11 +1140,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalTop = removedChild.top
val originalRight = removedChild.right
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.TOP,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.TOP,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1112,7 +1156,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalLeft,
t = originalTop - M_TOP,
r = originalRight,
- b = originalTop - M_TOP
+ b = originalTop - M_TOP,
)
}
@@ -1123,11 +1167,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalTop = removedChild.top
val originalRight = removedChild.right
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1138,7 +1183,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalRight + M_RIGHT,
t = originalTop - M_TOP,
r = originalRight + M_RIGHT,
- b = originalTop - M_TOP
+ b = originalTop - M_TOP,
)
}
@@ -1150,11 +1195,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalRight = removedChild.right
val originalBottom = removedChild.bottom
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.RIGHT,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.RIGHT,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1165,7 +1211,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalRight + M_RIGHT,
t = originalTop,
r = originalRight + M_RIGHT,
- b = originalBottom
+ b = originalBottom,
)
}
@@ -1176,11 +1222,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalRight = removedChild.right
val originalBottom = removedChild.bottom
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1191,7 +1238,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalRight + M_RIGHT,
t = originalBottom + M_BOTTOM,
r = originalRight + M_RIGHT,
- b = originalBottom + M_BOTTOM
+ b = originalBottom + M_BOTTOM,
)
}
@@ -1203,11 +1250,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalRight = removedChild.right
val originalBottom = removedChild.bottom
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.BOTTOM,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.BOTTOM,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1218,7 +1266,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalLeft,
t = originalBottom + M_BOTTOM,
r = originalRight,
- b = originalBottom + M_BOTTOM
+ b = originalBottom + M_BOTTOM,
)
}
@@ -1229,11 +1277,12 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val originalLeft = removedChild.left
val originalBottom = removedChild.bottom
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild,
- destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
- includeMargins = true,
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(
+ removedChild,
+ destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
+ includeMargins = true,
+ )
forceLayout()
assertTrue(success)
@@ -1244,9 +1293,10 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
l = originalLeft - M_LEFT,
t = originalBottom + M_BOTTOM,
r = originalLeft - M_LEFT,
- b = originalBottom + M_BOTTOM
+ b = originalBottom + M_BOTTOM,
)
}
+
/* ******** end of animatesViewRemoval_includeMarginsTrue tests ******** */
@Test
@@ -1256,9 +1306,8 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val child = rootView.getChildAt(0) as ViewGroup
val firstGrandChild = child.getChildAt(0)
val secondGrandChild = child.getChildAt(1)
- val success = ViewHierarchyAnimator.animateRemoval(
- child, interpolator = Interpolators.LINEAR
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(child, interpolator = Interpolators.LINEAR)
assertTrue(success)
assertNotNull(child.getTag(R.id.tag_animator))
@@ -1288,9 +1337,8 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val removedChild = rootView.getChildAt(0)
val remainingChild = rootView.getChildAt(1)
- val success = ViewHierarchyAnimator.animateRemoval(
- removedChild, interpolator = Interpolators.LINEAR
- )
+ val success =
+ ViewHierarchyAnimator.animateRemoval(removedChild, interpolator = Interpolators.LINEAR)
// Ensure that the layout happens before the checks.
forceLayout()
@@ -1315,17 +1363,14 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
forceLayout()
val removedView = rootView.getChildAt(0)
- ViewHierarchyAnimator.animateRemoval(
- removedView,
- onAnimationEnd = onAnimationEndRunnable
- )
+ ViewHierarchyAnimator.animateRemoval(removedView, onAnimationEnd = onAnimationEndRunnable)
endAnimation(removedView)
assertEquals(true, runnableRun)
}
@Test
- fun animateRemoval_runnableDoesNotRunWhenAnimationCancelled() {
+ fun animateRemoval_runnableRunsWhenAnimationCancelled() {
var runnableRun = false
val onAnimationEndRunnable = { runnableRun = true }
@@ -1333,13 +1378,10 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
forceLayout()
val removedView = rootView.getChildAt(0)
- ViewHierarchyAnimator.animateRemoval(
- removedView,
- onAnimationEnd = onAnimationEndRunnable
- )
+ ViewHierarchyAnimator.animateRemoval(removedView, onAnimationEnd = onAnimationEndRunnable)
cancelAnimation(removedView)
- assertEquals(false, runnableRun)
+ assertEquals(true, runnableRun)
}
@Test
@@ -1351,10 +1393,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
forceLayout()
val removedView = rootView.getChildAt(0)
- ViewHierarchyAnimator.animateRemoval(
- removedView,
- onAnimationEnd = onAnimationEndRunnable
- )
+ ViewHierarchyAnimator.animateRemoval(removedView, onAnimationEnd = onAnimationEndRunnable)
advanceAnimation(removedView, 0.5f)
assertEquals(false, runnableRun)
@@ -1370,7 +1409,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
rootView.addView(secondChild)
rootView.measure(
View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
)
rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
@@ -1378,7 +1417,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
// Change all bounds.
rootView.measure(
View.MeasureSpec.makeMeasureSpec(150, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
)
rootView.layout(0 /* l */, 0 /* t */, 150 /* r */, 100 /* b */)
@@ -1501,7 +1540,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
checkBounds(rootView, l = 0, t = 15, r = 70, b = 80)
// Change all bounds again.
- rootView.layout(10 /* l */, 10 /* t */, 50/* r */, 50 /* b */)
+ rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
assertNull(rootView.getTag(R.id.tag_animator))
checkBounds(rootView, l = 10, t = 10, r = 50, b = 50)
@@ -1523,7 +1562,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
ViewHierarchyAnimator.stopAnimating(rootView)
// Change all bounds again.
- rootView.layout(10 /* l */, 10 /* t */, 50/* r */, 50 /* b */)
+ rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
assertNull(rootView.getTag(R.id.tag_animator))
checkBounds(rootView, l = 10, t = 10, r = 50, b = 50)
@@ -1543,10 +1582,8 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
val secondChild = View(mContext)
rootView.addView(secondChild)
- val firstChildParams = LinearLayout.LayoutParams(
- 0 /* width */,
- LinearLayout.LayoutParams.MATCH_PARENT
- )
+ val firstChildParams =
+ LinearLayout.LayoutParams(0 /* width */, LinearLayout.LayoutParams.MATCH_PARENT)
firstChildParams.weight = 0.5f
if (includeMarginsOnFirstChild) {
firstChildParams.leftMargin = M_LEFT
@@ -1556,23 +1593,25 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
}
firstChild.layoutParams = firstChildParams
- val secondChildParams = LinearLayout.LayoutParams(
- 0 /* width */,
- LinearLayout.LayoutParams.MATCH_PARENT
- )
+ val secondChildParams =
+ LinearLayout.LayoutParams(0 /* width */, LinearLayout.LayoutParams.MATCH_PARENT)
secondChildParams.weight = 0.5f
secondChild.layoutParams = secondChildParams
firstGrandChild.layoutParams = RelativeLayout.LayoutParams(40 /* width */, 40 /* height */)
- (firstGrandChild.layoutParams as RelativeLayout.LayoutParams)
- .addRule(RelativeLayout.ALIGN_PARENT_START)
- (firstGrandChild.layoutParams as RelativeLayout.LayoutParams)
- .addRule(RelativeLayout.ALIGN_PARENT_TOP)
+ (firstGrandChild.layoutParams as RelativeLayout.LayoutParams).addRule(
+ RelativeLayout.ALIGN_PARENT_START
+ )
+ (firstGrandChild.layoutParams as RelativeLayout.LayoutParams).addRule(
+ RelativeLayout.ALIGN_PARENT_TOP
+ )
secondGrandChild.layoutParams = RelativeLayout.LayoutParams(40 /* width */, 40 /* height */)
- (secondGrandChild.layoutParams as RelativeLayout.LayoutParams)
- .addRule(RelativeLayout.ALIGN_PARENT_END)
- (secondGrandChild.layoutParams as RelativeLayout.LayoutParams)
- .addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
+ (secondGrandChild.layoutParams as RelativeLayout.LayoutParams).addRule(
+ RelativeLayout.ALIGN_PARENT_END
+ )
+ (secondGrandChild.layoutParams as RelativeLayout.LayoutParams).addRule(
+ RelativeLayout.ALIGN_PARENT_BOTTOM
+ )
forceLayout()
}
@@ -1580,7 +1619,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
private fun forceLayout() {
rootView.measure(
View.MeasureSpec.makeMeasureSpec(200 /* width */, View.MeasureSpec.AT_MOST),
- View.MeasureSpec.makeMeasureSpec(100 /* height */, View.MeasureSpec.AT_MOST)
+ View.MeasureSpec.makeMeasureSpec(100 /* height */, View.MeasureSpec.AT_MOST),
)
rootView.layout(0 /* l */, 0 /* t */, 200 /* r */, 100 /* b */)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8c7cd619a158..cdda9ccc9b9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -47,8 +47,7 @@ import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.concurrency.FakeExecutor
@@ -61,7 +60,6 @@ import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Rule
@@ -95,9 +93,6 @@ class BackActionInteractorTest : SysuiTestCase() {
@Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
@Mock private lateinit var iStatusBarService: IStatusBarService
@Mock private lateinit var headsUpManager: HeadsUpManager
- private val activeNotificationsRepository = ActiveNotificationListRepository()
- private val activeNotificationsInteractor =
- ActiveNotificationsInteractor(activeNotificationsRepository, StandardTestDispatcher())
private val keyguardRepository = FakeKeyguardRepository()
private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
@@ -107,7 +102,7 @@ class BackActionInteractorTest : SysuiTestCase() {
keyguardRepository,
headsUpManager,
powerInteractor,
- activeNotificationsInteractor,
+ kosmos.activeNotificationsInteractor,
kosmos::sceneInteractor,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
index e1421691a92d..58fe2c9cbe57 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnit
private const val USER_ID = 22
private const val OWNER_ID = 10
+private const val PASSWORD_ID = 30
private const val OPERATION_ID = 100L
private const val MAX_ATTEMPTS = 5
@@ -247,7 +248,11 @@ class CredentialInteractorImplTest : SysuiTestCase() {
private fun pinRequest(credentialOwner: Int = USER_ID): BiometricPromptRequest.Credential.Pin =
BiometricPromptRequest.Credential.Pin(
promptInfo(),
- BiometricUserInfo(userId = USER_ID, deviceCredentialOwnerId = credentialOwner),
+ BiometricUserInfo(
+ userId = USER_ID,
+ deviceCredentialOwnerId = credentialOwner,
+ userIdForPasswordEntry = PASSWORD_ID,
+ ),
BiometricOperationInfo(OPERATION_ID),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index c803097134de..d75c0138bcbf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -36,6 +36,7 @@ import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.HapticFeedbackConstants
@@ -97,13 +98,14 @@ import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
private const val USER_ID = 4
+private const val WORK_USER_ID = 100
private const val REQUEST_ID = 4L
private const val CHALLENGE = 2L
private const val DELAY = 1000L
+private const val OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO = "should.use.activiy.logo"
private const val OP_PACKAGE_NAME_WITH_APP_LOGO = "biometric.testapp"
-private const val OP_PACKAGE_NAME_NO_ICON = "biometric.testapp.noicon"
+private const val OP_PACKAGE_NAME_NO_LOGO_INFO = "biometric.testapp.nologoinfo"
private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found"
-private const val OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO = "should.use.activiy.logo"
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -120,13 +122,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
private val defaultLogoIconFromAppInfo = context.getDrawable(R.drawable.ic_android)
private val defaultLogoIconFromActivityInfo = context.getDrawable(R.drawable.ic_add)
+ private val defaultLogoIconWithBadge = context.getDrawable(R.drawable.ic_alarm)
private val logoResFromApp = R.drawable.ic_cake
private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp)
private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
private val defaultLogoDescriptionFromAppInfo = "Test Android App"
private val defaultLogoDescriptionFromActivityInfo = "Test Coke App"
+ private val defaultLogoDescriptionWithBadge = "Work app"
private val logoDescriptionFromApp = "Test Cake App"
- private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
+
private val authInteractionProperties = AuthInteractionProperties()
/** Prompt panel size padding */
@@ -173,55 +177,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Before
fun setup() {
- // Set up default logo info and app customized info
- whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
- .thenReturn(applicationInfoNoIconOrDescription)
- whenever(
- kosmos.packageManager.getApplicationInfo(
- eq(OP_PACKAGE_NAME_WITH_APP_LOGO),
- anyInt(),
- )
- )
- .thenReturn(applicationInfoWithIconAndDescription)
- whenever(
- kosmos.packageManager.getApplicationInfo(
- eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
- anyInt(),
- )
- )
- .thenReturn(applicationInfoWithIconAndDescription)
- whenever(
- kosmos.packageManager.getApplicationInfo(
- eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
- anyInt(),
- )
- )
- .thenThrow(NameNotFoundException())
-
- whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
- whenever(kosmos.iconProvider.getIcon(activityInfo))
- .thenReturn(defaultLogoIconFromActivityInfo)
- whenever(activityInfo.loadLabel(kosmos.packageManager))
- .thenReturn(defaultLogoDescriptionFromActivityInfo)
-
- whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIconAndDescription))
- .thenReturn(defaultLogoIconFromAppInfo)
- whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIconAndDescription))
- .thenReturn(defaultLogoDescriptionFromAppInfo)
- whenever(kosmos.packageManager.getApplicationIcon(applicationInfoNoIconOrDescription))
- .thenReturn(null)
- whenever(kosmos.packageManager.getApplicationLabel(applicationInfoNoIconOrDescription))
- .thenReturn("")
- whenever(kosmos.packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
- whenever(kosmos.packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
-
- context.setMockPackageManager(kosmos.packageManager)
- overrideResource(logoResFromApp, logoDrawableFromAppRes)
- overrideResource(
- R.array.config_useActivityLogoForBiometricPrompt,
- arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
- )
-
+ setupLogo()
overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth)
overrideResource(
R.dimen.biometric_dialog_fingerprint_icon_height,
@@ -264,6 +220,74 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
.build()
}
+ private fun setupLogo() {
+ // Set up app customized logo
+ overrideResource(logoResFromApp, logoDrawableFromAppRes)
+
+ // Set up when activity info should be used
+ overrideResource(
+ R.array.config_useActivityLogoForBiometricPrompt,
+ arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
+ )
+ whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
+ whenever(kosmos.iconProvider.getIcon(activityInfo))
+ .thenReturn(defaultLogoIconFromActivityInfo)
+ whenever(activityInfo.loadLabel(kosmos.packageManager))
+ .thenReturn(defaultLogoDescriptionFromActivityInfo)
+
+ // Set up when application info should be used for default logo
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(OP_PACKAGE_NAME_WITH_APP_LOGO),
+ anyInt(),
+ )
+ )
+ .thenReturn(applicationInfoWithIconAndDescription)
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
+ anyInt(),
+ )
+ )
+ .thenReturn(applicationInfoWithIconAndDescription)
+ whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIconAndDescription))
+ .thenReturn(defaultLogoIconFromAppInfo)
+ whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIconAndDescription))
+ .thenReturn(defaultLogoDescriptionFromAppInfo)
+
+ // Set up when package name cannot but found
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
+ anyInt(),
+ )
+ )
+ .thenThrow(NameNotFoundException())
+
+ // Set up when no default logo from application info
+ whenever(
+ kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_LOGO_INFO), anyInt())
+ )
+ .thenReturn(applicationInfoNoIconOrDescription)
+ whenever(kosmos.packageManager.getApplicationIcon(applicationInfoNoIconOrDescription))
+ .thenReturn(null)
+ whenever(kosmos.packageManager.getApplicationLabel(applicationInfoNoIconOrDescription))
+ .thenReturn("")
+
+ // Set up work badge
+ whenever(kosmos.packageManager.getUserBadgedIcon(any(), eq(UserHandle.of(USER_ID)))).then {
+ it.getArgument(0)
+ }
+ whenever(kosmos.packageManager.getUserBadgedLabel(any(), eq(UserHandle.of(USER_ID)))).then {
+ it.getArgument(0)
+ }
+ whenever(kosmos.packageManager.getUserBadgedIcon(any(), eq(UserHandle.of(WORK_USER_ID))))
+ .then { defaultLogoIconWithBadge }
+ whenever(kosmos.packageManager.getUserBadgedLabel(any(), eq(UserHandle.of(WORK_USER_ID))))
+ .then { defaultLogoDescriptionWithBadge }
+ context.setMockPackageManager(kosmos.packageManager)
+ }
+
@Test
fun start_idle_and_show_authenticating() =
runGenericTest(doNotStart = true) {
@@ -1520,6 +1544,16 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo).isNotNull()
assertThat(logoInfo!!.first).isNull()
+ assertThat(logoInfo!!.second).isEqualTo("")
+ }
+
+ @Test
+ fun logo_defaultIsNull() =
+ runGenericTest(packageName = OP_PACKAGE_NAME_NO_LOGO_INFO) {
+ val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+ assertThat(logoInfo).isNotNull()
+ assertThat(logoInfo!!.first).isNull()
+ assertThat(logoInfo!!.second).isEqualTo("")
}
@Test
@@ -1527,32 +1561,39 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+ assertThat(logoInfo).isNotNull()
// 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return
// applicationInfoWithIconAndDescription with "defaultLogoIconFromAppInfo",
// 2. iconProvider.getIcon(activityInfo) is set to return
// "defaultLogoIconFromActivityInfo"
// For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1
- assertThat(logoInfo).isNotNull()
assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromActivityInfo)
+ // 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return
+ // applicationInfoWithIconAndDescription with "defaultLogoDescriptionFromAppInfo",
+ // 2. activityInfo.loadLabel() is set to return defaultLogoDescriptionFromActivityInfo
+ // For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1
+ assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromActivityInfo)
}
@Test
- fun logo_defaultIsNull() =
- runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
- assertThat(logoInfo).isNotNull()
- assertThat(logoInfo!!.first).isNull()
- }
-
- @Test
- fun logo_default() = runGenericTest {
+ fun logo_defaultFromApplicationInfo() = runGenericTest {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo).isNotNull()
assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromAppInfo)
+ assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
}
@Test
- fun logo_resSetByApp() =
+ fun logo_defaultWithWorkBadge() =
+ runGenericTest(userId = WORK_USER_ID) {
+ val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+ assertThat(logoInfo).isNotNull()
+ assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconWithBadge)
+ assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionWithBadge)
+ }
+
+ @Test
+ fun logoRes_setByApp() =
runGenericTest(logoRes = logoResFromApp) {
val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1561,44 +1602,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- fun logo_bitmapSetByApp() =
+ fun logoBitmap_setByApp() =
runGenericTest(logoBitmap = logoBitmapFromApp) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat((logoInfo!!.first as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
}
@Test
- fun logoDescription_emptyIfPkgNameNotFound() =
- runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
- assertThat(logoInfo!!.second).isEqualTo("")
- }
-
- @Test
- fun logoDescription_defaultFromActivityInfo() =
- runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
- val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
- // 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
- // applicationInfoWithIconAndDescription with defaultLogoDescription,
- // 2. activityInfo.loadLabel() is set to return defaultLogoDescriptionWithOverrides
- // For the apps with packageNameForLogoWithOverrides, 2 should be called instead of 1
- assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromActivityInfo)
- }
-
- @Test
- fun logoDescription_defaultIsEmpty() =
- runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
- assertThat(logoInfo!!.second).isEqualTo("")
- }
-
- @Test
- fun logoDescription_default() = runGenericTest {
- val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
- assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
- }
-
- @Test
fun logoDescription_setByApp() =
runGenericTest(logoDescription = logoDescriptionFromApp) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1766,6 +1776,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
logoBitmap: Bitmap? = null,
logoDescription: String? = null,
packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO,
+ userId: Int = USER_ID,
block: suspend TestScope.() -> Unit,
) {
val topActivity = ComponentName(packageName, "test app")
@@ -1785,6 +1796,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap,
logoDescriptionFromApp = logoDescription,
packageName = packageName,
+ userId = userId,
)
kosmos.biometricStatusRepository.setFingerprintAcquiredStatus(
@@ -2010,6 +2022,7 @@ private fun PromptSelectorInteractor.initializePrompt(
logoBitmapFromApp: Bitmap? = null,
logoDescriptionFromApp: String? = null,
packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO,
+ userId: Int = USER_ID,
) {
val info =
PromptInfo().apply {
@@ -2028,7 +2041,7 @@ private fun PromptSelectorInteractor.initializePrompt(
setPrompt(
info,
- USER_ID,
+ userId,
REQUEST_ID,
BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face),
CHALLENGE,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
index f58bbc3cf0cf..d3715926932c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
@@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -71,7 +70,7 @@ class BouncerUserActionsViewModelTest : SysuiTestCase() {
assertThat(actions)
.containsEntriesExactly(
Back to UserActionResult(Scenes.QuickSettings),
- Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
+ Swipe.Down to UserActionResult(Scenes.QuickSettings),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
index 09831059a4b3..f4cffc5d962a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
@@ -20,7 +20,7 @@ import android.hardware.display.BrightnessInfo
import android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE
import android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.hardware.display.DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -119,7 +119,8 @@ class ScreenBrightnessDisplayManagerRepositoryTest : SysuiTestCase() {
.registerDisplayListener(
capture(listenerCaptor),
eq(null),
- eq(EVENT_FLAG_DISPLAY_BRIGHTNESS),
+ eq(0),
+ eq(PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS),
)
val newBrightness = BrightnessInfo(0.6f, 0.3f, 0.9f)
@@ -157,7 +158,8 @@ class ScreenBrightnessDisplayManagerRepositoryTest : SysuiTestCase() {
.registerDisplayListener(
capture(listenerCaptor),
eq(null),
- eq(EVENT_FLAG_DISPLAY_BRIGHTNESS),
+ eq(0),
+ eq(PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS),
)
changeBrightnessInfoAndNotify(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
index 116b7054ed5d..7cdfb0eb2451 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
+import com.android.systemui.kosmos.brightnessWarningToast
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
@@ -61,6 +62,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() {
sliderHapticsViewModelFactory,
brightnessMirrorShowingInteractor,
supportsMirroring = true,
+ brightnessWarningToast,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
index 72e0726dedb0..5994afa948c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
@@ -55,7 +55,7 @@ class ConfigurationInteractorTest : SysuiTestCase() {
testableResources.overrideConfiguration(configuration)
configurationRepository = FakeConfigurationRepository()
testScope = TestScope()
- underTest = ConfigurationInteractor(configurationRepository)
+ underTest = ConfigurationInteractorImpl(configurationRepository)
}
@Test
@@ -207,7 +207,7 @@ class ConfigurationInteractorTest : SysuiTestCase() {
updateDisplay(
width = DISPLAY_HEIGHT,
height = DISPLAY_WIDTH,
- rotation = Surface.ROTATION_90
+ rotation = Surface.ROTATION_90,
)
runCurrent()
@@ -217,7 +217,7 @@ class ConfigurationInteractorTest : SysuiTestCase() {
private fun updateDisplay(
width: Int = DISPLAY_WIDTH,
height: Int = DISPLAY_HEIGHT,
- @Surface.Rotation rotation: Int = Surface.ROTATION_0
+ @Surface.Rotation rotation: Int = Surface.ROTATION_0,
) {
configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height))
configuration.windowConfiguration.displayRotation = rotation
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
index 82918a569990..af2d7e45ee5f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
@@ -47,20 +47,14 @@ class CommunalMetricsLoggerTest : SysuiTestCase() {
@Test
fun logAddWidget_componentNotLoggable_doNotLog() {
- underTest.logAddWidget(
- componentName = "com.green.package/my_test_widget",
- rank = 1,
- )
+ underTest.logAddWidget(componentName = "com.green.package/my_test_widget", rank = 1)
verify(statsLogProxy, never())
- .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
}
@Test
fun logAddWidget_componentLoggable_logAddEvent() {
- underTest.logAddWidget(
- componentName = "com.blue.package/my_test_widget",
- rank = 1,
- )
+ underTest.logAddWidget(componentName = "com.blue.package/my_test_widget", rank = 1)
verify(statsLogProxy)
.writeCommunalHubWidgetEventReported(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
@@ -71,20 +65,14 @@ class CommunalMetricsLoggerTest : SysuiTestCase() {
@Test
fun logRemoveWidget_componentNotLoggable_doNotLog() {
- underTest.logRemoveWidget(
- componentName = "com.yellow.package/my_test_widget",
- rank = 2,
- )
+ underTest.logRemoveWidget(componentName = "com.yellow.package/my_test_widget", rank = 2)
verify(statsLogProxy, never())
- .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
}
@Test
fun logRemoveWidget_componentLoggable_logRemoveEvent() {
- underTest.logRemoveWidget(
- componentName = "com.red.package/my_test_widget",
- rank = 2,
- )
+ underTest.logRemoveWidget(componentName = "com.red.package/my_test_widget", rank = 2)
verify(statsLogProxy)
.writeCommunalHubWidgetEventReported(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
@@ -95,20 +83,14 @@ class CommunalMetricsLoggerTest : SysuiTestCase() {
@Test
fun logTapWidget_componentNotLoggable_doNotLog() {
- underTest.logTapWidget(
- componentName = "com.yellow.package/my_test_widget",
- rank = 2,
- )
+ underTest.logTapWidget(componentName = "com.yellow.package/my_test_widget", rank = 2)
verify(statsLogProxy, never())
- .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
}
@Test
fun logTapWidget_componentLoggable_logRemoveEvent() {
- underTest.logTapWidget(
- componentName = "com.red.package/my_test_widget",
- rank = 2,
- )
+ underTest.logTapWidget(componentName = "com.red.package/my_test_widget", rank = 2)
verify(statsLogProxy)
.writeCommunalHubWidgetEventReported(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP,
@@ -140,4 +122,43 @@ class CommunalMetricsLoggerTest : SysuiTestCase() {
)
assertThat(statsEvents).hasSize(1)
}
+
+ @Test
+ fun logResizeWidget_componentNotLoggable_doNotLog() {
+ underTest.logResizeWidget(
+ componentName = "com.green.package/my_test_widget",
+ rank = 1,
+ spanY = 2,
+ )
+ verify(statsLogProxy, never())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
+ }
+
+ @Test
+ fun logResizeWidget_componentLoggable_logResizeEvent() {
+ underTest.logResizeWidget(
+ componentName = "com.blue.package/my_test_widget",
+ rank = 1,
+ spanY = 2,
+ )
+ verify(statsLogProxy)
+ .writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+ "com.blue.package/my_test_widget",
+ rank = 1,
+ spanY = 2,
+ )
+ }
+
+ @Test
+ fun logResizeWidget_defaultSpanY_usesDefaultValue() {
+ underTest.logResizeWidget(componentName = "com.blue.package/my_test_widget", rank = 1)
+ verify(statsLogProxy)
+ .writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+ "com.blue.package/my_test_widget",
+ rank = 1,
+ spanY = 0,
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index cecc11e5ffd4..6b851cb017e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -353,6 +353,32 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
assertThat(event.contentDescription).isEqualTo("Test Clock widget added to lock screen")
}
+ @Test
+ fun onResizeWidget_logsMetrics() =
+ testScope.runTest {
+ val appWidgetId = 123
+ val spanY = 2
+ val widgetIdToRankMap = mapOf(appWidgetId to 1)
+ val componentName = ComponentName("test.package", "TestWidget")
+ val rank = 1
+
+ underTest.onResizeWidget(
+ appWidgetId = appWidgetId,
+ spanY = spanY,
+ widgetIdToRankMap = widgetIdToRankMap,
+ componentName = componentName,
+ rank = rank,
+ )
+
+ verify(communalInteractor).resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+ verify(metricsLogger)
+ .logResizeWidget(
+ componentName = componentName.flattenToString(),
+ rank = rank,
+ spanY = spanY,
+ )
+ }
+
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
index 44ce08514dee..c3c958ca0e94 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
@@ -20,6 +20,8 @@ import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.Intent
+import android.content.IntentSender
+import android.os.Binder
import android.os.UserHandle
import android.testing.TestableLooper
import android.widget.RemoteViews
@@ -29,6 +31,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IConfigureWidgetCallback
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -43,11 +46,13 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -164,7 +169,7 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
}
@Test
- fun addWidget_getWidgetUpdate() =
+ fun addWidget_noConfigurationCallback_getWidgetUpdate() =
testScope.runTest {
setupWidgets()
@@ -180,7 +185,7 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
// Add a widget
- service.addWidget(ComponentName("pkg_4", "cls_4"), UserHandle.of(0), 3)
+ service.addWidget(ComponentName("pkg_4", "cls_4"), UserHandle.of(0), 3, null)
runCurrent()
// Verify an update pushed with widget 4 added
@@ -192,6 +197,71 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
}
@Test
+ fun addWidget_withConfigurationCallback_configurationFails_doNotAddWidget() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Add a widget with a configuration callback that fails
+ service.addWidget(
+ ComponentName("pkg_4", "cls_4"),
+ UserHandle.of(0),
+ 3,
+ createConfigureWidgetCallback(success = false),
+ )
+ runCurrent()
+
+ // Verify that widget 4 is not added
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ }
+
+ @Test
+ fun addWidget_withConfigurationCallback_configurationSucceeds_addWidget() =
+ testScope.runTest {
+ setupWidgets()
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ // Verify the update is as expected
+ val widgets by collectLastValue(service.listenForWidgetUpdates())
+ assertThat(widgets).hasSize(3)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+ // Add a widget with a configuration callback that fails
+ service.addWidget(
+ ComponentName("pkg_4", "cls_4"),
+ UserHandle.of(0),
+ 3,
+ createConfigureWidgetCallback(success = true),
+ )
+ runCurrent()
+
+ // Verify that widget 4 is added
+ assertThat(widgets).hasSize(4)
+ assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+ assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+ assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+ assertThat(widgets?.get(3)?.has(4, "pkg_4/cls_4", 3, 3)).isTrue()
+ }
+
+ @Test
fun deleteWidget_getWidgetUpdate() =
testScope.runTest {
setupWidgets()
@@ -271,6 +341,21 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
}
+ @Test
+ fun getIntentSenderForConfigureActivity() =
+ testScope.runTest {
+ val expected = IntentSender(Binder())
+ whenever(appWidgetHost.getIntentSenderForConfigureActivity(anyInt(), anyInt()))
+ .thenReturn(expected)
+
+ // Bind service
+ val binder = underTest.onBind(Intent())
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+ val actual = service.getIntentSenderForConfigureActivity(1)
+ assertThat(actual).isEqualTo(expected)
+ }
+
private fun setupWidgets() {
widgetRepository.addWidget(
appWidgetId = 1,
@@ -293,7 +378,7 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
}
private fun IGlanceableHubWidgetManagerService.listenForWidgetUpdates() =
- conflatedCallbackFlow<List<CommunalWidgetContentModel>> {
+ conflatedCallbackFlow {
val listener =
object : IGlanceableHubWidgetsListener.Stub() {
override fun onWidgetsUpdated(widgets: List<CommunalWidgetContentModel>) {
@@ -316,4 +401,15 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
this.rank == rank &&
this.spanY == spanY
}
+
+ private fun createConfigureWidgetCallback(success: Boolean): IConfigureWidgetCallback {
+ return object : IConfigureWidgetCallback.Stub() {
+ override fun onConfigureWidget(
+ appWidgetId: Int,
+ resultReceiver: IConfigureWidgetCallback.IResultReceiver?,
+ ) {
+ resultReceiver?.onResult(success)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
index 5d4eaf07be25..e1bdf1c42c9a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
@@ -18,17 +18,18 @@ package com.android.systemui.communal.widgets
import android.app.Activity
import android.content.ActivityNotFoundException
+import android.content.IntentSender
+import android.os.Binder
+import android.os.OutcomeReceiver
import androidx.activity.ComponentActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
@@ -38,15 +39,22 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class WidgetConfigurationControllerTest : SysuiTestCase() {
- @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
- @Mock private lateinit var ownerActivity: ComponentActivity
+ private val appWidgetHost = mock<CommunalAppWidgetHost>()
+ private val ownerActivity = mock<ComponentActivity>()
+
+ private val outcomeReceiverCaptor = argumentCaptor<OutcomeReceiver<IntentSender?, Throwable>>()
private val kosmos = testKosmos()
@@ -54,18 +62,19 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
underTest =
WidgetConfigurationController(
ownerActivity,
{ appWidgetHost },
kosmos.testDispatcher,
kosmos.fakeGlanceableHubMultiUserHelper,
+ { kosmos.mockGlanceableHubWidgetManager },
+ kosmos.fakeExecutor,
)
}
@Test
- fun configurationFailsWhenActivityNotFound() =
+ fun configureWidget_activityNotFound_returnsFalse() =
with(kosmos) {
testScope.runTest {
whenever(
@@ -84,13 +93,97 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
}
@Test
- fun configurationFails() =
+ fun configureWidget_configurationFails_returnsFalse() =
+ with(kosmos) {
+ testScope.runTest {
+ val result = async { underTest.configureWidget(123) }
+ runCurrent()
+ assertThat(result.isCompleted).isFalse()
+
+ underTest.setConfigurationResult(Activity.RESULT_CANCELED)
+ runCurrent()
+
+ assertThat(result.await()).isFalse()
+ result.cancel()
+ }
+ }
+
+ @Test
+ fun configureWidget_configurationSucceeds_returnsTrue() =
+ with(kosmos) {
+ testScope.runTest {
+ val result = async { underTest.configureWidget(123) }
+ runCurrent()
+ assertThat(result.isCompleted).isFalse()
+
+ underTest.setConfigurationResult(Activity.RESULT_OK)
+ runCurrent()
+
+ assertThat(result.await()).isTrue()
+ result.cancel()
+ }
+ }
+
+ @Test
+ fun configureWidget_headlessSystemUser_activityNotFound_returnsFalse() =
+ with(kosmos) {
+ testScope.runTest {
+ fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)
+
+ // Activity not found
+ whenever(
+ mockGlanceableHubWidgetManager.getIntentSenderForConfigureActivity(
+ anyInt(),
+ outcomeReceiverCaptor.capture(),
+ any(),
+ )
+ )
+ .then { outcomeReceiverCaptor.firstValue.onError(ActivityNotFoundException()) }
+
+ val result = async { underTest.configureWidget(123) }
+ runCurrent()
+
+ assertThat(result.await()).isFalse()
+ result.cancel()
+ }
+ }
+
+ @Test
+ fun configureWidget_headlessSystemUser_intentSenderNull_returnsFalse() =
+ with(kosmos) {
+ testScope.runTest {
+ fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)
+
+ prepareIntentSender(null)
+
+ assertThat(underTest.configureWidget(123)).isFalse()
+ }
+ }
+
+ @Test
+ fun configureWidget_headlessSystemUser_configurationFails_returnsFalse() =
with(kosmos) {
testScope.runTest {
+ fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)
+
+ val intentSender = IntentSender(Binder())
+ prepareIntentSender(intentSender)
+
val result = async { underTest.configureWidget(123) }
runCurrent()
assertThat(result.isCompleted).isFalse()
+ verify(ownerActivity)
+ .startIntentSenderForResult(
+ eq(intentSender),
+ eq(WidgetConfigurationController.REQUEST_CODE),
+ anyOrNull(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ any(),
+ )
+
underTest.setConfigurationResult(Activity.RESULT_CANCELED)
runCurrent()
@@ -100,13 +193,29 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
}
@Test
- fun configurationSuccessful() =
+ fun configureWidget_headlessSystemUser_configurationSucceeds_returnsTrue() =
with(kosmos) {
testScope.runTest {
+ fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)
+
+ val intentSender = IntentSender(Binder())
+ prepareIntentSender(intentSender)
+
val result = async { underTest.configureWidget(123) }
runCurrent()
assertThat(result.isCompleted).isFalse()
+ verify(ownerActivity)
+ .startIntentSenderForResult(
+ eq(intentSender),
+ eq(WidgetConfigurationController.REQUEST_CODE),
+ anyOrNull(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ any(),
+ )
+
underTest.setConfigurationResult(Activity.RESULT_OK)
runCurrent()
@@ -114,4 +223,16 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
result.cancel()
}
}
+
+ private fun prepareIntentSender(intentSender: IntentSender?) =
+ with(kosmos) {
+ whenever(
+ mockGlanceableHubWidgetManager.getIntentSenderForConfigureActivity(
+ anyInt(),
+ outcomeReceiverCaptor.capture(),
+ any(),
+ )
+ )
+ .then { outcomeReceiverCaptor.firstValue.onResult(intentSender) }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
index 160865d625f5..f0d79bb83652 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.deviceentry.domain.interactor
import android.content.pm.UserInfo
+import android.os.PowerManager
+import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
@@ -31,22 +33,26 @@ import com.android.systemui.flags.fakeSystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -228,6 +234,8 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
@Test
fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep() =
testScope.runTest {
+ setLockAfterScreenTimeout(0)
+ kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false
val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
@@ -243,35 +251,54 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
}
@Test
- fun deviceUnlockStatus_becomesUnlocked_whenFingerprintUnlocked_whileDeviceAsleepInAod() =
+ fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_afterDelay() =
testScope.runTest {
+ val delay = 5000
+ setLockAfterScreenTimeout(delay)
+ kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false
val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
- assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
- kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- testScope = this,
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
)
+ runCurrent()
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+
kosmos.powerInteractor.setAsleepForTest()
runCurrent()
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+ advanceTimeBy(delay.toLong())
assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
+ }
+
+ @Test
+ fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_powerButtonLocksInstantly() =
+ testScope.runTest {
+ setLockAfterScreenTimeout(5000)
+ kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = true
+ val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
SuccessFingerprintAuthenticationStatus(0, true)
)
runCurrent()
assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+
+ kosmos.powerInteractor.setAsleepForTest(
+ sleepReason = PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
+ )
+ runCurrent()
+
+ assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
}
@Test
- fun deviceUnlockStatus_staysLocked_whenFingerprintUnlocked_whileDeviceAsleep() =
+ fun deviceUnlockStatus_becomesUnlocked_whenFingerprintUnlocked_whileDeviceAsleep() =
testScope.runTest {
+ setLockAfterScreenTimeout(0)
val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
- assertThat(kosmos.keyguardTransitionInteractor.getCurrentState())
- .isEqualTo(KeyguardState.LOCKSCREEN)
kosmos.powerInteractor.setAsleepForTest()
runCurrent()
@@ -282,7 +309,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
SuccessFingerprintAuthenticationStatus(0, true)
)
runCurrent()
- assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
}
@Test
@@ -478,6 +505,98 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
.isEqualTo(DeviceEntryRestrictionReason.DeviceNotUnlockedSinceMainlineUpdate)
}
+ @Test
+ fun deviceUnlockStatus_locksImmediately_whenDreamStarts_noTimeout() =
+ testScope.runTest {
+ setLockAfterScreenTimeout(0)
+ val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
+ unlockDevice()
+
+ startDreaming()
+
+ assertThat(isUnlocked).isFalse()
+ }
+
+ @Test
+ fun deviceUnlockStatus_locksWithDelay_afterDreamStarts_withTimeout() =
+ testScope.runTest {
+ val delay = 5000
+ setLockAfterScreenTimeout(delay)
+ val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
+ unlockDevice()
+
+ startDreaming()
+ assertThat(isUnlocked).isTrue()
+
+ advanceTimeBy(delay - 1L)
+ assertThat(isUnlocked).isTrue()
+
+ advanceTimeBy(1L)
+ assertThat(isUnlocked).isFalse()
+ }
+
+ @Test
+ fun deviceUnlockStatus_doesNotLockWithDelay_whenDreamStopsBeforeTimeout() =
+ testScope.runTest {
+ val delay = 5000
+ setLockAfterScreenTimeout(delay)
+ val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
+ unlockDevice()
+
+ startDreaming()
+ assertThat(isUnlocked).isTrue()
+
+ advanceTimeBy(delay - 1L)
+ assertThat(isUnlocked).isTrue()
+
+ stopDreaming()
+ assertThat(isUnlocked).isTrue()
+
+ advanceTimeBy(1L)
+ assertThat(isUnlocked).isTrue()
+ }
+
+ @Test
+ fun deviceUnlockStatus_doesNotLock_whenDreamStarts_ifNotInteractive() =
+ testScope.runTest {
+ setLockAfterScreenTimeout(0)
+ val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked })
+ unlockDevice()
+
+ startDreaming()
+
+ assertThat(isUnlocked).isFalse()
+ }
+
+ private fun TestScope.unlockDevice() {
+ val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason")
+ runCurrent()
+ }
+
+ private fun setLockAfterScreenTimeout(timeoutMs: Int) {
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ timeoutMs,
+ kosmos.selectedUserInteractor.getSelectedUserId(),
+ )
+ }
+
+ private fun TestScope.startDreaming() {
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ runCurrent()
+ }
+
+ private fun TestScope.stopDreaming() {
+ kosmos.fakeKeyguardRepository.setDreaming(false)
+ runCurrent()
+ }
+
private fun TestScope.verifyRestrictionReasonsForAuthFlags(
vararg authFlagToDeviceEntryRestriction: Pair<Int, DeviceEntryRestrictionReason?>
) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index cd8b2e12a3d5..e6e5665e7694 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -540,7 +540,8 @@ class DisplayRepositoryTest : SysuiTestCase() {
.registerDisplayListener(
connectedDisplayListener.capture(),
eq(testHandler),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED),
+ eq(0),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED),
)
return flowValue
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt
index f8a45e82c2ab..b343def58e29 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt
@@ -31,7 +31,8 @@ import com.android.systemui.controls.panels.authorizedPanelsRepository
import com.android.systemui.controls.panels.selectedComponentRepository
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
+import com.android.systemui.dreams.homecontrols.shared.controlsSettings
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsComponent
import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsListingController
import com.android.systemui.dreams.homecontrols.system.domain.interactor.homeControlsComponentInteractor
@@ -42,13 +43,10 @@ import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -56,6 +54,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -90,13 +89,13 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
fun testRegisterSingleListener() =
testScope.runTest {
setup()
- val controlsSettings by collectLastValue(addCallback())
+ val controlsSettings by collectLastValue(underTest.controlsSettings)
runServicesUpdate()
assertThat(controlsSettings)
.isEqualTo(
- CallbackArgs(
- panelComponent = TEST_COMPONENT,
+ HomeControlsComponentInfo(
+ componentName = TEST_COMPONENT,
allowTrivialControlsOnLockscreen = false,
)
)
@@ -106,21 +105,21 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
fun testRegisterMultipleListeners() =
testScope.runTest {
setup()
- val controlsSettings1 by collectLastValue(addCallback())
- val controlsSettings2 by collectLastValue(addCallback())
+ val controlsSettings1 by collectLastValue(underTest.controlsSettings)
+ val controlsSettings2 by collectLastValue(underTest.controlsSettings)
runServicesUpdate()
assertThat(controlsSettings1)
.isEqualTo(
- CallbackArgs(
- panelComponent = TEST_COMPONENT,
+ HomeControlsComponentInfo(
+ componentName = TEST_COMPONENT,
allowTrivialControlsOnLockscreen = false,
)
)
assertThat(controlsSettings2)
.isEqualTo(
- CallbackArgs(
- panelComponent = TEST_COMPONENT,
+ HomeControlsComponentInfo(
+ componentName = TEST_COMPONENT,
allowTrivialControlsOnLockscreen = false,
)
)
@@ -130,13 +129,13 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
fun testListenerCalledWhenStateChanges() =
testScope.runTest {
setup()
- val controlsSettings by collectLastValue(addCallback())
+ val controlsSettings by collectLastValue(underTest.controlsSettings)
runServicesUpdate()
assertThat(controlsSettings)
.isEqualTo(
- CallbackArgs(
- panelComponent = TEST_COMPONENT,
+ HomeControlsComponentInfo(
+ componentName = TEST_COMPONENT,
allowTrivialControlsOnLockscreen = false,
)
)
@@ -146,13 +145,47 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
// Updated with null component now that we are no longer authorized.
assertThat(controlsSettings)
.isEqualTo(
- CallbackArgs(panelComponent = null, allowTrivialControlsOnLockscreen = false)
+ HomeControlsComponentInfo(
+ componentName = null,
+ allowTrivialControlsOnLockscreen = false,
+ )
)
}
+ @Test
+ fun testDestroy() =
+ testScope.runTest {
+ setup()
+ val controlsSettings1 by collectLastValue(underTest.controlsSettings)
+
+ assertThat(controlsSettings1)
+ .isEqualTo(
+ HomeControlsComponentInfo(
+ componentName = null,
+ allowTrivialControlsOnLockscreen = false,
+ )
+ )
+
+ underTest.onDestroy()
+ runServicesUpdate()
+ fakeControlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
+
+ // Existing callback is not triggered if destroyed.
+ assertThat(controlsSettings1)
+ .isEqualTo(
+ HomeControlsComponentInfo(
+ componentName = null,
+ allowTrivialControlsOnLockscreen = false,
+ )
+ )
+ // New callbacks cannot be added.
+ val controlsSettings2 by collectLastValue(underTest.controlsSettings)
+ assertThat(controlsSettings2).isNull()
+ }
+
private fun TestScope.runServicesUpdate() {
runCurrent()
- val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
+ val listings = listOf(buildControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
val callback = withArgCaptor {
Mockito.verify(kosmos.controlsListingController).addCallback(capture())
}
@@ -160,20 +193,6 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
runCurrent()
}
- private fun addCallback() = conflatedCallbackFlow {
- val callback =
- object : IOnControlsSettingsChangeListener.Stub() {
- override fun onControlsSettingsChanged(
- panelComponent: ComponentName?,
- allowTrivialControlsOnLockscreen: Boolean,
- ) {
- trySend(CallbackArgs(panelComponent, allowTrivialControlsOnLockscreen))
- }
- }
- underTest.registerListenerForCurrentUser(callback)
- awaitClose { underTest.unregisterListenerForCurrentUser(callback) }
- }
-
private suspend fun TestScope.setup() {
kosmos.fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
kosmos.fakeUserTracker.set(listOf(PRIMARY_USER), 0)
@@ -182,12 +201,7 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
runCurrent()
}
- private data class CallbackArgs(
- val panelComponent: ComponentName?,
- val allowTrivialControlsOnLockscreen: Boolean,
- )
-
- private fun ControlsServiceInfo(
+ private fun buildControlsServiceInfo(
componentName: ComponentName,
label: CharSequence,
hasPanel: Boolean,
@@ -225,7 +239,7 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
UserInfo(
/* id= */ PRIMARY_USER_ID,
/* name= */ "primary user",
- /* flags= */ UserInfo.FLAG_PRIMARY,
+ /* flags= */ UserInfo.FLAG_MAIN,
)
private const val TEST_PACKAGE = "pkg"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpManagerTest.kt
index f331060db43e..5827c7b444d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpManagerTest.kt
@@ -23,6 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.table.TableLogBuffer
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
@@ -134,6 +135,21 @@ class DumpManagerTest : SysuiTestCase() {
}
@Test
+ fun registerDumpable_supportsAnonymousDumpables() {
+ val anonDumpable =
+ object : Dumpable {
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("AnonDumpable")
+ }
+ }
+
+ // THEN registration with implicit names should succeed
+ dumpManager.registerCriticalDumpable(anonDumpable)
+
+ // No exception thrown
+ }
+
+ @Test
fun getDumpables_returnsSafeCollection() {
// GIVEN a variety of registered dumpables
dumpManager.registerCriticalDumpable("dumpable1", dumpable1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index 3388a785a26a..20cd8608d204 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -28,11 +28,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.Serializable
@@ -68,6 +70,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
@Mock private lateinit var systemProperties: SystemPropertiesHelper
@Mock private lateinit var resources: Resources
@Mock private lateinit var restarter: Restarter
+ private lateinit var fakeExecutor: FakeExecutor
private lateinit var userTracker: FakeUserTracker
private val flagMap = mutableMapOf<String, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
@@ -83,6 +86,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(releasedFlagB.name, releasedFlagB)
+ fakeExecutor = FakeExecutor(FakeSystemClock())
userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockContext })
mFeatureFlagsClassicDebug =
@@ -95,7 +99,8 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader,
flagMap,
restarter,
- userTracker
+ userTracker,
+ fakeExecutor,
)
mFeatureFlagsClassicDebug.init()
verify(flagManager).onSettingsChangedAction = any()
@@ -325,14 +330,14 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
// trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
mockContext,
- Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "")
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, ""),
)
verifyNoMoreInteractions(flagManager, globalSettings)
// valid id with no value puts empty string in the setting
broadcastReceiver.onReceive(
mockContext,
- Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "1")
+ Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_NAME, "1"),
)
verifyPutData("1", "", numReads = 0)
}
@@ -415,7 +420,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader.setFlagValue(
teamfoodableFlagA.namespace,
teamfoodableFlagA.name,
- !teamfoodableFlagA.default
+ !teamfoodableFlagA.default,
)
verify(restarter, never()).restartSystemUI(anyString())
}
@@ -428,7 +433,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader.setFlagValue(
teamfoodableFlagA.namespace,
teamfoodableFlagA.name,
- !teamfoodableFlagA.default
+ !teamfoodableFlagA.default,
)
verify(restarter).restartSystemUI(anyString())
}
@@ -441,7 +446,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() {
serverFlagReader.setFlagValue(
teamfoodableFlagA.namespace,
teamfoodableFlagA.name,
- teamfoodableFlagA.default
+ teamfoodableFlagA.default,
)
verify(restarter, never()).restartSystemUI(anyString())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
index 5df9b7b8d5b8..2efa2f34e394 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
@@ -85,63 +85,62 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() {
@Test
fun connectKeyboard_delayElapse_launchForKeyboard() =
testScope.runTest {
- launchAndAssert(TutorialType.KEYBOARD)
-
keyboardRepository.setIsAnyKeyboardConnected(true)
advanceTimeBy(LAUNCH_DELAY)
+
+ launchAndAssert(TutorialType.KEYBOARD)
}
@Test
fun connectBothDevices_delayElapse_launchForBoth() =
testScope.runTest {
- launchAndAssert(TutorialType.BOTH)
-
keyboardRepository.setIsAnyKeyboardConnected(true)
touchpadRepository.setIsAnyTouchpadConnected(true)
advanceTimeBy(LAUNCH_DELAY)
+
+ launchAndAssert(TutorialType.BOTH)
}
@Test
fun connectBothDevice_delayNotElapse_launchNothing() =
testScope.runTest {
- launchAndAssert(TutorialType.NONE)
-
keyboardRepository.setIsAnyKeyboardConnected(true)
touchpadRepository.setIsAnyTouchpadConnected(true)
advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+
+ launchAndAssert(TutorialType.NONE)
}
@Test
fun nothingConnect_delayElapse_launchNothing() =
testScope.runTest {
- launchAndAssert(TutorialType.NONE)
-
keyboardRepository.setIsAnyKeyboardConnected(false)
touchpadRepository.setIsAnyTouchpadConnected(false)
advanceTimeBy(LAUNCH_DELAY)
+
+ launchAndAssert(TutorialType.NONE)
}
@Test
fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() =
testScope.runTest {
- launchAndAssert(TutorialType.BOTH)
-
keyboardRepository.setIsAnyKeyboardConnected(true)
advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
touchpadRepository.setIsAnyTouchpadConnected(true)
advanceTimeBy(REMAINING_TIME)
+
+ launchAndAssert(TutorialType.BOTH)
}
@Test
fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() =
testScope.runTest {
- launchAndAssert(TutorialType.NONE)
-
keyboardRepository.setIsAnyKeyboardConnected(true)
advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
touchpadRepository.setIsAnyTouchpadConnected(true)
keyboardRepository.setIsAnyKeyboardConnected(false)
advanceTimeBy(REMAINING_TIME)
+ launchAndAssert(TutorialType.NONE)
}
private suspend fun launchAndAssert(expectedTutorial: TutorialType) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
index 2735d2f03e6a..a0bef727e548 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
@@ -24,7 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
import com.google.common.truth.Truth.assertThat
@@ -59,7 +59,7 @@ class KeyboardDockingIndicationViewModelTest : SysuiTestCase() {
val keyboardDockingIndicationInteractor =
KeyboardDockingIndicationInteractor(keyboardRepository)
- val configurationInteractor = ConfigurationInteractor(configurationRepository)
+ val configurationInteractor = ConfigurationInteractorImpl(configurationRepository)
underTest =
KeyboardDockingIndicationViewModel(
@@ -67,7 +67,7 @@ class KeyboardDockingIndicationViewModelTest : SysuiTestCase() {
context,
keyboardDockingIndicationInteractor,
configurationInteractor,
- testScope.backgroundScope
+ testScope.backgroundScope,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
new file mode 100644
index 000000000000..c646d8f11706
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.createKeyTrigger
+import android.hardware.input.KeyGestureEvent
+import android.hardware.input.fakeInputManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES
+import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
+
+ private val mockUserContext: Context = mock()
+ private val kosmos =
+ testKosmos().also {
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
+ }
+
+ private val fakeInputManager = kosmos.fakeInputManager
+ private val testScope = kosmos.testScope
+ private val helper = kosmos.shortcutHelperTestHelper
+ private val repo = kosmos.customShortcutCategoriesRepository
+
+ @Before
+ fun setup() {
+ whenever(mockUserContext.getSystemService(INPUT_SERVICE))
+ .thenReturn(fakeInputManager.inputManager)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun categories_emitsCorrectlyConvertedShortcutCategories() {
+ testScope.runTest {
+ whenever(
+ fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())
+ )
+ .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations)
+
+ helper.toggle(deviceId = 123)
+ val categories by collectLastValue(repo.categories)
+
+ assertThat(categories)
+ .containsExactlyElementsIn(expectedShortcutCategoriesWithSimpleShortcutCombination)
+ }
+ }
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun categories_emitsEmptyListWhenFlagIsDisabled() {
+ testScope.runTest {
+ whenever(
+ fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())
+ )
+ .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations)
+
+ helper.toggle(deviceId = 123)
+ val categories by collectLastValue(repo.categories)
+
+ assertThat(categories).isEmpty()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun categories_ignoresUnknownKeyGestureTypes() {
+ testScope.runTest {
+ whenever(
+ fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())
+ )
+ .thenReturn(customizableInputGestureWithUnknownKeyGestureType)
+
+ helper.toggle(deviceId = 123)
+ val categories by collectLastValue(repo.categories)
+
+ assertThat(categories).isEmpty()
+ }
+ }
+
+ private fun simpleInputGestureData(
+ keyCode: Int = KeyEvent.KEYCODE_A,
+ modifiers: Int = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ keyGestureType: Int,
+ ): InputGestureData {
+ val builder = InputGestureData.Builder()
+ builder.setKeyGestureType(keyGestureType)
+ builder.setTrigger(createKeyTrigger(keyCode, modifiers))
+ return builder.build()
+ }
+
+ private fun simpleShortcutCategory(
+ category: ShortcutCategoryType,
+ subcategoryLabel: String,
+ shortcutLabel: String,
+ ): ShortcutCategory {
+ return ShortcutCategory(
+ type = category,
+ subCategories =
+ listOf(
+ ShortcutSubCategory(
+ label = subcategoryLabel,
+ shortcuts = listOf(simpleShortcut(shortcutLabel)),
+ )
+ ),
+ )
+ }
+
+ private fun simpleShortcut(label: String) =
+ Shortcut(
+ label = label,
+ commands =
+ listOf(
+ ShortcutCommand(
+ isCustom = true,
+ keys =
+ listOf(
+ ShortcutKey.Text("Ctrl"),
+ ShortcutKey.Text("Alt"),
+ ShortcutKey.Text("A"),
+ ),
+ )
+ ),
+ )
+
+ private val customizableInputGestureWithUnknownKeyGestureType =
+ // These key gesture events are currently not supported by shortcut helper customizer
+ listOf(
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY),
+ )
+
+ private val expectedShortcutCategoriesWithSimpleShortcutCombination =
+ listOf(
+ simpleShortcutCategory(System, "System apps", "Open assistant"),
+ simpleShortcutCategory(System, "System controls", "Go to home screen"),
+ simpleShortcutCategory(System, "System apps", "Open settings"),
+ simpleShortcutCategory(System, "System controls", "Lock screen"),
+ simpleShortcutCategory(System, "System controls", "View notifications"),
+ simpleShortcutCategory(System, "System apps", "Take a note"),
+ simpleShortcutCategory(System, "System controls", "Take screenshot"),
+ simpleShortcutCategory(System, "System controls", "Go back"),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Switch from split screen to full screen",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Use split screen with current app on the left",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Switch to app on left or above while using split screen",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Use split screen with current app on the right",
+ ),
+ simpleShortcutCategory(
+ MultiTasking,
+ "Split screen",
+ "Switch to app on right or below while using split screen",
+ ),
+ simpleShortcutCategory(System, "System controls", "Show shortcuts"),
+ simpleShortcutCategory(System, "System controls", "View recent apps"),
+ simpleShortcutCategory(AppCategories, "Applications", "Calculator"),
+ simpleShortcutCategory(AppCategories, "Applications", "Calendar"),
+ simpleShortcutCategory(AppCategories, "Applications", "Chrome"),
+ simpleShortcutCategory(AppCategories, "Applications", "Contacts"),
+ simpleShortcutCategory(AppCategories, "Applications", "Gmail"),
+ simpleShortcutCategory(AppCategories, "Applications", "Maps"),
+ simpleShortcutCategory(AppCategories, "Applications", "Messages"),
+ simpleShortcutCategory(MultiTasking, "Recent apps", "Cycle forward through recent apps"),
+ )
+
+ private val customizableInputGesturesWithSimpleShortcutCombinations =
+ listOf(
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
+ ),
+ simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
+ ),
+ simpleInputGestureData(
+ keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
+ ),
+ )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
index 620b8b63c71a..f90ab1fcc75b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt
@@ -40,6 +40,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.defaultShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
@@ -47,7 +48,6 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
-import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesRepository
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource
@@ -71,7 +71,7 @@ import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() {
+class DefaultShortcutCategoriesRepositoryTest : SysuiTestCase() {
private val fakeSystemSource = FakeKeyboardShortcutGroupsSource()
private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
@@ -87,7 +87,7 @@ class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() {
it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
}
- private val repo = kosmos.shortcutHelperCategoriesRepository
+ private val repo = kosmos.defaultShortcutCategoriesRepository
private val helper = kosmos.shortcutHelperTestHelper
private val testScope = kosmos.testScope
private val fakeInputManager = kosmos.fakeInputManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index d1431eecfc68..1580ea5c1272 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.shortcutCustomizationDialogStarterFactory
import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -71,7 +72,13 @@ class ShortcutHelperDialogStarterTest : SysuiTestCase() {
private val starter: ShortcutHelperDialogStarter =
with(kosmos) {
- ShortcutHelperDialogStarter(coroutineScope, viewModel, dialogFactory, activityStarter)
+ ShortcutHelperDialogStarter(
+ coroutineScope,
+ viewModel,
+ shortcutCustomizationDialogStarterFactory,
+ dialogFactory,
+ activityStarter,
+ )
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 26ce67da10b3..7ec53dfbdd10 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -40,8 +40,8 @@ import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
import com.android.systemui.util.settings.fakeSettings
-import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -72,14 +72,13 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
@Before
fun setup() {
- val settingsRepository =
- UserAwareSecureSettingsRepositoryImpl(secureSettings, userRepository, dispatcher)
+ val settingsRepository = kosmos.userAwareSecureSettingsRepository
val stickyKeysRepository =
StickyKeysRepositoryImpl(
inputManager,
dispatcher,
settingsRepository,
- mock<StickyKeysLogger>()
+ mock<StickyKeysLogger>(),
)
setStickyKeySetting(enabled = false)
viewModel =
@@ -114,7 +113,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
verify(inputManager)
.registerStickyModifierStateListener(
any(),
- any(InputManager.StickyModifierStateListener::class.java)
+ any(InputManager.StickyModifierStateListener::class.java),
)
}
}
@@ -187,11 +186,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
assertThat(stickyKeys)
.isEqualTo(
- mapOf(
- ALT to Locked(false),
- META to Locked(false),
- SHIFT to Locked(false),
- )
+ mapOf(ALT to Locked(false), META to Locked(false), SHIFT to Locked(false))
)
}
}
@@ -218,7 +213,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
mapOf(
META to false,
SHIFT to false, // shift is sticky but not locked
- CTRL to false
+ CTRL to false,
)
)
val previousShiftIndex = stickyKeys?.toList()?.indexOf(SHIFT to Locked(false))
@@ -228,7 +223,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
SHIFT to false,
SHIFT to true, // shift is now locked
META to false,
- CTRL to false
+ CTRL to false,
)
)
assertThat(stickyKeys?.toList()?.indexOf(SHIFT to Locked(true)))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index bfe89de6229d..3d5498b61471 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -16,11 +16,14 @@
package com.android.systemui.keyguard.data.repository
+import android.animation.Animator
import android.animation.ValueAnimator
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -41,6 +44,8 @@ import com.google.common.truth.Truth.assertThat
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.UUID
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.dropWhile
@@ -53,6 +58,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -65,6 +71,8 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: KeyguardTransitionRepository
private lateinit var runner: KeyguardTransitionRunner
+ private val animatorListener = mock<Animator.AnimatorListener>()
+
@Before
fun setUp() {
underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main)
@@ -80,7 +88,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
runner.startTransition(
this,
TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
- maxFrames = 100
+ maxFrames = 100,
)
assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN)
@@ -107,7 +115,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
LOCKSCREEN,
AOD,
getAnimator(),
- TransitionModeOnCanceled.LAST_VALUE
+ TransitionModeOnCanceled.LAST_VALUE,
),
)
@@ -142,7 +150,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
LOCKSCREEN,
AOD,
getAnimator(),
- TransitionModeOnCanceled.RESET
+ TransitionModeOnCanceled.RESET,
),
)
@@ -177,7 +185,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
LOCKSCREEN,
AOD,
getAnimator(),
- TransitionModeOnCanceled.REVERSE
+ TransitionModeOnCanceled.REVERSE,
),
)
@@ -476,6 +484,49 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(steps.size).isEqualTo(3)
}
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF)
+ fun forceFinishCurrentTransition_noFurtherStepsEmitted() =
+ testScope.runTest {
+ val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
+
+ var sentForceFinish = false
+
+ runner.startTransition(
+ this,
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
+ maxFrames = 100,
+ // Force-finish on the second frame.
+ frameCallback = { frameNumber ->
+ if (!sentForceFinish && frameNumber > 1) {
+ testScope.launch { underTest.forceFinishCurrentTransition() }
+ sentForceFinish = true
+ }
+ },
+ )
+
+ val lastTwoRunningSteps =
+ steps.filter { it.transitionState == TransitionState.RUNNING }.takeLast(2)
+
+ // Make sure we stopped emitting RUNNING steps early, but then emitted a final 1f step.
+ assertTrue(lastTwoRunningSteps[0].value < 0.5f)
+ assertTrue(lastTwoRunningSteps[1].value == 1f)
+
+ assertEquals(steps.last().from, AOD)
+ assertEquals(steps.last().to, LOCKSCREEN)
+ assertEquals(steps.last().transitionState, TransitionState.FINISHED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF)
+ fun forceFinishCurrentTransition_noTransitionStarted_noStepsEmitted() =
+ testScope.runTest {
+ val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
+
+ underTest.forceFinishCurrentTransition()
+ assertEquals(0, steps.size)
+ }
+
private fun listWithStep(
step: BigDecimal,
start: BigDecimal = BigDecimal.ZERO,
@@ -505,7 +556,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
to,
fractions[0].toFloat(),
TransitionState.STARTED,
- OWNER_NAME
+ OWNER_NAME,
)
)
fractions.forEachIndexed { index, fraction ->
@@ -519,7 +570,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
to,
fraction.toFloat(),
TransitionState.RUNNING,
- OWNER_NAME
+ OWNER_NAME,
)
)
}
@@ -538,6 +589,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
setDuration(10)
+ addListener(animatorListener)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
index 8914c80cdd5e..ae2a5c5fe501 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
@@ -34,6 +34,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -43,6 +44,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -60,10 +62,13 @@ import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -96,7 +101,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.AOD,
testScope = testScope,
- throughTransitionState = TransitionState.RUNNING
+ throughTransitionState = TransitionState.RUNNING,
)
powerInteractor.onCameraLaunchGestureDetected()
@@ -134,7 +139,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
from = KeyguardState.AOD,
to = KeyguardState.LOCKSCREEN,
testScope = testScope,
- throughTransitionState = TransitionState.RUNNING
+ throughTransitionState = TransitionState.RUNNING,
)
powerInteractor.onCameraLaunchGestureDetected()
@@ -182,21 +187,12 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
runCurrent()
- assertThat(values)
- .containsExactly(
- false,
- true,
- )
+ assertThat(values).containsExactly(false, true)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false)
runCurrent()
- assertThat(values)
- .containsExactly(
- false,
- true,
- false,
- )
+ assertThat(values).containsExactly(false, true, false)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
runCurrent()
@@ -228,7 +224,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
from = KeyguardState.GONE,
to = KeyguardState.AOD,
testScope = testScope,
- throughTransitionState = TransitionState.RUNNING
+ throughTransitionState = TransitionState.RUNNING,
)
powerInteractor.onCameraLaunchGestureDetected()
@@ -242,10 +238,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
testScope = testScope,
)
- assertThat(values)
- .containsExactly(
- false,
- )
+ assertThat(values).containsExactly(false)
}
@Test
@@ -263,7 +256,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
from = KeyguardState.UNDEFINED,
to = KeyguardState.AOD,
testScope = testScope,
- throughTransitionState = TransitionState.RUNNING
+ throughTransitionState = TransitionState.RUNNING,
)
powerInteractor.onCameraLaunchGestureDetected()
@@ -278,10 +271,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
testScope = testScope,
)
- assertThat(values)
- .containsExactly(
- false,
- )
+ assertThat(values).containsExactly(false)
}
@Test
@@ -304,8 +294,19 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
assertThat(occludingActivityWillDismissKeyguard).isTrue()
// Re-lock device:
- kosmos.powerInteractor.setAsleepForTest()
- runCurrent()
+ lockDevice()
assertThat(occludingActivityWillDismissKeyguard).isFalse()
}
+
+ private suspend fun TestScope.lockDevice() {
+ kosmos.powerInteractor.setAsleepForTest()
+ advanceTimeBy(
+ kosmos.userAwareSecureSettingsRepository
+ .getInt(
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT,
+ )
+ .toLong()
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 6e16705b0739..2c12f8782ddc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -34,6 +34,8 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -115,9 +117,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // We should start with the surface invisible on LOCKSCREEN.
+ false // We should start with the surface invisible on LOCKSCREEN.
),
- values
+ values,
)
val lockscreenSpecificSurfaceVisibility = true
@@ -134,13 +136,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// We started a transition from LOCKSCREEN, we should be using the value emitted by the
// lockscreenSurfaceVisibilityFlow.
- assertEquals(
- listOf(
- false,
- lockscreenSpecificSurfaceVisibility,
- ),
- values
- )
+ assertEquals(listOf(false, lockscreenSpecificSurfaceVisibility), values)
// Go back to LOCKSCREEN, since we won't emit 'true' twice in a row.
transitionRepository.sendTransitionStep(
@@ -166,7 +162,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
lockscreenSpecificSurfaceVisibility,
false, // FINISHED (LOCKSCREEN)
),
- values
+ values,
)
val bouncerSpecificVisibility = true
@@ -191,13 +187,13 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
false,
bouncerSpecificVisibility,
),
- values
+ values,
)
}
@Test
@EnableSceneContainer
- fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() =
+ fun surfaceBehindVisibility_fromLockscreenToGone_dependsOnDeviceEntry() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
@@ -212,7 +208,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
SuccessFingerprintAuthenticationStatus(0, true)
)
- // Start the transition to Gone, the surface should become immediately visible.
+ // Start the transition to Gone, the surface should remain invisible.
kosmos.setSceneTransition(
ObservableTransitionState.Transition(
fromScene = Scenes.Lockscreen,
@@ -224,9 +220,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
)
)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- assertThat(isSurfaceBehindVisible).isTrue()
+ assertThat(isSurfaceBehindVisible).isFalse()
- // Towards the end of the transition, the surface should continue to be visible.
+ // Towards the end of the transition, the surface should continue to remain invisible.
kosmos.setSceneTransition(
ObservableTransitionState.Transition(
fromScene = Scenes.Lockscreen,
@@ -238,7 +234,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
)
)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- assertThat(isSurfaceBehindVisible).isTrue()
+ assertThat(isSurfaceBehindVisible).isFalse()
// After the transition, settles on Gone. Surface behind should stay visible now.
kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
@@ -249,43 +245,6 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
@Test
@EnableSceneContainer
- fun surfaceBehindVisibility_fromLockscreenToGone_withUserInput_falseUntilInputStops() =
- testScope.runTest {
- val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
- val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
-
- // Before the transition, we start on Lockscreen so the surface should start invisible.
- kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- assertThat(isSurfaceBehindVisible).isFalse()
-
- // Unlocked with fingerprint.
- kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
-
- // Start the transition to Gone, the surface should not be visible while
- // isUserInputOngoing is true
- val isUserInputOngoing = MutableStateFlow(true)
- kosmos.setSceneTransition(
- ObservableTransitionState.Transition(
- fromScene = Scenes.Lockscreen,
- toScene = Scenes.Gone,
- isInitiatedByUserInput = true,
- isUserInputOngoing = isUserInputOngoing,
- progress = flowOf(0.51f),
- currentScene = flowOf(Scenes.Gone),
- )
- )
- assertThat(isSurfaceBehindVisible).isFalse()
-
- // When isUserInputOngoing becomes false, then the surface should become visible.
- isUserInputOngoing.value = false
- assertThat(isSurfaceBehindVisible).isTrue()
- }
-
- @Test
- @EnableSceneContainer
fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
@@ -362,20 +321,14 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
assertThat(currentScene).isEqualTo(Scenes.Gone)
- listOf(
- Scenes.Shade,
- Scenes.QuickSettings,
- Scenes.Shade,
- Scenes.Gone,
- )
- .forEach { scene ->
- kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
- kosmos.sceneInteractor.changeScene(scene, "")
- assertThat(currentScene).isEqualTo(scene)
- assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
- .that(isSurfaceBehindVisible)
- .isTrue()
- }
+ listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Gone).forEach { scene ->
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+ kosmos.sceneInteractor.changeScene(scene, "")
+ assertThat(currentScene).isEqualTo(scene)
+ assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+ .that(isSurfaceBehindVisible)
+ .isTrue()
+ }
}
@Test
@@ -386,19 +339,14 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- listOf(
- Scenes.Shade,
- Scenes.QuickSettings,
- Scenes.Shade,
- Scenes.Lockscreen,
- )
- .forEach { scene ->
- kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
- kosmos.sceneInteractor.changeScene(scene, "")
- assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
- .that(isSurfaceBehindVisible)
- .isFalse()
- }
+ listOf(Scenes.Shade, Scenes.QuickSettings, Scenes.Shade, Scenes.Lockscreen).forEach {
+ scene ->
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+ kosmos.sceneInteractor.changeScene(scene, "")
+ assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+ .that(isSurfaceBehindVisible)
+ .isFalse()
+ }
}
@Test
@@ -427,9 +375,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Not using the animation when we're just sitting on LOCKSCREEN.
+ false // Not using the animation when we're just sitting on LOCKSCREEN.
),
- values
+ values,
)
surfaceBehindIsAnimatingFlow.emit(true)
@@ -437,7 +385,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
runCurrent()
@@ -446,7 +394,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
false,
true, // Still true when we're FINISHED -> GONE, since we're still animating.
),
- values
+ values,
)
surfaceBehindIsAnimatingFlow.emit(false)
@@ -458,7 +406,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
true,
false, // False once the animation ends.
),
- values
+ values,
)
}
@@ -488,9 +436,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- false, // Not using the animation when we're just sitting on LOCKSCREEN.
+ false // Not using the animation when we're just sitting on LOCKSCREEN.
),
- values
+ values,
)
surfaceBehindIsAnimatingFlow.emit(true)
@@ -509,7 +457,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
false,
true, // We're happily animating while transitioning to gone.
),
- values
+ values,
)
// Oh no, we're still surfaceBehindAnimating=true, but no longer transitioning to GONE.
@@ -536,7 +484,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
true,
false, // Despite the animator still running, this should be false.
),
- values
+ values,
)
surfaceBehindIsAnimatingFlow.emit(false)
@@ -548,7 +496,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
true,
false, // The animator ending should have no effect.
),
- values
+ values,
)
}
@@ -579,10 +527,10 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- true, // Unsurprisingly, we should start with the lockscreen visible on
+ true // Unsurprisingly, we should start with the lockscreen visible on
// LOCKSCREEN.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -596,9 +544,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
assertEquals(
listOf(
- true, // Lockscreen remains visible while we're transitioning to GONE.
+ true // Lockscreen remains visible while we're transitioning to GONE.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -615,7 +563,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
true,
false, // Once we're fully GONE, the lockscreen should not be visible.
),
- values
+ values,
)
}
@@ -628,7 +576,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
runCurrent()
@@ -640,7 +588,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// Then, false, since we finish in GONE.
false,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -665,7 +613,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// Should remain false as we transition from GONE.
false,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -693,7 +641,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// visibility of the from state (LS).
true,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -706,14 +654,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
runCurrent()
- assertEquals(
- listOf(
- true,
- false,
- true,
- ),
- values
- )
+ assertEquals(listOf(true, false, true), values)
}
/**
@@ -730,7 +671,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
runCurrent()
@@ -740,7 +681,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// Not visible since we're GONE.
false,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -803,7 +744,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// STARTED to GONE after a CANCELED from GONE.
false,
),
- values
+ values,
)
transitionRepository.sendTransitionSteps(
@@ -820,7 +761,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// visible again once we're finished in LOCKSCREEN.
true,
),
- values
+ values,
)
}
@@ -833,7 +774,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
runCurrent()
@@ -843,7 +784,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// Not visible when finished in GONE.
false,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -869,7 +810,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
// Still not visible during GONE -> AOD.
false,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -886,9 +827,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
true,
false,
// Visible now that we're FINISHED in AOD.
- true
+ true,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -914,9 +855,9 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
true,
false,
// Remains visible from AOD during transition.
- true
+ true,
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -934,15 +875,15 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
false,
true,
// Until we're finished in GONE again.
- false
+ false,
),
- values
+ values,
)
}
@Test
@EnableSceneContainer
- fun lockscreenVisibility() =
+ fun lockscreenVisibilityWithScenes() =
testScope.runTest {
val isDeviceUnlocked by
collectLastValue(
@@ -956,32 +897,69 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
assertThat(lockscreenVisibility).isTrue()
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+ kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(lockscreenVisibility).isTrue()
+
+ kosmos.setSceneTransition(Transition(from = Scenes.Shade, to = Scenes.QuickSettings))
+ assertThat(lockscreenVisibility).isTrue()
+
+ kosmos.setSceneTransition(Idle(Scenes.QuickSettings))
+ kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "")
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+ assertThat(lockscreenVisibility).isTrue()
+
+ kosmos.setSceneTransition(Transition(from = Scenes.QuickSettings, to = Scenes.Shade))
+ assertThat(lockscreenVisibility).isTrue()
+
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+ kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(lockscreenVisibility).isTrue()
+
+ kosmos.setSceneTransition(Idle(Scenes.Bouncer))
kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
assertThat(currentScene).isEqualTo(Scenes.Bouncer)
assertThat(lockscreenVisibility).isTrue()
+ kosmos.setSceneTransition(Transition(from = Scenes.Bouncer, to = Scenes.Gone))
+ assertThat(lockscreenVisibility).isTrue()
+
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
assertThat(isDeviceUnlocked).isTrue()
kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(lockscreenVisibility).isFalse()
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(lockscreenVisibility).isFalse()
+ kosmos.setSceneTransition(Transition(from = Scenes.Shade, to = Scenes.QuickSettings))
+ assertThat(lockscreenVisibility).isFalse()
+
+ kosmos.setSceneTransition(Idle(Scenes.QuickSettings))
kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "")
assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
assertThat(lockscreenVisibility).isFalse()
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(lockscreenVisibility).isFalse()
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(lockscreenVisibility).isFalse()
+ kosmos.setSceneTransition(Transition(from = Scenes.Gone, to = Scenes.Lockscreen))
+ assertThat(lockscreenVisibility).isFalse()
+
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "")
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
assertThat(lockscreenVisibility).isTrue()
@@ -1037,7 +1015,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
flowOf(Scenes.Lockscreen),
progress,
false,
- flowOf(false)
+ flowOf(false),
)
private val goneToLs =
@@ -1047,7 +1025,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
flowOf(Scenes.Lockscreen),
progress,
false,
- flowOf(false)
+ flowOf(false),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 9c58e2b987a1..92764ae94271 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -29,11 +29,13 @@ import com.android.wm.shell.keyguard.KeyguardTransitions
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -112,4 +114,20 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
verify(activityTaskManagerService).setLockScreenShown(true, false)
verifyNoMoreInteractions(activityTaskManagerService)
}
+
+ @Test
+ fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall() {
+ underTest.setLockscreenShown(true)
+ underTest.setSurfaceBehindVisibility(true)
+ verify(activityTaskManagerService).keyguardGoingAway(0)
+
+ underTest.setSurfaceBehindVisibility(true)
+ verifyNoMoreInteractions(keyguardTransitions)
+ }
+
+ @Test
+ fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility() {
+ underTest.setSurfaceBehindVisibility(false)
+ verify(activityTaskManagerService).setLockScreenShown(eq(true), any())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index ecc62e908a4f..87ab3c89a671 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -69,7 +69,9 @@ class ClockSectionTest : SysuiTestCase() {
get() =
kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() +
context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
- context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+ context.resources.getDimensionPixelSize(
+ customR.dimen.keyguard_smartspace_top_offset
+ )
private val LARGE_CLOCK_TOP
get() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 7f0937059494..0e3b03f74c02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -44,6 +44,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Ignore
@@ -107,6 +108,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
fun translationAndScale_whenNotDozing() =
testScope.runTest {
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to not dozing (on lockscreen)
keyguardTransitionRepository.sendTransitionStep(
@@ -180,6 +182,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100))
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -221,6 +224,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -263,6 +267,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -305,6 +310,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -423,6 +429,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
.thenReturn(if (isWeatherClock) true else false)
val movement by collectLastValue(underTest.movement)
+ assertThat(movement?.translationX).isEqualTo(0)
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -434,6 +441,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
),
validateStep = false,
)
+ runCurrent()
// Trigger a change to the burn-in model
burnInFlow.value = BurnInModel(translationX = 20, translationY = 30, scale = 0.5f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
index 6397979d9627..5c4b7432e18d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
@@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.ShowOverlay
@@ -201,8 +200,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() {
val userActions by collectLastValue(underTest.actions)
val downDestination =
userActions?.get(
- Swipe(
- SwipeDirection.Down,
+ Swipe.Down(
fromSource = Edge.Top.takeIf { downFromEdge },
pointerCount = if (downWithTwoPointers) 2 else 1,
)
@@ -292,8 +290,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() {
val downDestination =
userActions?.get(
- Swipe(
- SwipeDirection.Down,
+ Swipe.Down(
fromSource = Edge.Top.takeIf { downFromEdge },
pointerCount = if (downWithTwoPointers) 2 else 1,
)
@@ -310,8 +307,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() {
val downFromTopRightDestination =
userActions?.get(
- Swipe(
- SwipeDirection.Down,
+ Swipe.Down(
fromSource = SceneContainerEdge.TopRight,
pointerCount = if (downWithTwoPointers) 2 else 1,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
index 1abb441439fe..5798e0776c4f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
@@ -21,6 +21,7 @@ import android.animation.ValueAnimator
import android.view.Choreographer.FrameCallback
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import java.util.function.Consumer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -35,9 +36,8 @@ import org.junit.Assert.fail
* Gives direct control over ValueAnimator, in order to make transition tests deterministic. See
* [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly.
*/
-class KeyguardTransitionRunner(
- val repository: KeyguardTransitionRepository,
-) : AnimationFrameCallbackProvider {
+class KeyguardTransitionRunner(val repository: KeyguardTransitionRepository) :
+ AnimationFrameCallbackProvider {
private var frameCount = 1L
private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null))
@@ -48,7 +48,12 @@ class KeyguardTransitionRunner(
* For transitions being directed by an animator. Will control the number of frames being
* generated so the values are deterministic.
*/
- suspend fun startTransition(scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100) {
+ suspend fun startTransition(
+ scope: CoroutineScope,
+ info: TransitionInfo,
+ maxFrames: Int = 100,
+ frameCallback: Consumer<Long>? = null,
+ ) {
// AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main
// thread
withContext(Dispatchers.Main) {
@@ -62,7 +67,12 @@ class KeyguardTransitionRunner(
isTerminated = frameNumber >= maxFrames
if (!isTerminated) {
- withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) }
+ try {
+ withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) }
+ frameCallback?.accept(frameNumber)
+ } catch (e: IllegalStateException) {
+ e.printStackTrace()
+ }
}
}
}
@@ -90,9 +100,13 @@ class KeyguardTransitionRunner(
override fun postFrameCallback(cb: FrameCallback) {
frames.value = Pair(frameCount++, cb)
}
+
override fun postCommitCallback(runnable: Runnable) {}
+
override fun getFrameTime() = frameCount
+
override fun getFrameDelay() = 1L
+
override fun setFrameDelay(delay: Long) {}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
new file mode 100644
index 000000000000..f8f6fe246563
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.domain.pipeline
+
+import android.media.session.MediaSession
+import android.os.Bundle
+import android.os.Handler
+import android.os.looper
+import android.testing.TestableLooper.RunWithLooper
+import androidx.media.utils.MediaConstants
+import androidx.media3.common.Player
+import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController as Media3Controller
+import androidx.media3.session.SessionCommand
+import androidx.media3.session.SessionToken
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.graphics.imageLoader
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.shared.mediaLogger
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.fakeSessionTokenFactory
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.util.concurrency.execution
+import com.google.common.collect.ImmutableList
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+private const val PACKAGE_NAME = "package_name"
+private const val CUSTOM_ACTION_NAME = "Custom Action"
+private const val CUSTOM_ACTION_COMMAND = "custom-action"
+
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidJUnit4::class)
+class Media3ActionFactoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val controllerFactory = kosmos.fakeMediaControllerFactory
+ private val tokenFactory = kosmos.fakeSessionTokenFactory
+
+ private val commandCaptor = argumentCaptor<SessionCommand>()
+ private val runnableCaptor = argumentCaptor<Runnable>()
+
+ private val legacyToken = MediaSession.Token(1, null)
+ private val token = mock<SessionToken>()
+ private val handler =
+ mock<Handler> {
+ on { post(runnableCaptor.capture()) } doAnswer
+ {
+ runnableCaptor.lastValue.run()
+ true
+ }
+ }
+ private val customLayout = ImmutableList.of<CommandButton>()
+ private val media3Controller =
+ mock<Media3Controller> {
+ on { customLayout } doReturn customLayout
+ on { sessionExtras } doReturn Bundle()
+ on { isCommandAvailable(any()) } doReturn true
+ on { isSessionCommandAvailable(any<SessionCommand>()) } doReturn true
+ }
+
+ private lateinit var underTest: Media3ActionFactory
+
+ @Before
+ fun setup() {
+ underTest =
+ Media3ActionFactory(
+ context,
+ kosmos.imageLoader,
+ controllerFactory,
+ tokenFactory,
+ kosmos.mediaLogger,
+ kosmos.looper,
+ handler,
+ kosmos.testScope,
+ kosmos.execution,
+ )
+
+ controllerFactory.setMedia3Controller(media3Controller)
+ tokenFactory.setMedia3SessionToken(token)
+ }
+
+ @Test
+ fun media3Actions_playingState_withCustomActions() =
+ testScope.runTest {
+ // Media is playing, all commands available, with custom actions
+ val customLayout = ImmutableList.copyOf((0..1).map { createCustomCommandButton(it) })
+ whenever(media3Controller.customLayout).thenReturn(customLayout)
+ whenever(media3Controller.isPlaying).thenReturn(true)
+ val result = getActions()
+
+ assertThat(result).isNotNull()
+
+ val actions = result!!
+ assertThat(actions.playOrPause!!.contentDescription)
+ .isEqualTo(context.getString(R.string.controls_media_button_pause))
+ actions.playOrPause!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).pause()
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.prevOrCustom!!.contentDescription)
+ .isEqualTo(context.getString(R.string.controls_media_button_prev))
+ actions.prevOrCustom!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).seekToPrevious()
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.nextOrCustom!!.contentDescription)
+ .isEqualTo(context.getString(R.string.controls_media_button_next))
+ actions.nextOrCustom!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).seekToNext()
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.custom0!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 0")
+ actions.custom0!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 0")
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.custom1!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 1")
+ actions.custom1!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 1")
+ verify(media3Controller).release()
+ }
+
+ @Test
+ fun media3Actions_pausedState_hasPauseAction() =
+ testScope.runTest {
+ whenever(media3Controller.isPlaying).thenReturn(false)
+ val result = getActions()
+
+ assertThat(result).isNotNull()
+ val actions = result!!
+ assertThat(actions.playOrPause!!.contentDescription)
+ .isEqualTo(context.getString(R.string.controls_media_button_play))
+ clearInvocations(media3Controller)
+
+ actions.playOrPause!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).play()
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+ }
+
+ @Test
+ fun media3Actions_bufferingState_hasLoadingSpinner() =
+ testScope.runTest {
+ whenever(media3Controller.isPlaying).thenReturn(false)
+ whenever(media3Controller.playbackState).thenReturn(Player.STATE_BUFFERING)
+ val result = getActions()
+
+ assertThat(result).isNotNull()
+ val actions = result!!
+ assertThat(actions.playOrPause!!.contentDescription)
+ .isEqualTo(context.getString(R.string.controls_media_button_connecting))
+ assertThat(actions.playOrPause!!.action).isNull()
+ assertThat(actions.playOrPause!!.rebindId)
+ .isEqualTo(com.android.internal.R.drawable.progress_small_material)
+ }
+
+ @Test
+ fun media3Actions_noPrevNext_usesCustom() =
+ testScope.runTest {
+ val customLayout = ImmutableList.copyOf((0..4).map { createCustomCommandButton(it) })
+ whenever(media3Controller.customLayout).thenReturn(customLayout)
+ whenever(media3Controller.isPlaying).thenReturn(true)
+ whenever(media3Controller.isCommandAvailable(eq(Player.COMMAND_SEEK_TO_PREVIOUS)))
+ .thenReturn(false)
+ whenever(
+ media3Controller.isCommandAvailable(
+ eq(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
+ )
+ )
+ .thenReturn(false)
+ whenever(media3Controller.isCommandAvailable(eq(Player.COMMAND_SEEK_TO_NEXT)))
+ .thenReturn(false)
+ whenever(
+ media3Controller.isCommandAvailable(eq(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM))
+ )
+ .thenReturn(false)
+ val result = getActions()
+
+ assertThat(result).isNotNull()
+ val actions = result!!
+
+ assertThat(actions.prevOrCustom!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 0")
+ actions.prevOrCustom!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 0")
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.nextOrCustom!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 1")
+ actions.nextOrCustom!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 1")
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.custom0!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 2")
+ actions.custom0!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 2")
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.custom1!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 3")
+ actions.custom1!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 3")
+ verify(media3Controller).release()
+ }
+
+ @Test
+ fun media3Actions_noPrevNext_reservedSpace() =
+ testScope.runTest {
+ val customLayout = ImmutableList.copyOf((0..4).map { createCustomCommandButton(it) })
+ whenever(media3Controller.customLayout).thenReturn(customLayout)
+ whenever(media3Controller.isPlaying).thenReturn(true)
+ whenever(media3Controller.isCommandAvailable(eq(Player.COMMAND_SEEK_TO_PREVIOUS)))
+ .thenReturn(false)
+ whenever(
+ media3Controller.isCommandAvailable(
+ eq(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
+ )
+ )
+ .thenReturn(false)
+ whenever(media3Controller.isCommandAvailable(eq(Player.COMMAND_SEEK_TO_NEXT)))
+ .thenReturn(false)
+ whenever(
+ media3Controller.isCommandAvailable(eq(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM))
+ )
+ .thenReturn(false)
+ val extras =
+ Bundle().apply {
+ putBoolean(
+ MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV,
+ true,
+ )
+ putBoolean(
+ MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT,
+ true,
+ )
+ }
+ whenever(media3Controller.sessionExtras).thenReturn(extras)
+ val result = getActions()
+
+ assertThat(result).isNotNull()
+ val actions = result!!
+
+ assertThat(actions.prevOrCustom).isNull()
+ assertThat(actions.nextOrCustom).isNull()
+
+ assertThat(actions.custom0!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 0")
+ actions.custom0!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 0")
+ verify(media3Controller).release()
+ clearInvocations(media3Controller)
+
+ assertThat(actions.custom1!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 1")
+ actions.custom1!!.action!!.run()
+ runCurrent()
+ verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
+ assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 1")
+ verify(media3Controller).release()
+ }
+
+ private suspend fun getActions(): MediaButton? {
+ val result = underTest.createActionsFromSession(PACKAGE_NAME, legacyToken)
+ testScope.runCurrent()
+ verify(media3Controller).release()
+
+ // Clear so tests can verify the correct number of release() calls in later operations
+ clearInvocations(media3Controller)
+ return result
+ }
+
+ private fun createCustomCommandButton(id: Int): CommandButton {
+ return CommandButton.Builder()
+ .setDisplayName("$CUSTOM_ACTION_NAME $id")
+ .setSessionCommand(SessionCommand("$CUSTOM_ACTION_COMMAND $id", Bundle()))
+ .build()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
index fc9e595945dd..cf503bbf1310 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
@@ -29,6 +29,7 @@ import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
import android.service.notification.StatusBarNotification
+import android.testing.TestableLooper.RunWithLooper
import androidx.media.utils.MediaConstants
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -69,6 +70,7 @@ private const val SESSION_TITLE = "title"
private const val SESSION_EMPTY_TITLE = ""
@SmallTest
+@RunWithLooper
@RunWith(AndroidJUnit4::class)
class MediaDataLoaderTest : SysuiTestCase() {
@@ -80,6 +82,7 @@ class MediaDataLoaderTest : SysuiTestCase() {
private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
private val mediaFlags = kosmos.mediaFlags
private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
+ private lateinit var media3ActionFactory: Media3ActionFactory
private val session = MediaSession(context, "MediaDataLoaderTestSession")
private val metadataBuilder =
MediaMetadata.Builder().apply {
@@ -87,21 +90,26 @@ class MediaDataLoaderTest : SysuiTestCase() {
putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
}
- private val underTest: MediaDataLoader =
- MediaDataLoader(
- context,
- testDispatcher,
- testScope,
- mediaControllerFactory,
- mediaFlags,
- kosmos.imageLoader,
- statusBarManager,
- )
+ private lateinit var underTest: MediaDataLoader
@Before
fun setUp() {
+ media3ActionFactory = kosmos.media3ActionFactory
mediaControllerFactory.setControllerForToken(session.sessionToken, mediaController)
+ whenever(mediaController.sessionToken).thenReturn(session.sessionToken)
whenever(mediaController.metadata).then { metadataBuilder.build() }
+
+ underTest =
+ MediaDataLoader(
+ context,
+ testDispatcher,
+ testScope,
+ mediaControllerFactory,
+ mediaFlags,
+ kosmos.imageLoader,
+ statusBarManager,
+ kosmos.media3ActionFactory,
+ )
}
@Test
@@ -304,7 +312,6 @@ class MediaDataLoaderTest : SysuiTestCase() {
}
build()
}
-
val result = underTest.loadMediaData(KEY, mediaNotification)
assertThat(result).isNotNull()
@@ -394,6 +401,7 @@ class MediaDataLoaderTest : SysuiTestCase() {
mediaFlags,
mockImageLoader,
statusBarManager,
+ media3ActionFactory,
)
metadataBuilder.putString(
MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
@@ -422,6 +430,7 @@ class MediaDataLoaderTest : SysuiTestCase() {
mediaFlags,
mockImageLoader,
statusBarManager,
+ media3ActionFactory,
)
metadataBuilder.putString(
MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index e12c67b24893..104aa517fa4f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -16,7 +16,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.wm.shell.recents.RecentTasks
-import com.android.wm.shell.shared.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.GroupedTaskInfo
import com.android.wm.shell.shared.split.SplitBounds
import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
import com.google.common.truth.Truth.assertThat
@@ -219,9 +219,9 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
}
@Suppress("UNCHECKED_CAST")
- private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
+ private fun givenRecentTasks(vararg tasks: GroupedTaskInfo) {
whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
- val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
+ val consumer = it.arguments.last() as Consumer<List<GroupedTaskInfo>>
consumer.accept(tasks.toList())
}
}
@@ -247,7 +247,7 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
userId: Int = 0,
isVisible: Boolean = false,
userType: RecentTask.UserType = STANDARD,
- ): GroupedRecentTaskInfo {
+ ): GroupedTaskInfo {
val userInfo =
mock<UserInfo> {
whenever(isCloneProfile).thenReturn(userType == CLONED)
@@ -255,7 +255,7 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
whenever(isPrivateProfile).thenReturn(userType == PRIVATE)
}
whenever(userManager.getUserInfo(userId)).thenReturn(userInfo)
- return GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId, userId, isVisible))
+ return GroupedTaskInfo.forFullscreenTasks(createTaskInfo(taskId, userId, isVisible))
}
private fun createTaskPair(
@@ -263,9 +263,9 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
userId1: Int = 0,
taskId2: Int,
userId2: Int = 0,
- isVisible: Boolean = false
- ): GroupedRecentTaskInfo =
- GroupedRecentTaskInfo.forSplitTasks(
+ isVisible: Boolean = false,
+ ): GroupedTaskInfo =
+ GroupedTaskInfo.forSplitTasks(
createTaskInfo(taskId1, userId1, isVisible),
createTaskInfo(taskId2, userId2, isVisible),
SplitBounds(Rect(), Rect(), taskId1, taskId2, SNAP_TO_2_50_50)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 2905a7329d21..646722bee35f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -116,6 +116,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.data.repository.LightBarControllerStore;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -208,7 +209,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private LightBarController mLightBarController;
@Mock
- private LightBarController.Factory mLightBarcontrollerFactory;
+ private LightBarControllerStore mLightBarControllerStore;
@Mock
private AutoHideController mAutoHideController;
@Mock
@@ -257,7 +258,7 @@ public class NavigationBarTest extends SysuiTestCase {
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
+ when(mLightBarControllerStore.forDisplay(anyInt())).thenReturn(mLightBarController);
when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton);
when(mNavigationBarView.getRecentsButton()).thenReturn(mRecentsButton);
@@ -649,8 +650,7 @@ public class NavigationBarTest extends SysuiTestCase {
mFakeExecutor,
mUiEventLogger,
mNavBarHelper,
- mLightBarController,
- mLightBarcontrollerFactory,
+ mLightBarControllerStore,
mAutoHideController,
mAutoHideControllerFactory,
Optional.of(mTelecomManager),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index 642d9a0b1e9d..855931c32671 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -76,12 +75,8 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
assertThat(
- (actions?.get(
- Swipe(
- direction = SwipeDirection.Down,
- fromSource = SceneContainerEdge.TopRight,
- )
- ) as? UserActionResult.ReplaceByOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight))
+ as? UserActionResult.ReplaceByOverlay)
?.overlay
)
.isEqualTo(Overlays.QuickSettingsShade)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
index ada2138d4d52..b30313e07012 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -35,9 +35,12 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -121,6 +124,38 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
}
+ @Test
+ fun showHeader_showsOnNarrowScreen() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeLayoutWide(false)
+
+ // Shown when notifications are present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ runCurrent()
+ assertThat(underTest.showHeader).isTrue()
+
+ // Hidden when notifications are not present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(0)
+ runCurrent()
+ assertThat(underTest.showHeader).isFalse()
+ }
+
+ @Test
+ fun showHeader_hidesOnWideScreen() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+
+ // Hidden when notifications are present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(1)
+ runCurrent()
+ assertThat(underTest.showHeader).isFalse()
+
+ // Hidden when notifications are not present.
+ kosmos.activeNotificationListRepository.setActiveNotifs(0)
+ runCurrent()
+ assertThat(underTest.showHeader).isFalse()
+ }
+
private fun TestScope.lockDevice() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
kosmos.powerInteractor.setAsleepForTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index 8ccaf6bc0651..0f631509bfba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -300,7 +300,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
UserHandle.USER_CURRENT);
verifyActivityDetails("abc/.def");
- assertThat(mController.isEnabledForLockScreenButton()).isFalse();
+ assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isAllowedOnLockScreen()).isTrue();
assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 16ae4662c2d4..0356422bda04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -42,6 +42,7 @@ import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.provider.DeviceConfig;
import android.testing.TestableLooper;
@@ -49,6 +50,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -315,13 +317,36 @@ public class FgsManagerControllerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_STOPPABLE_FGS_SYSTEM_APP)
+ public void testButtonVisibilityOfStoppableApps() throws Exception {
+ setUserProfiles(0);
+ setBackgroundRestrictionExemptionReason("pkg", 12345, REASON_ALLOWLISTED_PACKAGE);
+ setBackgroundRestrictionExemptionReason("vendor_pkg", 67890, REASON_ALLOWLISTED_PACKAGE);
+
+ // Same as above, but apps are opt-in to be stoppable
+ setStoppableApps(new String[] {"pkg"}, /* vendor */ false);
+ setStoppableApps(new String[] {"vendor_pkg"}, /* vendor */ true);
+
+ final Binder binder = new Binder();
+ setShowStopButtonForUserAllowlistedApps(true);
+ // Both are foreground.
+ mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
+ mIForegroundServiceObserver.onForegroundStateChanged(binder, "vendor_pkg", 0, true);
+ Assert.assertEquals(2, mFmc.visibleButtonsCount());
+
+ // The vendor package is no longer foreground. Only `pkg` remains.
+ mIForegroundServiceObserver.onForegroundStateChanged(binder, "vendor_pkg", 0, false);
+ Assert.assertEquals(1, mFmc.visibleButtonsCount());
+ }
+
+ @Test
public void testShowUserVisibleJobsOnCreation() {
// Test when the default is on.
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"true", false);
FgsManagerController fmc = new FgsManagerControllerImpl(
- mContext,
+ mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
mSystemClock,
@@ -348,7 +373,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"false", false);
fmc = new FgsManagerControllerImpl(
- mContext,
+ mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
mSystemClock,
@@ -446,6 +471,11 @@ public class FgsManagerControllerTest extends SysuiTestCase {
.getBackgroundRestrictionExemptionReason(uid);
}
+ private void setStoppableApps(String[] packageNames, boolean vendor) throws Exception {
+ overrideResource(vendor ? com.android.internal.R.array.vendor_stoppable_fgs_system_apps
+ : com.android.internal.R.array.stoppable_fgs_system_apps, packageNames);
+ }
+
FgsManagerController createFgsManagerController() throws RemoteException {
ArgumentCaptor<IForegroundServiceObserver> iForegroundServiceObserverArgumentCaptor =
ArgumentCaptor.forClass(IForegroundServiceObserver.class);
@@ -455,7 +485,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(BroadcastReceiver.class);
FgsManagerController result = new FgsManagerControllerImpl(
- mContext,
+ mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
mSystemClock,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 9fe9ed2bf47a..b72668bf5be6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.qs.composefragment.viewmodel
import android.app.StatusBarManager
import android.content.testableContext
+import android.graphics.Rect
import android.testing.TestableLooper.RunWithLooper
import androidx.compose.runtime.snapshots.Snapshot
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -36,12 +37,14 @@ import com.android.systemui.qs.composefragment.viewmodel.MediaState.ANY_MEDIA
import com.android.systemui.qs.composefragment.viewmodel.MediaState.NO_MEDIA
import com.android.systemui.qs.fgsManagerController
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
+import com.android.systemui.qs.panels.ui.viewmodel.setConfigurationForMediaInRow
import com.android.systemui.res.R
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.util.animation.DisappearParameters
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -269,6 +272,197 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
}
}
+ @Test
+ fun mediaNotInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = false)
+ setMediaState(ACTIVE_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isFalse()
+ assertThat(underTest.qsMediaInRow).isFalse()
+ }
+ }
+
+ @Test
+ fun mediaInRow_mediaActive_bothInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(ACTIVE_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isTrue()
+ assertThat(underTest.qsMediaInRow).isTrue()
+ }
+ }
+
+ @Test
+ fun mediaInRow_mediaRecommendation_onlyQSInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(ANY_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isFalse()
+ assertThat(underTest.qsMediaInRow).isTrue()
+ }
+ }
+
+ @Test
+ fun mediaInRow_correctConfig_noMediaVisible_noMediaInRow() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(NO_MEDIA)
+
+ assertThat(underTest.qqsMediaInRow).isFalse()
+ assertThat(underTest.qsMediaInRow).isFalse()
+ }
+ }
+
+ @Test
+ fun qqsMediaExpansion_collapsedMediaInLandscape() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setCollapsedMediaInLandscape(true)
+ setMediaState(ACTIVE_MEDIA)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.COLLAPSED)
+ }
+ }
+
+ @Test
+ fun qqsMediaExpansion_notCollapsedMediaInLandscape_alwaysExpanded() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setCollapsedMediaInLandscape(false)
+ setMediaState(ACTIVE_MEDIA)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+ }
+ }
+
+ @Test
+ fun qqsMediaExpansion_reactsToChangesInCollapsedMediaInLandscape() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setConfigurationForMediaInRow(mediaInRow = true)
+ setMediaState(ACTIVE_MEDIA)
+
+ setCollapsedMediaInLandscape(false)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+
+ setCollapsedMediaInLandscape(true)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.COLLAPSED)
+ }
+ }
+
+ @Test
+ fun applyQsScrollPositionForClipping() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ val left = 1f
+ val top = 3f
+ val right = 5f
+ val bottom = 7f
+
+ underTest.applyNewQsScrollerBounds(left, top, right, bottom)
+
+ assertThat(qsMediaHost.currentClipping)
+ .isEqualTo(Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt()))
+ }
+ }
+
+ @Test
+ fun shouldUpdateMediaSquishiness_inSplitShadeFalse_mediaSquishinessSet() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest.isInSplitShade = false
+ underTest.squishinessFraction = 0.3f
+
+ underTest.shouldUpdateSquishinessOnMedia = true
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(0.3f)
+
+ underTest.shouldUpdateSquishinessOnMedia = false
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(1f)
+ }
+ }
+
+ @Test
+ fun inSplitShade_differentStatusBarState_mediaSquishinessSet() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest.isInSplitShade = true
+ underTest.squishinessFraction = 0.3f
+
+ sysuiStatusBarStateController.setState(StatusBarState.SHADE)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(0.3f)
+
+ sysuiStatusBarStateController.setState(StatusBarState.KEYGUARD)
+ runCurrent()
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f)
+
+ sysuiStatusBarStateController.setState(StatusBarState.SHADE_LOCKED)
+ runCurrent()
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f)
+ }
+ }
+
+ @Test
+ fun disappearParams() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setMediaState(ACTIVE_MEDIA)
+
+ setConfigurationForMediaInRow(false)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+
+ assertThat(underTest.qqsMediaHost.disappearParameters)
+ .isEqualTo(disappearParamsColumn)
+ assertThat(underTest.qsMediaHost.disappearParameters)
+ .isEqualTo(disappearParamsColumn)
+
+ setConfigurationForMediaInRow(true)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+
+ assertThat(underTest.qqsMediaHost.disappearParameters).isEqualTo(disappearParamsRow)
+ assertThat(underTest.qsMediaHost.disappearParameters).isEqualTo(disappearParamsRow)
+ }
+ }
+
private fun TestScope.setMediaState(state: MediaState) {
with(kosmos) {
val activeMedia = state == ACTIVE_MEDIA
@@ -282,6 +476,14 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
runCurrent()
}
+ private fun TestScope.setCollapsedMediaInLandscape(collapsed: Boolean) {
+ with(kosmos) {
+ overrideResource(R.bool.config_quickSettingsMediaLandscapeCollapsed, collapsed)
+ fakeConfigurationRepository.onAnyConfigurationChange()
+ }
+ runCurrent()
+ }
+
companion object {
private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS
@@ -290,6 +492,26 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest()
}
private const val epsilon = 0.001f
+
+ private val disappearParamsColumn =
+ DisappearParameters().apply {
+ fadeStartPosition = 0.95f
+ disappearStart = 0f
+ disappearEnd = 0.95f
+ disappearSize.set(1f, 0f)
+ gonePivot.set(0f, 0f)
+ contentTranslationFraction.set(0f, 1f)
+ }
+
+ private val disappearParamsRow =
+ DisappearParameters().apply {
+ fadeStartPosition = 0.95f
+ disappearStart = 0f
+ disappearEnd = 0.6f
+ disappearSize.set(0f, 0.4f)
+ gonePivot.set(1f, 0f)
+ contentTranslationFraction.set(0.25f, 1f)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
index dda9cd5529a5..4dcbdfae6d4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
@@ -104,67 +104,6 @@ class QSPreferencesRepositoryTest : SysuiTestCase() {
}
}
- @Test
- fun showLabels_updatesFromSharedPreferences() =
- with(kosmos) {
- testScope.runTest {
- val latest by collectLastValue(underTest.showLabels)
- assertThat(latest).isFalse()
-
- setShowLabelsInSharedPreferences(true)
- assertThat(latest).isTrue()
-
- setShowLabelsInSharedPreferences(false)
- assertThat(latest).isFalse()
- }
- }
-
- @Test
- fun showLabels_updatesFromUserChange() =
- with(kosmos) {
- testScope.runTest {
- fakeUserRepository.setUserInfos(USERS)
- val latest by collectLastValue(underTest.showLabels)
-
- fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
- setShowLabelsInSharedPreferences(false)
-
- fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
- setShowLabelsInSharedPreferences(true)
-
- fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
- assertThat(latest).isFalse()
- }
- }
-
- @Test
- fun setShowLabels_inSharedPreferences() {
- underTest.setShowLabels(false)
- assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
-
- underTest.setShowLabels(true)
- assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
- }
-
- @Test
- fun setShowLabels_forDifferentUser() =
- with(kosmos) {
- testScope.runTest {
- fakeUserRepository.setUserInfos(USERS)
-
- fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
- underTest.setShowLabels(false)
- assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
-
- fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
- underTest.setShowLabels(true)
- assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
-
- fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
- assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
- }
- }
-
private fun getSharedPreferences(): SharedPreferences =
with(kosmos) {
return userFileManager.getSharedPreferences(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt
new file mode 100644
index 000000000000..ab78029684d4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/QSFragmentComposeTest.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.getBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.width
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.composefragment.QuickQuickSettingsLayout
+import com.android.systemui.qs.composefragment.QuickSettingsLayout
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class QSFragmentComposeTest : SysuiTestCase() {
+
+ @get:Rule val composeTestRule = createComposeRule()
+
+ @Test
+ fun portraitLayout_qqs() {
+ composeTestRule.setContent {
+ QuickQuickSettingsLayout(
+ tiles = { Tiles(TILES_HEIGHT_PORTRAIT) },
+ media = { Media() },
+ mediaInRow = false,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ // All nodes aligned in a column
+ assertThat(tilesBounds.left).isEqualTo(mediaBounds.left)
+ assertThat(tilesBounds.right).isEqualTo(mediaBounds.right)
+ assertThat(tilesBounds.bottom).isLessThan(mediaBounds.top)
+ }
+
+ @Test
+ fun landscapeLayout_qqs() {
+ composeTestRule.setContent {
+ QuickQuickSettingsLayout(
+ tiles = { Tiles(TILES_HEIGHT_LANDSCAPE) },
+ media = { Media() },
+ mediaInRow = true,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ // Media to the right of tiles
+ assertThat(tilesBounds.right).isLessThan(mediaBounds.left)
+ // "Same" width
+ assertThat((tilesBounds.width - mediaBounds.width).abs()).isAtMost(1.dp)
+ // Vertically centered
+ assertThat((tilesBounds.centerY - mediaBounds.centerY).abs()).isAtMost(1.dp)
+ }
+
+ @Test
+ fun portraitLayout_qs() {
+ composeTestRule.setContent {
+ QuickSettingsLayout(
+ brightness = { Brightness() },
+ tiles = { Tiles(TILES_HEIGHT_PORTRAIT) },
+ media = { Media() },
+ mediaInRow = false,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val brightnessBounds = composeTestRule.onNodeWithTag(BRIGHTNESS).getBoundsInRoot()
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ assertThat(brightnessBounds.left).isEqualTo(tilesBounds.left)
+ assertThat(tilesBounds.left).isEqualTo(mediaBounds.left)
+
+ assertThat(brightnessBounds.right).isEqualTo(tilesBounds.right)
+ assertThat(tilesBounds.right).isEqualTo(mediaBounds.right)
+
+ assertThat(brightnessBounds.bottom).isLessThan(tilesBounds.top)
+ assertThat(tilesBounds.bottom).isLessThan(mediaBounds.top)
+ }
+
+ @Test
+ fun landscapeLayout_qs() {
+ composeTestRule.setContent {
+ QuickSettingsLayout(
+ brightness = { Brightness() },
+ tiles = { Tiles(TILES_HEIGHT_PORTRAIT) },
+ media = { Media() },
+ mediaInRow = true,
+ )
+ }
+
+ composeTestRule.waitForIdle()
+
+ val brightnessBounds = composeTestRule.onNodeWithTag(BRIGHTNESS).getBoundsInRoot()
+ val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot()
+ val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot()
+
+ // Brightness takes full width, with left end aligned with tiles and right end aligned with
+ // media
+ assertThat(brightnessBounds.left).isEqualTo(tilesBounds.left)
+ assertThat(brightnessBounds.right).isEqualTo(mediaBounds.right)
+
+ // Brightness above tiles and media
+ assertThat(brightnessBounds.bottom).isLessThan(tilesBounds.top)
+ assertThat(brightnessBounds.bottom).isLessThan(mediaBounds.top)
+
+ // Media to the right of tiles
+ assertThat(tilesBounds.right).isLessThan(mediaBounds.left)
+ // "Same" width
+ assertThat((tilesBounds.width - mediaBounds.width).abs()).isAtMost(1.dp)
+ // Vertically centered
+ assertThat((tilesBounds.centerY - mediaBounds.centerY).abs()).isAtMost(1.dp)
+ }
+
+ private companion object {
+ const val BRIGHTNESS = "brightness"
+ const val TILES = "tiles"
+ const val MEDIA = "media"
+ val TILES_HEIGHT_PORTRAIT = 300.dp
+ val TILES_HEIGHT_LANDSCAPE = 150.dp
+ val MEDIA_HEIGHT = 100.dp
+ val BRIGHTNESS_HEIGHT = 64.dp
+
+ @Composable
+ fun Brightness() {
+ Box(modifier = Modifier.testTag(BRIGHTNESS).height(BRIGHTNESS_HEIGHT).fillMaxWidth())
+ }
+
+ @Composable
+ fun Tiles(height: Dp) {
+ Box(modifier = Modifier.testTag(TILES).height(height).fillMaxWidth())
+ }
+
+ @Composable
+ fun Media() {
+ Box(modifier = Modifier.testTag(MEDIA).height(MEDIA_HEIGHT).fillMaxWidth())
+ }
+
+ val DpRect.centerY: Dp
+ get() = (top + bottom) / 2
+
+ fun Dp.abs() = if (this > 0.dp) this else -this
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 3910903af4aa..ae7c44e9b146 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -35,7 +35,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class EditTileListStateTest : SysuiTestCase() {
- private val underTest = EditTileListState(TestEditTiles, 4)
+ private val underTest = EditTileListState(TestEditTiles, columns = 4, largeTilesSpan = 2)
@Test
fun startDrag_listHasSpacers() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
new file mode 100644
index 000000000000..635badac04f5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Configuration
+import android.content.res.mainResources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.flags.setFlagValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(ParameterizedAndroidJunit4::class)
+@SmallTest
+class MediaInRowInLandscapeViewModelTest(private val testData: TestData) : SysuiTestCase() {
+
+ private val kosmos = testKosmos().apply { usingMediaInComposeFragment = testData.usingMedia }
+
+ private val underTest by lazy {
+ kosmos.mediaInRowInLandscapeViewModelFactory.create(TESTED_MEDIA_LOCATION)
+ }
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.setFlagValue(DualShade.FLAG_NAME, testData.shadeMode == ShadeMode.Dual)
+ }
+
+ @Test
+ fun shouldMediaShowInRow() =
+ with(kosmos) {
+ testScope.runTest {
+ underTest.activateIn(testScope)
+
+ shadeRepository.setShadeLayoutWide(testData.shadeMode != ShadeMode.Single)
+ val config =
+ Configuration(mainResources.configuration).apply {
+ orientation = testData.orientation
+ screenLayout = testData.screenLayoutLong
+ }
+ fakeConfigurationRepository.onConfigurationChange(config)
+ mainResources.configuration.updateFrom(config)
+ mediaHostStatesManager.updateHostState(
+ testData.mediaLocation,
+ MediaHost.MediaHostStateHolder().apply { visible = testData.mediaVisible },
+ )
+ runCurrent()
+
+ assertThat(underTest.shouldMediaShowInRow).isEqualTo(testData.mediaInRowExpected)
+ }
+ }
+
+ data class TestData(
+ val usingMedia: Boolean,
+ val shadeMode: ShadeMode,
+ val orientation: Int,
+ val screenLayoutLong: Int,
+ val mediaVisible: Boolean,
+ @MediaLocation val mediaLocation: Int,
+ ) {
+ val mediaInRowExpected: Boolean
+ get() =
+ usingMedia &&
+ shadeMode == ShadeMode.Single &&
+ orientation == Configuration.ORIENTATION_LANDSCAPE &&
+ screenLayoutLong == Configuration.SCREENLAYOUT_LONG_YES &&
+ mediaVisible &&
+ mediaLocation == TESTED_MEDIA_LOCATION
+ }
+
+ companion object {
+ private const val TESTED_MEDIA_LOCATION = LOCATION_QS
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun data(): Collection<TestData> {
+ val usingMediaValues = setOf(true, false)
+ val shadeModeValues = setOf(ShadeMode.Single, ShadeMode.Split, ShadeMode.Dual)
+ val orientationValues =
+ setOf(Configuration.ORIENTATION_LANDSCAPE, Configuration.ORIENTATION_PORTRAIT)
+ val screenLayoutLongValues =
+ setOf(Configuration.SCREENLAYOUT_LONG_YES, Configuration.SCREENLAYOUT_LONG_NO)
+ val mediaVisibleValues = setOf(true, false)
+ val mediaLocationsValues = setOf(LOCATION_QS, LOCATION_QQS)
+
+ return usingMediaValues.flatMap { usingMedia ->
+ shadeModeValues.flatMap { shadeMode ->
+ orientationValues.flatMap { orientation ->
+ screenLayoutLongValues.flatMap { screenLayoutLong ->
+ mediaVisibleValues.flatMap { mediaVisible ->
+ mediaLocationsValues.map { mediaLocation ->
+ TestData(
+ usingMedia,
+ shadeMode,
+ orientation,
+ screenLayoutLong,
+ mediaVisible,
+ mediaLocation,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
new file mode 100644
index 000000000000..4ae8589de87b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelTest.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
+import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
+import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
+import com.android.systemui.res.R
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class QSColumnsViewModelTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ usingMediaInComposeFragment = true
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_infinite_grid_num_columns,
+ SINGLE_SPLIT_SHADE_COLUMNS,
+ )
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_dual_shade_num_columns,
+ DUAL_SHADE_COLUMNS,
+ )
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_split_shade_num_columns,
+ SINGLE_SPLIT_SHADE_COLUMNS,
+ )
+ qsColumnsRepository = QSColumnsRepository(mainResources, configurationRepository)
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationNull_singleOrSplit_alwaysSingleShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(null)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationNull_dualShade_alwaysDualShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(null)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQS_dualShade_alwaysDualShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @EnableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQQS_dualShade_alwaysDualShadeColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(DUAL_SHADE_COLUMNS)
+ }
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQS_singleOrSplit_halfColumnsOnCorrectConfigurationAndVisible() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ makeMediaVisible(LOCATION_QS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS / 2)
+ }
+ }
+
+ @Test
+ @DisableFlags(DualShade.FLAG_NAME)
+ fun mediaLocationQQS_singleOrSplit_halfColumnsOnCorrectConfigurationAndVisible() =
+ with(kosmos) {
+ testScope.runTest {
+ val underTest = qsColumnsViewModelFactory.create(LOCATION_QQS)
+ underTest.activateIn(testScope)
+
+ setConfigurationForMediaInRow(mediaInRow = false)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS)
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(SINGLE_SPLIT_SHADE_COLUMNS / 2)
+ }
+ }
+
+ companion object {
+ private const val SINGLE_SPLIT_SHADE_COLUMNS = 4
+ private const val DUAL_SHADE_COLUMNS = 2
+
+ private fun Kosmos.makeMediaVisible(@MediaLocation location: Int, visible: Boolean) {
+ mediaHostStatesManager.updateHostState(
+ location,
+ MediaHost.MediaHostStateHolder().apply { this.visible = visible },
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index ab5a049c91f1..3d1265aec45e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -24,6 +24,11 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.qs.panels.domain.interactor.qsPreferencesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -67,6 +72,7 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
4,
)
fakeConfigurationRepository.onConfigurationChange()
+ usingMediaInComposeFragment = true
}
private val underTest =
@@ -145,6 +151,33 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
}
}
+ @Test
+ fun mediaVisibleInLandscape_doubleRows_halfColumns() =
+ with(kosmos) {
+ testScope.runTest {
+ setRows(1)
+ assertThat(underTest.columns).isEqualTo(4)
+ // All tiles in 4 columns (but we only show the first 3 tiles)
+ // [1] [2] [3 3]
+ // [4] [5 5]
+ // [6 6] [7] [8]
+ // [9 9]
+
+ runCurrent()
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(3))
+
+ makeMediaVisible(LOCATION_QQS, visible = true)
+ setConfigurationForMediaInRow(mediaInRow = true)
+ runCurrent()
+
+ assertThat(underTest.columns).isEqualTo(2)
+ // Tiles in 4 columns
+ // [1] [2]
+ // [3 3]
+ assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(3))
+ }
+ }
+
private fun Kosmos.setTiles(tiles: List<TileSpec>) {
currentTilesInteractor.setTiles(tiles)
}
@@ -163,5 +196,12 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
private companion object {
const val PREFIX_SMALL = "small"
const val PREFIX_LARGE = "large"
+
+ private fun Kosmos.makeMediaVisible(@MediaLocation location: Int, visible: Boolean) {
+ mediaHostStatesManager.updateHostState(
+ location,
+ MediaHost.MediaHostStateHolder().apply { this.visible = visible },
+ )
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 0729e2fcd35f..03c1f92aad4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -93,6 +93,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
private static final String CARD_DESCRIPTION = "•••• 1234";
private static final Icon CARD_IMAGE =
Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
+ private static final Icon INVALID_CARD_IMAGE =
+ Icon.createWithContentUri("content://media/external/images/media");
private static final int PRIMARY_USER_ID = 0;
private static final int SECONDARY_USER_ID = 10;
@@ -444,6 +446,14 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
}
@Test
+ public void testQueryCards_invalidDrawable_noSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpInvalidWalletCard(/* hasCard= */ true);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
public void testQueryCards_error_notUpdateSideViewDrawable() {
String errorMessage = "getWalletCardsError";
GetWalletCardsError error = new GetWalletCardsError(CARD_IMAGE, errorMessage);
@@ -503,9 +513,33 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
}
+ private void setUpInvalidWalletCard(boolean hasCard) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(createInvalidWalletCard(mContext))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
private WalletCard createWalletCard(Context context) {
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
return new WalletCard.Builder(CARD_ID, CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
}
+
+ private WalletCard createInvalidWalletCard(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(
+ CARD_ID, INVALID_CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build();
+ }
+
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 0d12483bad0a..53708fd417e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -23,7 +23,6 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -160,9 +159,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
ArgumentCaptor<Runnable> onStartRecordingClicked = ArgumentCaptor.forClass(Runnable.class);
- verify(mController).createScreenRecordDialog(any(), eq(mFeatureFlags),
- eq(mDialogTransitionAnimator), eq(mActivityStarter),
- onStartRecordingClicked.capture());
+ verify(mController).createScreenRecordDialog(onStartRecordingClicked.capture());
// When starting the recording, we collapse the shade and disable the dialog animation.
assertNotNull(onStartRecordingClicked.getValue());
@@ -298,14 +295,13 @@ public class ScreenRecordTileTest extends SysuiTestCase {
public void showingDialogPrompt_logsMediaProjectionPermissionRequested() {
when(mController.isStarting()).thenReturn(false);
when(mController.isRecording()).thenReturn(false);
- when(mController.createScreenRecordDialog(any(), any(), any(), any(), any()))
+ when(mController.createScreenRecordDialog(any()))
.thenReturn(mPermissionDialogPrompt);
mTile.handleClick(null /* view */);
mTestableLooper.processAllMessages();
- verify(mController).createScreenRecordDialog(any(), eq(mFeatureFlags),
- eq(mDialogTransitionAnimator), eq(mActivityStarter), any());
+ verify(mController).createScreenRecordDialog(any());
var onDismissAction = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction.class);
verify(mKeyguardDismissUtil).executeWhenUnlocked(
onDismissAction.capture(), anyBoolean(), anyBoolean());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index ff40e43e2c8c..a06353171c33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -115,7 +115,7 @@ class QSTileLoggerTest : SysuiTestCase() {
underTest.logUserActionPipeline(
TileSpec.create("test_spec"),
QSTileUserAction.Click(null),
- QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {},
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {},
"test_data",
)
@@ -141,7 +141,7 @@ class QSTileLoggerTest : SysuiTestCase() {
fun testLogStateUpdate() {
underTest.logStateUpdate(
TileSpec.create("test_spec"),
- QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {},
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {},
"test_data",
)
@@ -162,18 +162,14 @@ class QSTileLoggerTest : SysuiTestCase() {
@Test
fun testLogForceUpdate() {
- underTest.logForceUpdate(
- TileSpec.create("test_spec"),
- )
+ underTest.logForceUpdate(TileSpec.create("test_spec"))
assertThat(logBuffer.getStringBuffer()).contains("tile data force update")
}
@Test
fun testLogInitialUpdate() {
- underTest.logInitialRequest(
- TileSpec.create("test_spec"),
- )
+ underTest.logInitialRequest(TileSpec.create("test_spec"))
assertThat(logBuffer.getStringBuffer()).contains("tile data initial update")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index c918ed82604c..056efb34a0b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -85,8 +85,8 @@ class QSTileViewModelImplTest : SysuiTestCase() {
object : QSTileDataToStateMapper<Any> {
override fun map(config: QSTileConfig, data: Any): QSTileState =
QSTileState.build(
- { Icon.Resource(0, ContentDescription.Resource(0)) },
- data.toString()
+ Icon.Resource(0, ContentDescription.Resource(0)),
+ data.toString(),
) {}
}
},
@@ -116,7 +116,7 @@ class QSTileViewModelImplTest : SysuiTestCase() {
.isEqualTo(
"test_spec:\n" +
" QSTileState(" +
- "icon=() -> com.android.systemui.common.shared.model.Icon?, " +
+ "icon=Resource(res=0, contentDescription=Resource(res=0)), " +
"iconRes=null, " +
"label=test_data, " +
"activationState=INACTIVE, " +
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
index 5a73fe28ee18..00460bfe83b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
@@ -66,7 +66,7 @@ class AirplaneModeMapperTest : SysuiTestCase() {
createAirplaneModeState(
QSTileState.ActivationState.ACTIVE,
context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_ACTIVE],
- R.drawable.qs_airplane_icon_on
+ R.drawable.qs_airplane_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -81,7 +81,7 @@ class AirplaneModeMapperTest : SysuiTestCase() {
createAirplaneModeState(
QSTileState.ActivationState.INACTIVE,
context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_INACTIVE],
- R.drawable.qs_airplane_icon_off
+ R.drawable.qs_airplane_icon_off,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -89,11 +89,11 @@ class AirplaneModeMapperTest : SysuiTestCase() {
private fun createAirplaneModeState(
activationState: QSTileState.ActivationState,
secondaryLabel: String,
- iconRes: Int
+ iconRes: Int,
): QSTileState {
val label = context.getString(R.string.airplane_mode)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -103,7 +103,7 @@ class AirplaneModeMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 79e4fef874b6..632aae035ede 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -51,7 +51,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
.apply { addOverride(R.drawable.ic_alarm, TestStubDrawable()) }
.resources,
context.theme,
- fakeClock
+ fakeClock,
)
}
@@ -69,7 +69,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
val expectedState =
createAlarmTileState(
QSTileState.ActivationState.INACTIVE,
- context.getString(R.string.qs_alarm_tile_no_alarm)
+ context.getString(R.string.qs_alarm_tile_no_alarm),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -85,7 +85,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
val localDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(triggerTime),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
val expectedSecondaryLabel = AlarmTileMapper.formatter24Hour.format(localDateTime)
val expectedState =
@@ -104,7 +104,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
val localDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(triggerTime),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
val expectedSecondaryLabel = AlarmTileMapper.formatter12Hour.format(localDateTime)
val expectedState =
@@ -124,7 +124,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
val localDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(triggerTime),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
val expectedSecondaryLabel = AlarmTileMapper.formatterDateOnly.format(localDateTime)
val expectedState =
@@ -144,7 +144,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
val localDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(triggerTime),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
val expectedSecondaryLabel = AlarmTileMapper.formatter12Hour.format(localDateTime)
val expectedState =
@@ -164,7 +164,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
val localDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(triggerTime),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
val expectedSecondaryLabel = AlarmTileMapper.formatterDateOnly.format(localDateTime)
val expectedState =
@@ -174,11 +174,11 @@ class AlarmTileMapperTest : SysuiTestCase() {
private fun createAlarmTileState(
activationState: QSTileState.ActivationState,
- secondaryLabel: String
+ secondaryLabel: String,
): QSTileState {
val label = context.getString(R.string.status_bar_alarm)
return QSTileState(
- { Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null) },
+ Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null),
R.drawable.ic_alarm,
label,
activationState,
@@ -188,7 +188,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.Chevron,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index a0d26c28cbfa..5385f945946c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -253,7 +253,7 @@ class BatterySaverTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.battery_detail_switch_title)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -265,7 +265,7 @@ class BatterySaverTileMapperTest : SysuiTestCase() {
stateDescription,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index ea7b7c5f797d..356b98eb192e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -45,7 +45,7 @@ class ColorCorrectionTileMapperTest : SysuiTestCase() {
context.orCreateTestableResources
.apply { addOverride(R.drawable.ic_qs_color_correction, TestStubDrawable()) }
.resources,
- context.theme
+ context.theme,
)
}
@@ -73,11 +73,11 @@ class ColorCorrectionTileMapperTest : SysuiTestCase() {
private fun createColorCorrectionTileState(
activationState: QSTileState.ActivationState,
- secondaryLabel: String
+ secondaryLabel: String,
): QSTileState {
val label = context.getString(R.string.quick_settings_color_correction_label)
return QSTileState(
- { Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null) },
+ Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null),
R.drawable.ic_qs_color_correction,
label,
activationState,
@@ -87,7 +87,7 @@ class ColorCorrectionTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
index f1d08c068150..8236c4c1e638 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -57,10 +57,7 @@ class CustomTileMapperTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply { customTileSpec = TileSpec.Companion.create(TEST_COMPONENT) }
private val underTest by lazy {
- CustomTileMapper(
- context = mockContext,
- uriGrantsManager = uriGrantsManager,
- )
+ CustomTileMapper(context = mockContext, uriGrantsManager = uriGrantsManager)
}
@Test
@@ -68,10 +65,7 @@ class CustomTileMapperTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
val actual =
- underTest.map(
- customTileQsTileConfig,
- createModel(hasPendingBind = true),
- )
+ underTest.map(customTileQsTileConfig, createModel(hasPendingBind = true))
val expected =
createTileState(
activationState = QSTileState.ActivationState.UNAVAILABLE,
@@ -91,10 +85,7 @@ class CustomTileMapperTest : SysuiTestCase() {
customTileQsTileConfig,
createModel(tileState = Tile.STATE_ACTIVE),
)
- val expected =
- createTileState(
- activationState = QSTileState.ActivationState.ACTIVE,
- )
+ val expected = createTileState(activationState = QSTileState.ActivationState.ACTIVE)
assertThat(actual).isEqualTo(expected)
}
@@ -110,9 +101,7 @@ class CustomTileMapperTest : SysuiTestCase() {
createModel(tileState = Tile.STATE_INACTIVE),
)
val expected =
- createTileState(
- activationState = QSTileState.ActivationState.INACTIVE,
- )
+ createTileState(activationState = QSTileState.ActivationState.INACTIVE)
assertThat(actual).isEqualTo(expected)
}
@@ -142,10 +131,7 @@ class CustomTileMapperTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
val actual =
- underTest.map(
- customTileQsTileConfig,
- createModel(isToggleable = false),
- )
+ underTest.map(customTileQsTileConfig, createModel(isToggleable = false))
val expected =
createTileState(
sideIcon = QSTileState.SideViewIcon.Chevron,
@@ -184,7 +170,7 @@ class CustomTileMapperTest : SysuiTestCase() {
customTileQsTileConfig,
createModel(
tileIcon = createIcon(RuntimeException(), false),
- defaultTileIcon = createIcon(null, true)
+ defaultTileIcon = createIcon(null, true),
),
)
val expected =
@@ -266,7 +252,7 @@ class CustomTileMapperTest : SysuiTestCase() {
a11yClass: String? = Switch::class.qualifiedName,
): QSTileState {
return QSTileState(
- { icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) } },
+ icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) },
null,
"test label",
activationState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index 63fb67d432f0..587585ccee2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -44,7 +44,7 @@ class FlashlightMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_flashlight_icon_on, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -74,7 +74,7 @@ class FlashlightMapperTest : SysuiTestCase() {
val expectedIcon =
Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
- val actualIcon = tileState.icon()
+ val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -85,7 +85,7 @@ class FlashlightMapperTest : SysuiTestCase() {
val expectedIcon =
Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
- val actualIcon = tileState.icon()
+ val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -96,7 +96,7 @@ class FlashlightMapperTest : SysuiTestCase() {
val expectedIcon =
Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
- val actualIcon = tileState.icon()
+ val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index f8e01be5163f..e81771ec38d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -42,7 +42,7 @@ class FontScalingTileMapperTest : SysuiTestCase() {
context.orCreateTestableResources
.apply { addOverride(R.drawable.ic_qs_font_scaling, TestStubDrawable()) }
.resources,
- context.theme
+ context.theme,
)
}
@@ -58,14 +58,7 @@ class FontScalingTileMapperTest : SysuiTestCase() {
private fun createFontScalingTileState(): QSTileState =
QSTileState(
- {
- Icon.Loaded(
- context.getDrawable(
- R.drawable.ic_qs_font_scaling,
- )!!,
- null
- )
- },
+ Icon.Loaded(context.getDrawable(R.drawable.ic_qs_font_scaling)!!, null),
R.drawable.ic_qs_font_scaling,
context.getString(R.string.quick_settings_font_scaling_label),
QSTileState.ActivationState.ACTIVE,
@@ -75,6 +68,6 @@ class FontScalingTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.Chevron,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
index cdf6bda91301..12d604ff6a7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -102,7 +102,7 @@ class HearingDevicesTileMapperTest : SysuiTestCase() {
val label = context.getString(R.string.quick_settings_hearing_devices_label)
val iconRes = R.drawable.qs_hearing_devices_icon
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index d32ba47204c0..9dcf49e02697 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -187,7 +187,7 @@ class InternetTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.quick_settings_internet_label)
return QSTileState(
- { icon },
+ icon,
iconRes,
label,
activationState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index a7bd69770a4f..30fce73e04da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -49,7 +49,7 @@ class ColorInversionTileMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_invert_colors_icon_on, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -63,7 +63,7 @@ class ColorInversionTileMapperTest : SysuiTestCase() {
createColorInversionTileState(
QSTileState.ActivationState.INACTIVE,
subtitleArray[1],
- R.drawable.qs_invert_colors_icon_off
+ R.drawable.qs_invert_colors_icon_off,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -78,7 +78,7 @@ class ColorInversionTileMapperTest : SysuiTestCase() {
createColorInversionTileState(
QSTileState.ActivationState.ACTIVE,
subtitleArray[2],
- R.drawable.qs_invert_colors_icon_on
+ R.drawable.qs_invert_colors_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -90,7 +90,7 @@ class ColorInversionTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.quick_settings_inversion_label)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -100,7 +100,7 @@ class ColorInversionTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index ea74a4c0d398..37e8a6053682 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -45,7 +45,7 @@ class LocationTileMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_location_icon_on, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -70,7 +70,7 @@ class LocationTileMapperTest : SysuiTestCase() {
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_on)!!, null)
- val actualIcon = tileState.icon()
+ val actualIcon = tileState.icon
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -79,7 +79,7 @@ class LocationTileMapperTest : SysuiTestCase() {
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_off)!!, null)
- val actualIcon = tileState.icon()
+ val actualIcon = tileState.icon
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index c3d45dbbd09a..4e91d16bf1ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -59,34 +59,24 @@ class ModesTileMapperTest : SysuiTestCase() {
@Test
fun inactiveState() {
val icon = TestStubDrawable("res123").asIcon()
- val model =
- ModesTileModel(
- isActivated = false,
- activeModes = emptyList(),
- icon = icon,
- )
+ val model = ModesTileModel(isActivated = false, activeModes = emptyList(), icon = icon)
val state = underTest.map(config, model)
assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE)
- assertThat(state.icon()).isEqualTo(icon)
+ assertThat(state.icon).isEqualTo(icon)
assertThat(state.secondaryLabel).isEqualTo("No active modes")
}
@Test
fun activeState_oneMode() {
val icon = TestStubDrawable("res123").asIcon()
- val model =
- ModesTileModel(
- isActivated = true,
- activeModes = listOf("DND"),
- icon = icon,
- )
+ val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"), icon = icon)
val state = underTest.map(config, model)
assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
- assertThat(state.icon()).isEqualTo(icon)
+ assertThat(state.icon).isEqualTo(icon)
assertThat(state.secondaryLabel).isEqualTo("DND is active")
}
@@ -103,7 +93,7 @@ class ModesTileMapperTest : SysuiTestCase() {
val state = underTest.map(config, model)
assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
- assertThat(state.icon()).isEqualTo(icon)
+ assertThat(state.icon).isEqualTo(icon)
assertThat(state.secondaryLabel).isEqualTo("3 modes are active")
}
@@ -115,12 +105,12 @@ class ModesTileMapperTest : SysuiTestCase() {
isActivated = false,
activeModes = emptyList(),
icon = icon,
- iconResId = 123
+ iconResId = 123,
)
val state = underTest.map(config, model)
- assertThat(state.icon()).isEqualTo(icon)
+ assertThat(state.icon).isEqualTo(icon)
assertThat(state.iconRes).isEqualTo(123)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 75273f2a52e1..1457f533f5ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -73,7 +73,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
val expectedState =
createNightDisplayTileState(
QSTileState.ActivationState.INACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE]
+ context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE],
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -88,7 +88,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
val expectedState =
createNightDisplayTileState(
QSTileState.ActivationState.INACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE]
+ context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE],
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -102,7 +102,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
val expectedState =
createNightDisplayTileState(
QSTileState.ActivationState.ACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE]
+ context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE],
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -116,7 +116,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
val expectedState =
createNightDisplayTileState(
QSTileState.ActivationState.ACTIVE,
- context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE]
+ context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE],
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -140,7 +140,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
val expectedState =
createNightDisplayTileState(
QSTileState.ActivationState.ACTIVE,
- context.getString(R.string.quick_settings_night_secondary_label_until_sunrise)
+ context.getString(R.string.quick_settings_night_secondary_label_until_sunrise),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -154,7 +154,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
val expectedState =
createNightDisplayTileState(
QSTileState.ActivationState.INACTIVE,
- context.getString(R.string.quick_settings_night_secondary_label_on_at_sunset)
+ context.getString(R.string.quick_settings_night_secondary_label_on_at_sunset),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -181,8 +181,8 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.INACTIVE,
context.getString(
R.string.quick_settings_night_secondary_label_on_at,
- formatter24Hour.format(testStartTime)
- )
+ formatter24Hour.format(testStartTime),
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -199,8 +199,8 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.INACTIVE,
context.getString(
R.string.quick_settings_night_secondary_label_on_at,
- formatter12Hour.format(testStartTime)
- )
+ formatter12Hour.format(testStartTime),
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -218,8 +218,8 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.INACTIVE,
context.getString(
R.string.quick_settings_night_secondary_label_on_at,
- formatter12Hour.format(testStartTime)
- )
+ formatter12Hour.format(testStartTime),
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -235,8 +235,8 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.ACTIVE,
context.getString(
R.string.quick_settings_secondary_label_until,
- formatter24Hour.format(testEndTime)
- )
+ formatter24Hour.format(testEndTime),
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -252,8 +252,8 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.ACTIVE,
context.getString(
R.string.quick_settings_secondary_label_until,
- formatter12Hour.format(testEndTime)
- )
+ formatter12Hour.format(testEndTime),
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -270,15 +270,15 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.ACTIVE,
context.getString(
R.string.quick_settings_secondary_label_until,
- formatter24Hour.format(testEndTime)
- )
+ formatter24Hour.format(testEndTime),
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
private fun createNightDisplayTileState(
activationState: QSTileState.ActivationState,
- secondaryLabel: String?
+ secondaryLabel: String?,
): QSTileState {
val label = context.getString(R.string.quick_settings_night_display_label)
val iconRes =
@@ -289,7 +289,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
if (TextUtils.isEmpty(secondaryLabel)) label
else TextUtils.concat(label, ", ", secondaryLabel)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -299,7 +299,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
new file mode 100644
index 000000000000..2ac3e081b8f4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Button
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotesTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val qsTileConfig = kosmos.qsNotesTileConfig
+
+ private val mapper by lazy {
+ NotesTileMapper(
+ context.orCreateTestableResources
+ .apply { addOverride(R.drawable.ic_qs_notes, TestStubDrawable()) }
+ .resources,
+ context.theme,
+ )
+ }
+
+ @Test
+ fun mappedStateMatchesModel() {
+ val inputModel = NotesTileModel
+
+ val outputState = mapper.map(qsTileConfig, inputModel)
+
+ val expectedState = createNotesTileState()
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createNotesTileState(): QSTileState =
+ QSTileState(
+ Icon.Loaded(context.getDrawable(R.drawable.ic_qs_notes)!!, null),
+ R.drawable.ic_qs_notes,
+ context.getString(R.string.quick_settings_notes_label),
+ QSTileState.ActivationState.INACTIVE,
+ /* secondaryLabel= */ null,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ context.getString(R.string.quick_settings_notes_label),
+ /* stateDescription= */ null,
+ QSTileState.SideViewIcon.Chevron,
+ QSTileState.EnabledState.ENABLED,
+ Button::class.qualifiedName,
+ )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
new file mode 100644
index 000000000000..35d6d5adc3f4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toCollection
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotesTileDataInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val testUser = UserHandle.of(1)
+ private lateinit var underTest: NotesTileDataInteractor
+
+
+ @EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagEnabled_notesRoleEnabled_returnTrue() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(true)
+ }
+
+ @DisableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagDisabled_notesRoleEnabled_returnFalse() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(false)
+ }
+
+ @EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagEnabled_notesRoleDisabled_returnFalse() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = false)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(false)
+ }
+
+ @DisableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
+ @Test
+ fun availability_qsFlagDisabled_notesRoleDisabled_returnFalse() =
+ testScope.runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = false)
+
+ val availability = underTest.availability(testUser).toCollection(mutableListOf())
+
+ assertThat(availability).containsExactly(false)
+ }
+
+ @Test
+ fun tileData_notEmpty() = runTest {
+ underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
+ val flowValue by
+ collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+
+ runCurrent()
+
+ assertThat(flowValue).isNotNull()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..54911e612291
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotesTileUserActionInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+ private val panelInteractor = mock<PanelInteractor>()
+ private val noteTaskController = mock<NoteTaskController>()
+
+ private lateinit var underTest: NotesTileUserActionInteractor
+
+ @Before
+ fun setUp() {
+ underTest = NotesTileUserActionInteractor(inputHandler, panelInteractor, noteTaskController)
+ }
+
+ @Test
+ fun handleClick_launchDefaultNotesApp() =
+ testScope.runTest {
+ underTest.handleInput(QSTileInputTestKtx.click(NotesTileModel))
+
+ verify(noteTaskController).showNoteTask(NoteTaskEntryPoint.QS_NOTES_TILE)
+ }
+
+ @Test
+ fun handleLongClick_launchSettings() =
+ testScope.runTest {
+ underTest.handleInput(QSTileInputTestKtx.longClick(NotesTileModel))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 3189a9e063a1..7782d2b279a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -51,11 +51,11 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
.apply {
addOverride(
com.android.internal.R.drawable.ic_qs_one_handed_mode,
- TestStubDrawable()
+ TestStubDrawable(),
)
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -69,7 +69,7 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
createOneHandedModeTileState(
QSTileState.ActivationState.INACTIVE,
subtitleArray[1],
- com.android.internal.R.drawable.ic_qs_one_handed_mode
+ com.android.internal.R.drawable.ic_qs_one_handed_mode,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -84,7 +84,7 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
createOneHandedModeTileState(
QSTileState.ActivationState.ACTIVE,
subtitleArray[2],
- com.android.internal.R.drawable.ic_qs_one_handed_mode
+ com.android.internal.R.drawable.ic_qs_one_handed_mode,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -96,7 +96,7 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.quick_settings_onehanded_label)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -106,7 +106,7 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index 08e5cbef31ab..ed33250a3392 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -49,11 +49,11 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
.apply {
addOverride(
com.android.systemui.res.R.drawable.ic_qr_code_scanner,
- TestStubDrawable()
+ TestStubDrawable(),
)
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -64,11 +64,7 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createQRCodeScannerTileState(
- QSTileState.ActivationState.INACTIVE,
- null,
- )
+ val expectedState = createQRCodeScannerTileState(QSTileState.ActivationState.INACTIVE, null)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -83,7 +79,7 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.UNAVAILABLE,
context.getString(
com.android.systemui.res.R.string.qr_code_scanner_updating_secondary_label
- )
+ ),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -94,12 +90,10 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title)
return QSTileState(
- {
- Icon.Loaded(
- context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
- null
- )
- },
+ Icon.Loaded(
+ context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
+ null,
+ ),
com.android.systemui.res.R.drawable.ic_qr_code_scanner,
label,
activationState,
@@ -109,7 +103,7 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.Chevron,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index ca30e9ca3e69..85111fd07663 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -51,7 +51,7 @@ class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_extra_dim_icon_off, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -61,10 +61,7 @@ class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
val outputState = mapper.map(config, inputModel)
- val expectedState =
- createReduceBrightColorsTileState(
- QSTileState.ActivationState.INACTIVE,
- )
+ val expectedState = createReduceBrightColorsTileState(QSTileState.ActivationState.INACTIVE)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -79,7 +76,7 @@ class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
}
private fun createReduceBrightColorsTileState(
- activationState: QSTileState.ActivationState,
+ activationState: QSTileState.ActivationState
): QSTileState {
val label =
context.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
@@ -88,7 +85,7 @@ class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
R.drawable.qs_extra_dim_icon_on
else R.drawable.qs_extra_dim_icon_off
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -101,7 +98,7 @@ class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 3e40c5ca797c..53671ba38eb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -66,13 +66,13 @@ class RotationLockTileMapperTest : SysuiTestCase() {
addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf() // empty array <=> device is not foldable
+ intArrayOf(), // empty array <=> device is not foldable
)
}
.resources,
context.theme,
devicePostureController,
- deviceStateManager
+ deviceStateManager,
)
}
@@ -86,7 +86,7 @@ class RotationLockTileMapperTest : SysuiTestCase() {
createRotationLockTileState(
QSTileState.ActivationState.ACTIVE,
EMPTY_SECONDARY_STRING,
- R.drawable.qs_auto_rotate_icon_on
+ R.drawable.qs_auto_rotate_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -101,7 +101,7 @@ class RotationLockTileMapperTest : SysuiTestCase() {
createRotationLockTileState(
QSTileState.ActivationState.ACTIVE,
context.getString(R.string.rotation_lock_camera_rotation_on),
- R.drawable.qs_auto_rotate_icon_on
+ R.drawable.qs_auto_rotate_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -116,7 +116,7 @@ class RotationLockTileMapperTest : SysuiTestCase() {
createRotationLockTileState(
QSTileState.ActivationState.INACTIVE,
EMPTY_SECONDARY_STRING,
- R.drawable.qs_auto_rotate_icon_off
+ R.drawable.qs_auto_rotate_icon_off,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -167,7 +167,7 @@ class RotationLockTileMapperTest : SysuiTestCase() {
mapper.apply {
overrideResource(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf(1, 2, 3)
+ intArrayOf(1, 2, 3),
)
}
whenever(deviceStateManager.supportedDeviceStates).thenReturn(kosmos.foldedDeviceStateList)
@@ -176,11 +176,11 @@ class RotationLockTileMapperTest : SysuiTestCase() {
private fun createRotationLockTileState(
activationState: QSTileState.ActivationState,
secondaryLabel: String,
- iconRes: Int
+ iconRes: Int,
): QSTileState {
val label = context.getString(R.string.quick_settings_rotation_unlocked_label)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -190,7 +190,7 @@ class RotationLockTileMapperTest : SysuiTestCase() {
secondaryLabel,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index 9bb61415de28..9a450653aa8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -46,7 +46,7 @@ class DataSaverTileMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_data_saver_icon_on, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -59,7 +59,7 @@ class DataSaverTileMapperTest : SysuiTestCase() {
val expectedState =
createDataSaverTileState(
QSTileState.ActivationState.ACTIVE,
- R.drawable.qs_data_saver_icon_on
+ R.drawable.qs_data_saver_icon_on,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -73,14 +73,14 @@ class DataSaverTileMapperTest : SysuiTestCase() {
val expectedState =
createDataSaverTileState(
QSTileState.ActivationState.INACTIVE,
- R.drawable.qs_data_saver_icon_off
+ R.drawable.qs_data_saver_icon_off,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
private fun createDataSaverTileState(
activationState: QSTileState.ActivationState,
- iconRes: Int
+ iconRes: Int,
): QSTileState {
val label = context.getString(R.string.data_saver)
val secondaryLabel =
@@ -91,7 +91,7 @@ class DataSaverTileMapperTest : SysuiTestCase() {
else context.resources.getStringArray(R.array.tile_states_saver)[0]
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -101,7 +101,7 @@ class DataSaverTileMapperTest : SysuiTestCase() {
null,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 899122d4dd45..0b56d7b64aab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -23,29 +23,27 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -54,24 +52,11 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
private val testScope = kosmos.testScope
private val keyguardInteractor = kosmos.keyguardInteractor
private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
- private val featureFlags = kosmos.featureFlagsClassic
- private val activityStarter = kosmos.activityStarter
private val keyguardDismissUtil = mock<KeyguardDismissUtil>()
private val panelInteractor = mock<PanelInteractor>()
private val dialog = mock<Dialog>()
private val recordingController =
- mock<RecordingController> {
- whenever(
- createScreenRecordDialog(
- eq(context),
- eq(featureFlags),
- eq(dialogTransitionAnimator),
- eq(activityStarter),
- any()
- )
- )
- .thenReturn(dialog)
- }
+ mock<RecordingController> { on { createScreenRecordDialog(any()) } doReturn dialog }
private val screenRecordRepository =
ScreenRecordRepositoryImpl(
@@ -81,7 +66,6 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
private val underTest =
ScreenRecordTileUserActionInteractor(
- context,
testScope.testScheduler,
testScope.testScheduler,
screenRecordRepository,
@@ -91,8 +75,6 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
dialogTransitionAnimator,
panelInteractor,
mock<MediaProjectionMetricsLogger>(),
- featureFlags,
- activityStarter,
)
@Test
@@ -120,22 +102,16 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
underTest.handleInput(QSTileInputTestKtx.click(recordingModel))
val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
verify(recordingController)
- .createScreenRecordDialog(
- eq(context),
- eq(featureFlags),
- eq(dialogTransitionAnimator),
- eq(activityStarter),
- onStartRecordingClickedCaptor.capture()
- )
+ .createScreenRecordDialog(onStartRecordingClickedCaptor.capture())
val onDismissActionCaptor = argumentCaptor<OnDismissAction>()
verify(keyguardDismissUtil)
.executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
- onDismissActionCaptor.value.onDismiss()
+ onDismissActionCaptor.lastValue.onDismiss()
verify(dialog).show() // because the view was null
// When starting the recording, we collapse the shade and disable the dialog animation.
- onStartRecordingClickedCaptor.value.run()
+ onStartRecordingClickedCaptor.lastValue.run()
verify(dialogTransitionAnimator).disableAllCurrentDialogsExitAnimations()
verify(panelInteractor).collapsePanels()
}
@@ -145,9 +121,9 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
*/
@Test
fun handleClickFromView_whenDoingNothing_whenKeyguardNotShowing_showDialogFromView() = runTest {
- val expandable = mock<Expandable>()
val controller = mock<DialogTransitionAnimator.Controller>()
- whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
+ val expandable =
+ mock<Expandable> { on { dialogTransitionController(any()) } doReturn controller }
kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
@@ -158,18 +134,12 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
)
val onStartRecordingClickedCaptor = argumentCaptor<Runnable>()
verify(recordingController)
- .createScreenRecordDialog(
- eq(context),
- eq(featureFlags),
- eq(dialogTransitionAnimator),
- eq(activityStarter),
- onStartRecordingClickedCaptor.capture()
- )
+ .createScreenRecordDialog(onStartRecordingClickedCaptor.capture())
val onDismissActionCaptor = argumentCaptor<OnDismissAction>()
verify(keyguardDismissUtil)
.executeWhenUnlocked(onDismissActionCaptor.capture(), eq(false), eq(true))
- onDismissActionCaptor.value.onDismiss()
+ onDismissActionCaptor.lastValue.onDismiss()
verify(dialogTransitionAnimator).show(eq(dialog), eq(controller), eq(true))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index 336b56612261..cd683c44a59c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -52,7 +52,7 @@ class ScreenRecordTileMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_screen_record_icon_off, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -82,7 +82,7 @@ class ScreenRecordTileMapperTest : SysuiTestCase() {
createScreenRecordTileState(
QSTileState.ActivationState.ACTIVE,
R.drawable.qs_screen_record_icon_on,
- String.format("%d...", timeLeft)
+ String.format("%d...", timeLeft),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -110,7 +110,7 @@ class ScreenRecordTileMapperTest : SysuiTestCase() {
val label = context.getString(R.string.quick_settings_screen_record_label)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -123,7 +123,7 @@ class ScreenRecordTileMapperTest : SysuiTestCase() {
QSTileState.SideViewIcon.Chevron
else QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index b08f39b0accf..c569403960d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -56,7 +56,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
context.getString(R.string.quick_settings_camera_mic_available),
R.drawable.qs_camera_access_icon_on,
null,
- CAMERA
+ CAMERA,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -74,7 +74,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
context.getString(R.string.quick_settings_camera_mic_blocked),
R.drawable.qs_camera_access_icon_off,
null,
- CAMERA
+ CAMERA,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -92,7 +92,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
context.getString(R.string.quick_settings_camera_mic_available),
R.drawable.qs_mic_access_on,
null,
- MICROPHONE
+ MICROPHONE,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -110,7 +110,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
context.getString(R.string.quick_settings_camera_mic_blocked),
R.drawable.qs_mic_access_off,
null,
- MICROPHONE
+ MICROPHONE,
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -146,7 +146,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
else context.getString(R.string.quick_settings_mic_label)
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -156,7 +156,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
stateDescription,
QSTileState.SideViewIcon.None,
QSTileState.EnabledState.ENABLED,
- Switch::class.qualifiedName
+ Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index c021caa598b9..0d2ebe42b7ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -49,7 +49,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
addOverride(R.drawable.qs_light_dark_theme_icon_on, TestStubDrawable())
}
.resources,
- context.theme
+ context.theme,
)
}
@@ -69,7 +69,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
expandedAccessibilityClass: KClass<out View>? = Switch::class,
): QSTileState {
return QSTileState(
- { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes,
label,
activationState,
@@ -79,7 +79,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
stateDescription,
sideViewIcon,
enabledState,
- expandedAccessibilityClass?.qualifiedName
+ expandedAccessibilityClass?.qualifiedName,
)
}
@@ -98,7 +98,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
createUiNightModeTileState(
activationState = QSTileState.ActivationState.UNAVAILABLE,
secondaryLabel = expectedSecondaryLabel,
- contentDescription = expectedContentDescription
+ contentDescription = expectedContentDescription,
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -118,7 +118,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
createUiNightModeTileState(
activationState = QSTileState.ActivationState.UNAVAILABLE,
secondaryLabel = expectedSecondaryLabel,
- contentDescription = expectedContentDescription
+ contentDescription = expectedContentDescription,
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -136,7 +136,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.INACTIVE,
label = expectedLabel,
secondaryLabel = expectedSecondaryLabel,
- contentDescription = expectedLabel
+ contentDescription = expectedLabel,
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -155,7 +155,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
label = expectedLabel,
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.ACTIVE,
- contentDescription = expectedLabel
+ contentDescription = expectedLabel,
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -174,7 +174,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
label = expectedLabel,
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.ACTIVE,
- contentDescription = expectedLabel
+ contentDescription = expectedLabel,
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -193,7 +193,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
label = expectedLabel,
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.INACTIVE,
- contentDescription = expectedLabel
+ contentDescription = expectedLabel,
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -214,7 +214,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.ACTIVE,
contentDescription = expectedLabel,
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -237,7 +237,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.UNAVAILABLE,
contentDescription = expectedContentDescription,
- supportedActions = setOf(QSTileState.UserAction.LONG_CLICK)
+ supportedActions = setOf(QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -258,7 +258,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.INACTIVE,
contentDescription = expectedLabel,
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -279,7 +279,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.UNAVAILABLE,
contentDescription = TextUtils.concat(expectedLabel, ", ", expectedSecondaryLabel),
- supportedActions = setOf(QSTileState.UserAction.LONG_CLICK)
+ supportedActions = setOf(QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -300,7 +300,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.UNAVAILABLE,
contentDescription = TextUtils.concat(expectedLabel, ", ", expectedSecondaryLabel),
- supportedActions = setOf(QSTileState.UserAction.LONG_CLICK)
+ supportedActions = setOf(QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -312,7 +312,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
nightMode = true,
powerSave = false,
isLocationEnabled = true,
- uiMode = UiModeManager.MODE_NIGHT_AUTO
+ uiMode = UiModeManager.MODE_NIGHT_AUTO,
)
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
@@ -328,7 +328,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.ACTIVE,
contentDescription = TextUtils.concat(expectedLabel, ", ", expectedSecondaryLabel),
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -340,7 +340,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
nightMode = false,
powerSave = false,
isLocationEnabled = true,
- uiMode = UiModeManager.MODE_NIGHT_AUTO
+ uiMode = UiModeManager.MODE_NIGHT_AUTO,
)
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
@@ -356,7 +356,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.INACTIVE,
contentDescription = TextUtils.concat(expectedLabel, ", ", expectedSecondaryLabel),
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -379,7 +379,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.ACTIVE,
contentDescription = expectedLabel,
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -401,7 +401,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.INACTIVE,
contentDescription = expectedLabel,
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -413,7 +413,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
nightMode = false,
powerSave = false,
uiMode = UiModeManager.MODE_NIGHT_CUSTOM,
- nighModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN
+ nighModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN,
)
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
@@ -428,7 +428,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.INACTIVE,
contentDescription = expectedLabel,
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -440,7 +440,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
nightMode = true,
powerSave = false,
uiMode = UiModeManager.MODE_NIGHT_CUSTOM,
- nighModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN
+ nighModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN,
)
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
@@ -455,7 +455,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
activationState = QSTileState.ActivationState.ACTIVE,
contentDescription = expectedLabel,
supportedActions =
- setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
@@ -467,7 +467,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
nightMode = false,
powerSave = true,
uiMode = UiModeManager.MODE_NIGHT_CUSTOM,
- nighModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN
+ nighModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN,
)
val actualState: QSTileState = mapper.map(qsTileConfig, inputModel)
@@ -484,7 +484,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
secondaryLabel = expectedSecondaryLabel,
activationState = QSTileState.ActivationState.UNAVAILABLE,
contentDescription = expectedContentDescription,
- supportedActions = setOf(QSTileState.UserAction.LONG_CLICK)
+ supportedActions = setOf(QSTileState.UserAction.LONG_CLICK),
)
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index e7bde681fe6f..86321ea04703 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -56,7 +56,7 @@ class WorkModeTileMapperTest : SysuiTestCase() {
whenever(
devicePolicyResourceManager.getString(
eq(DevicePolicyResources.Strings.SystemUi.QS_WORK_PROFILE_LABEL),
- any()
+ any(),
)
)
.thenReturn(testLabel)
@@ -66,12 +66,12 @@ class WorkModeTileMapperTest : SysuiTestCase() {
.apply {
addOverride(
com.android.internal.R.drawable.stat_sys_managed_profile_status,
- TestStubDrawable()
+ TestStubDrawable(),
)
}
.resources,
context.theme,
- devicePolicyManager
+ devicePolicyManager,
)
}
@@ -105,13 +105,11 @@ class WorkModeTileMapperTest : SysuiTestCase() {
QSTileStateSubject.assertThat(actualState).isEqualTo(expectedState)
}
- private fun createWorkModeTileState(
- activationState: QSTileState.ActivationState,
- ): QSTileState {
+ private fun createWorkModeTileState(activationState: QSTileState.ActivationState): QSTileState {
val label = testLabel
val iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
return QSTileState(
- icon = { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ icon = Icon.Loaded(context.getDrawable(iconRes)!!, null),
iconRes = iconRes,
label = label,
activationState = activationState,
@@ -134,7 +132,7 @@ class WorkModeTileMapperTest : SysuiTestCase() {
stateDescription = null,
sideViewIcon = QSTileState.SideViewIcon.None,
enabledState = QSTileState.EnabledState.ENABLED,
- expandedAccessibilityClassName = Switch::class.qualifiedName
+ expandedAccessibilityClassName = Switch::class.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
index c33e2a49ef5d..954215eede0d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -184,10 +184,7 @@ class QSTileViewModelTest : SysuiTestCase() {
{
object : QSTileDataToStateMapper<String> {
override fun map(config: QSTileConfig, data: String): QSTileState =
- QSTileState.build(
- { Icon.Resource(0, ContentDescription.Resource(0)) },
- data
- ) {}
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), data) {}
}
},
disabledByPolicyInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
index 7955f2fc1335..0219a4c58dcf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
@@ -104,7 +104,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
eq(tileConfig.tileSpec),
eq(userAction),
any(),
- eq("initial_data")
+ eq("initial_data"),
)
verify(qsTileAnalytics).trackUserAction(eq(tileConfig), eq(userAction))
}
@@ -130,7 +130,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
.logUserActionRejectedByPolicy(
eq(userAction),
eq(tileConfig.tileSpec),
- eq(DISABLED_RESTRICTION)
+ eq(DISABLED_RESTRICTION),
)
verify(qsTileAnalytics, never()).trackUserAction(any(), any())
}
@@ -159,7 +159,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
.logUserActionRejectedByPolicy(
eq(userAction),
eq(tileConfig.tileSpec),
- eq(DISABLED_RESTRICTION)
+ eq(DISABLED_RESTRICTION),
)
verify(qsTileAnalytics, never()).trackUserAction(any(), any())
}
@@ -174,7 +174,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
QSTilePolicy.Restricted(
listOf(
DISABLED_RESTRICTION,
- FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2
+ FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2,
)
)
}
@@ -194,13 +194,13 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
.logUserActionRejectedByPolicy(
eq(userAction),
eq(tileConfig.tileSpec),
- eq(DISABLED_RESTRICTION)
+ eq(DISABLED_RESTRICTION),
)
verify(qsTileLogger, never())
.logUserActionRejectedByPolicy(
eq(userAction),
eq(tileConfig.tileSpec),
- eq(FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2)
+ eq(FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2),
)
verify(qsTileAnalytics, never()).trackUserAction(any(), any())
}
@@ -243,10 +243,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
{
object : QSTileDataToStateMapper<String> {
override fun map(config: QSTileConfig, data: String): QSTileState =
- QSTileState.build(
- { Icon.Resource(0, ContentDescription.Resource(0)) },
- data
- ) {}
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), data) {}
}
},
disabledByPolicyInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index 22913f12330f..8769022f3aa8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -26,7 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.dump.DumpManager
@@ -101,7 +101,7 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
private val fakeConfigurationRepository =
FakeConfigurationRepository().apply { onConfigurationChange(configuration) }
- private val configurationInteractor = ConfigurationInteractor(fakeConfigurationRepository)
+ private val configurationInteractor = ConfigurationInteractorImpl(fakeConfigurationRepository)
private val mockAsyncLayoutInflater =
mock<AsyncLayoutInflater>() {
@@ -151,10 +151,7 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
inOrder.verify(qsImpl!!).onCreate(nullable())
inOrder
.verify(qsImpl!!)
- .onComponentCreated(
- eq(qsSceneComponentFactory.components[0]),
- any(),
- )
+ .onComponentCreated(eq(qsSceneComponentFactory.components[0]), any())
}
@Test
@@ -422,10 +419,7 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
inOrder.verify(newQSImpl).onCreate(nullable())
inOrder
.verify(newQSImpl)
- .onComponentCreated(
- qsSceneComponentFactory.components[1],
- bundleArgCaptor.value,
- )
+ .onComponentCreated(qsSceneComponentFactory.components[1], bundleArgCaptor.value)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index c3a777cc3e44..939644594d31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -89,12 +88,8 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
assertThat(
- (actions?.get(
- Swipe(
- direction = SwipeDirection.Down,
- fromSource = SceneContainerEdge.TopLeft,
- )
- ) as? UserActionResult.ReplaceByOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft))
+ as? UserActionResult.ReplaceByOverlay)
?.overlay
)
.isEqualTo(Overlays.NotificationsShade)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
index f32894dfd383..24f6b6d8566b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
@@ -55,7 +56,10 @@ import org.junit.runner.RunWith
@EnableFlags(DualShade.FLAG_NAME)
class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply {
+ usingMediaInComposeFragment = false // This is not for the compose fragment
+ }
private val testScope = kosmos.testScope
private val sceneInteractor = kosmos.sceneInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
index 62b6391ca54c..d5fbe49a153b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
@@ -22,7 +22,6 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -98,9 +97,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Gone)
@@ -125,9 +123,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Lockscreen),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Lockscreen),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Lockscreen),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
@@ -154,9 +151,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Gone)
@@ -178,9 +174,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
@@ -214,9 +209,8 @@ class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
- Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up) to
- UserActionResult(SceneFamilies.Home),
+ Swipe.Up to UserActionResult(Scenes.Shade),
+ Swipe.Up(fromSource = Edge.Bottom) to UserActionResult(SceneFamilies.Home),
)
)
assertThat(homeScene).isEqualTo(Scenes.Gone)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 319f1e577cd9..b5f005cdc706 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.scene
+import android.provider.Settings
import android.telephony.TelephonyManager
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -42,8 +43,10 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
@@ -63,6 +66,7 @@ import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
import com.android.telecom.mockTelecomManager
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -71,6 +75,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Test
@@ -153,7 +158,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() =
kosmos.runTest {
- val actions by testScope.collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
@@ -170,7 +175,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
kosmos.runTest {
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
@@ -180,7 +185,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
kosmos.runTest {
- val actions by testScope.collectLastValue(shadeUserActionsViewModel.actions)
+ val actions by collectLastValue(shadeUserActionsViewModel.actions)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
assertCurrentScene(Scenes.Lockscreen)
@@ -197,8 +202,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
@Test
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
kosmos.runTest {
- val actions by testScope.collectLastValue(shadeUserActionsViewModel.actions)
- val canSwipeToEnter by testScope.collectLastValue(deviceEntryInteractor.canSwipeToEnter)
+ val actions by collectLastValue(shadeUserActionsViewModel.actions)
+ val canSwipeToEnter by collectLastValue(deviceEntryInteractor.canSwipeToEnter)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
@@ -279,7 +284,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
fun swipeUpOnLockscreenWhileUnlocked_dismissesLockscreen() =
kosmos.runTest {
unlockDevice()
- val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
@@ -302,7 +307,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
kosmos.runTest {
setAuthMethod(AuthenticationMethodModel.Password)
- val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
@@ -319,14 +324,13 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
kosmos.runTest {
setAuthMethod(AuthenticationMethodModel.Password)
- val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(to = upDestinationSceneKey)
- val bouncerActionButton by
- testScope.collectLastValue(bouncerSceneContentViewModel.actionButton)
+ val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible")
.that(bouncerActionButton)
.isNotNull()
@@ -341,14 +345,13 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
kosmos.runTest {
setAuthMethod(AuthenticationMethodModel.Password)
startPhoneCall()
- val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
+ val actions by collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(to = upDestinationSceneKey)
- val bouncerActionButton by
- testScope.collectLastValue(bouncerSceneContentViewModel.actionButton)
+ val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible during call")
.that(bouncerActionButton)
.isNotNull()
@@ -542,7 +545,14 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.isTrue()
powerInteractor.setAsleepForTest()
- testScope.runCurrent()
+ testScope.advanceTimeBy(
+ kosmos.userAwareSecureSettingsRepository
+ .getInt(
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT,
+ )
+ .toLong()
+ )
powerInteractor.setAwakeForTest()
testScope.runCurrent()
@@ -574,7 +584,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.that(getCurrentSceneInUi())
.isEqualTo(Scenes.Bouncer)
val authMethodViewModel by
- testScope.collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
+ collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -603,7 +613,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
.that(getCurrentSceneInUi())
.isEqualTo(Scenes.Bouncer)
val authMethodViewModel by
- testScope.collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
+ collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -632,14 +642,25 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
}
/** Changes device wakefulness state from awake to asleep, going through intermediary states. */
- private fun Kosmos.putDeviceToSleep() {
+ private suspend fun Kosmos.putDeviceToSleep(waitForLock: Boolean = true) {
val wakefulnessModel = powerInteractor.detailedWakefulness.value
assertWithMessage("Cannot put device to sleep as it's already asleep!")
.that(wakefulnessModel.isAwake())
.isTrue()
powerInteractor.setAsleepForTest()
- testScope.runCurrent()
+ if (waitForLock) {
+ testScope.advanceTimeBy(
+ kosmos.userAwareSecureSettingsRepository
+ .getInt(
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT,
+ )
+ .toLong()
+ )
+ } else {
+ testScope.runCurrent()
+ }
}
/** Emulates the dismissal of the IME (soft keyboard). */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index bfe5ef7acbce..db2297c99315 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -32,7 +32,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.statusbar.NotificationPresenter
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.init.NotificationsController
@@ -69,7 +69,7 @@ class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
private val notificationPresenter = mock<NotificationPresenter>()
private val notificationsController = mock<NotificationsController>()
private val powerInteractor = PowerInteractorFactory.create().powerInteractor
- private val activeNotificationsRepository = ActiveNotificationListRepository()
+ private val activeNotificationsRepository = kosmos.activeNotificationListRepository
private val activeNotificationsInteractor =
ActiveNotificationsInteractor(activeNotificationsRepository, testDispatcher)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 2c8f7cf47723..af0274b1caaa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -23,6 +23,7 @@ import android.hardware.face.FaceManager
import android.os.PowerManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -43,6 +44,7 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
@@ -53,12 +55,14 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
+import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -106,11 +110,13 @@ import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvision
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -753,7 +759,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
lastSleepReason = WakeSleepReason.POWER_BUTTON,
powerButtonLaunchGestureTriggered = false,
)
- transitionStateFlow.value = Transition(from = Scenes.Shade, to = Scenes.Lockscreen)
+ transitionStateFlow.value = Transition(from = Scenes.Gone, to = Scenes.Lockscreen)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
kosmos.fakePowerRepository.updateWakefulness(
@@ -1339,7 +1345,14 @@ class SceneContainerStartableTest : SysuiTestCase() {
// Putting the device to sleep to lock it again, which shouldn't report another
// successful unlock.
kosmos.powerInteractor.setAsleepForTest()
- runCurrent()
+ advanceTimeBy(
+ kosmos.userAwareSecureSettingsRepository
+ .getInt(
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT,
+ )
+ .toLong()
+ )
// Verify that the startable changed the scene to Lockscreen because the device locked
// following the sleep.
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -2119,40 +2132,6 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
- fun switchToGone_whenSurfaceBehindLockscreenVisibleMidTransition() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- val transitionStateFlow =
- prepareState(authenticationMethod = AuthenticationMethodModel.None)
- underTest.start()
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- // Swipe to Gone, more than halfway
- transitionStateFlow.value =
- ObservableTransitionState.Transition(
- fromScene = Scenes.Lockscreen,
- toScene = Scenes.Gone,
- currentScene = flowOf(Scenes.Gone),
- progress = flowOf(0.51f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(true),
- )
- runCurrent()
- // Lift finger
- transitionStateFlow.value =
- ObservableTransitionState.Transition(
- fromScene = Scenes.Lockscreen,
- toScene = Scenes.Gone,
- currentScene = flowOf(Scenes.Gone),
- progress = flowOf(0.51f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
- runCurrent()
-
- assertThat(currentScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
fun switchToGone_extendUnlock() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
@@ -2380,6 +2359,58 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(isLockscreenEnabled).isTrue()
}
+ @Test
+ fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() =
+ testScope.runTest {
+ val transitionState =
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ )
+ underTest.start()
+
+ val isUnlocked by
+ collectLastValue(
+ kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+ )
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val backStack by collectLastValue(sceneBackInteractor.backStack)
+ val isAlternateBouncerVisible by
+ collectLastValue(kosmos.alternateBouncerInteractor.isVisible)
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Change to shade.
+ sceneInteractor.changeScene(Scenes.Shade, "")
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Show the alternate bouncer.
+ kosmos.alternateBouncerInteractor.forceShow()
+ kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isTrue()
+
+ // Trigger a fingerprint unlock.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ assertThat(isUnlocked).isTrue()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone)
+ assertThat(isAlternateBouncerVisible).isFalse()
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
index 47fae9f0629f..bb2e9417ecef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -23,7 +23,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
@@ -71,8 +70,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey)
- .isEqualTo(ToSplitShade)
+ assertThat(userActions?.get(Swipe.Down)?.transitionKey).isEqualTo(ToSplitShade)
}
@Test
@@ -83,7 +81,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(false)
runCurrent()
- assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+ assertThat(userActions?.get(Swipe.Down)?.transitionKey).isNull()
}
@Test
@@ -94,7 +92,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+ assertThat(userActions?.get(Swipe.Down)?.transitionKey).isNull()
}
@Test
@@ -132,6 +130,6 @@ class GoneUserActionsViewModelTest : SysuiTestCase() {
}
private fun swipeDownFromTopWithTwoFingers(): UserAction {
- return Swipe(direction = SwipeDirection.Down, pointerCount = 2, fromSource = Edge.Top)
+ return Swipe.Down(pointerCount = 2, fromSource = Edge.Top)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
index 972afb58352d..d5f57da40e5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
@@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
@@ -77,7 +76,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val expected1 =
mapOf(
Back to UserActionResult(toScene = Scenes.Gone),
- Swipe(SwipeDirection.Up) to UserActionResult(toScene = Scenes.Shade)
+ Swipe.Up to UserActionResult(toScene = Scenes.Shade),
)
underTest.upstream.value = expected1
runCurrent()
@@ -86,7 +85,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val expected2 =
mapOf(
Back to UserActionResult(toScene = Scenes.Lockscreen),
- Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
+ Swipe.Down to UserActionResult(toScene = Scenes.Shade),
)
underTest.upstream.value = expected2
runCurrent()
@@ -104,7 +103,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val expected =
mapOf(
Back to UserActionResult(toScene = Scenes.Lockscreen),
- Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
+ Swipe.Down to UserActionResult(toScene = Scenes.Shade),
)
underTest.upstream.value = expected
runCurrent()
@@ -120,7 +119,7 @@ class UserActionsViewModelTest : SysuiTestCase() {
val upstream = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
override suspend fun hydrateActions(
- setActions: (Map<UserAction, UserActionResult>) -> Unit,
+ setActions: (Map<UserAction, UserActionResult>) -> Unit
) {
upstream.collect { setActions(it) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
index 4d71dc45001d..4871564a5376 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
@@ -18,6 +18,8 @@ package com.android.systemui.screenshot.data.model
import android.content.ComponentName
import android.graphics.Rect
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREEFORM_FULL_SCREEN
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREEFORM_MAXIMIZED
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREE_FORM
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.PIP
@@ -153,11 +155,23 @@ object DisplayContentScenarios {
fun freeFormApps(
vararg tasks: TaskSpec,
focusedTaskId: Int,
+ maximizedTaskId: Int = -1,
shadeExpanded: Boolean = false,
): DisplayContentModel {
val freeFormTasks =
tasks
- .map { freeForm(it) }
+ .map {
+ freeForm(
+ task = it,
+ bounds =
+ if (it.taskId == maximizedTaskId) {
+ FREEFORM_MAXIMIZED
+ } else {
+ FREE_FORM
+ },
+ maxBounds = FREEFORM_FULL_SCREEN,
+ )
+ }
// Root tasks are ordered top-down in List<RootTaskInfo>.
// Sort 'focusedTaskId' last (Boolean natural ordering: [false, true])
.sortedBy { it.childTaskIds[0] != focusedTaskId }
@@ -180,9 +194,9 @@ object DisplayContentScenarios {
val PIP = Rect(440, 1458, 1038, 1794)
val SPLIT_TOP = Rect(0, 0, 1080, 1187)
val SPLIT_BOTTOM = Rect(0, 1213, 1080, 2400)
- val FREE_FORM = Rect(119, 332, 1000, 1367)
// "Tablet" size
+ val FREE_FORM = Rect(119, 332, 1000, 1367)
val FREEFORM_FULL_SCREEN = Rect(0, 0, 2560, 1600)
val FREEFORM_MAXIMIZED = Rect(0, 48, 2560, 1480)
val FREEFORM_SPLIT_LEFT = Rect(0, 0, 1270, 1600)
@@ -301,11 +315,12 @@ object DisplayContentScenarios {
}
/** An activity in FreeForm mode */
- fun freeForm(task: TaskSpec, bounds: Rect = FREE_FORM) =
+ fun freeForm(task: TaskSpec, bounds: Rect = FREE_FORM, maxBounds: Rect = bounds) =
newRootTaskInfo(
taskId = task.taskId,
userId = task.userId,
bounds = bounds,
+ maxBounds = maxBounds,
windowingMode = WindowingMode.Freeform,
topActivity = ComponentName.unflattenFromString(task.name),
) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
index cedf0c8a2c06..4f6871ebbbf4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
@@ -84,6 +84,7 @@ fun newRootTaskInfo(
activityType: ActivityType = Standard,
windowingMode: WindowingMode = FullScreen,
bounds: Rect = Rect(),
+ maxBounds: Rect = bounds,
topActivity: ComponentName? = null,
topActivityType: ActivityType = Standard,
numActivities: Int? = null,
@@ -94,6 +95,7 @@ fun newRootTaskInfo(
setWindowingMode(windowingMode.toInt())
setActivityType(activityType.toInt())
setBounds(bounds)
+ setMaxBounds(maxBounds)
}
this.bounds = bounds
this.displayId = displayId
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
index b7f565df4a3c..c884b9a9ac54 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
@@ -90,9 +90,11 @@ class PrivateProfilePolicyTest {
Matched(
PrivateProfilePolicy.NAME,
PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
- CaptureParameters(
+ LegacyCaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
+ component =
+ ComponentName.unflattenFromString(YOUTUBE)
+ ?: error("Invalid component name"),
owner = UserHandle.of(PRIVATE),
),
)
@@ -142,7 +144,7 @@ class PrivateProfilePolicyTest {
Matched(
PrivateProfilePolicy.NAME,
PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
- CaptureParameters(
+ LegacyCaptureParameters(
type = FullScreen(displayId = 0),
component = ComponentName.unflattenFromString(YOUTUBE),
owner = UserHandle.of(PRIVATE),
@@ -167,7 +169,7 @@ class PrivateProfilePolicyTest {
Matched(
PrivateProfilePolicy.NAME,
PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
- CaptureParameters(
+ LegacyCaptureParameters(
type = FullScreen(displayId = 0),
component = ComponentName.unflattenFromString(FILES),
owner = UserHandle.of(PRIVATE),
@@ -188,7 +190,7 @@ class PrivateProfilePolicyTest {
Matched(
PrivateProfilePolicy.NAME,
PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
- CaptureParameters(
+ LegacyCaptureParameters(
type = FullScreen(displayId = 0),
component = ComponentName.unflattenFromString(YOUTUBE_PIP),
owner = UserHandle.of(PRIVATE),
@@ -212,7 +214,7 @@ class PrivateProfilePolicyTest {
Matched(
PrivateProfilePolicy.NAME,
PrivateProfilePolicy.PRIVATE_TASK_VISIBLE,
- CaptureParameters(
+ LegacyCaptureParameters(
type = FullScreen(displayId = 0),
component = ComponentName.unflattenFromString(YOUTUBE_PIP),
owner = UserHandle.of(PRIVATE),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt
index 28eb9fc1364b..948c24ec8aa2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt
@@ -17,11 +17,11 @@
package com.android.systemui.screenshot.policy
import android.content.ComponentName
+import android.graphics.Rect
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
-import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.LAUNCHER
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.MESSAGES
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.YOUTUBE
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREEFORM_FULL_SCREEN
@@ -32,10 +32,10 @@ import com.android.systemui.screenshot.data.model.DisplayContentScenarios.freeFo
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.pictureInPictureApp
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.splitScreenApps
+import com.android.systemui.screenshot.data.model.allTasks
import com.android.systemui.screenshot.data.repository.profileTypeRepository
import com.android.systemui.screenshot.policy.CaptureType.FullScreen
import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
-import com.android.systemui.screenshot.policy.CaptureType.RootTask
import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL
import com.android.systemui.screenshot.policy.TestUserIds.PRIVATE
import com.android.systemui.screenshot.policy.TestUserIds.WORK
@@ -50,69 +50,81 @@ class ScreenshotPolicyTest {
private val defaultComponent = ComponentName("default", "default")
private val defaultOwner = UserHandle.SYSTEM
+ private val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
@Test
fun fullScreen_work() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+ val displayContent = singleFullScreen(TaskSpec(taskId = 1002, name = FILES, userId = WORK))
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
- val result =
- policy.apply(
- singleFullScreen(TaskSpec(taskId = 1002, name = FILES, userId = WORK)),
- defaultComponent,
- defaultOwner,
- )
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN),
- component = ComponentName.unflattenFromString(FILES),
- owner = UserHandle.of(WORK),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
+ owner = UserHandle.of(expectedFocusedTask.userId),
)
)
}
@Test
fun fullScreen_private() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+ val displayContent =
+ singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE))
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
- val result =
- policy.apply(
- singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE)),
- defaultComponent,
- defaultOwner,
- )
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
- owner = UserHandle.of(PRIVATE),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
+ owner = UserHandle.of(expectedFocusedTask.userId),
)
)
}
@Test
fun splitScreen_workAndPersonal() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- splitScreenApps(
- first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
- focusedTaskId = 1002,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ splitScreenApps(
+ first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+ focusedTaskId = 1002,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = UserHandle.of(PERSONAL),
)
)
@@ -120,24 +132,28 @@ class ScreenshotPolicyTest {
@Test
fun splitScreen_personalAndPrivate() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- splitScreenApps(
- first = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
- second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
- focusedTaskId = 1002,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ splitScreenApps(
+ first = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+ second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+ focusedTaskId = 1002,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = UserHandle.of(PRIVATE),
)
)
@@ -145,24 +161,28 @@ class ScreenshotPolicyTest {
@Test
fun splitScreen_workAndPrivate() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- splitScreenApps(
- first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
- focusedTaskId = 1002,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ splitScreenApps(
+ first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+ focusedTaskId = 1002,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = UserHandle.of(PRIVATE),
)
)
@@ -170,32 +190,31 @@ class ScreenshotPolicyTest {
@Test
fun splitScreen_twoWorkTasks() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- splitScreenApps(
- parentTaskId = 1,
- parentBounds = FREEFORM_FULL_SCREEN,
- orientation = VERTICAL,
- first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = WORK),
- focusedTaskId = 1002,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ splitScreenApps(
+ parentTaskId = 1,
+ parentBounds = FREEFORM_FULL_SCREEN,
+ orientation = VERTICAL,
+ first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = WORK),
+ focusedTaskId = 1002,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
- type =
- RootTask(
- parentTaskId = 1,
- taskBounds = FREEFORM_FULL_SCREEN,
- childTaskIds = listOf(1002, 1003),
+ type = IsolatedTask(taskBounds = FREEFORM_FULL_SCREEN, taskId = 1),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
),
- component = ComponentName.unflattenFromString(FILES),
owner = UserHandle.of(WORK),
)
)
@@ -203,99 +222,112 @@ class ScreenshotPolicyTest {
@Test
fun freeform_floatingWindows() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- freeFormApps(
- TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
- focusedTaskId = 1003,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ freeFormApps(
+ TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+ focusedTaskId = 1003,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1003 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = UserHandle.of(PERSONAL),
)
)
}
@Test
- fun freeform_floatingWindows_maximized() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- freeFormApps(
- TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
- focusedTaskId = 1003,
- ),
- defaultComponent,
- defaultOwner,
+ fun freeform_floatingWindows_work_maximized() = runTest {
+ val displayContent =
+ freeFormApps(
+ TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+ focusedTaskId = 1002,
+ maximizedTaskId = 1002,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
- type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
- owner = UserHandle.of(PERSONAL),
+ type = IsolatedTask(taskId = 1002, taskBounds = expectedFocusedTask.bounds),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
+ owner = UserHandle.of(WORK),
)
)
}
@Test
fun freeform_floatingWindows_withPrivate() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- freeFormApps(
- TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
- TaskSpec(taskId = 1004, name = MESSAGES, userId = PERSONAL),
- focusedTaskId = 1004,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ freeFormApps(
+ TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+ TaskSpec(taskId = 1004, name = MESSAGES, userId = PERSONAL),
+ focusedTaskId = 1004,
)
+ val expectedFocusedTask = displayContent.allTasks().single { it.id == 1004 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(YOUTUBE),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = UserHandle.of(PRIVATE),
)
)
}
@Test
- fun freeform_floating_workOnly() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- freeFormApps(
- TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- focusedTaskId = 1002,
- ),
- defaultComponent,
- defaultOwner,
- )
+ fun freeform_floating_work() = runTest {
+ val displayContent =
+ freeFormApps(TaskSpec(taskId = 1002, name = FILES, userId = WORK), focusedTaskId = 1002)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = ComponentName.unflattenFromString(LAUNCHER),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = defaultOwner,
)
)
@@ -303,23 +335,27 @@ class ScreenshotPolicyTest {
@Test
fun fullScreen_shadeExpanded() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- singleFullScreen(
- TaskSpec(taskId = 1002, name = FILES, userId = WORK),
- shadeExpanded = true,
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ singleFullScreen(
+ TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+ shadeExpanded = true,
)
+ val expectedFocusedTask =
+ displayContent.rootTasks.first().childTasksTopDown().single { it.id == 1002 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = FullScreen(displayId = 0),
- component = defaultComponent,
+ contentTask =
+ TaskReference(
+ taskId = -1,
+ component = defaultComponent,
+ owner = defaultOwner,
+ bounds = Rect(),
+ ),
owner = defaultOwner,
)
)
@@ -327,25 +363,55 @@ class ScreenshotPolicyTest {
@Test
fun fullScreen_with_PictureInPicture() = runTest {
- val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
-
- val result =
- policy.apply(
- pictureInPictureApp(
- pip = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
- fullScreen = TaskSpec(taskId = 1003, name = FILES, userId = WORK),
- ),
- defaultComponent,
- defaultOwner,
+ val displayContent =
+ pictureInPictureApp(
+ pip = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
+ fullScreen = TaskSpec(taskId = 1003, name = FILES, userId = WORK),
)
+ val expectedFocusedTask = displayContent.allTasks().single { it.id == 1003 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
assertThat(result)
.isEqualTo(
CaptureParameters(
type = IsolatedTask(taskId = 1003, taskBounds = FULL_SCREEN),
- component = ComponentName.unflattenFromString(FILES),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
owner = UserHandle.of(WORK),
)
)
}
+
+ // TODO: PiP tasks should affect ownership (e.g. Private)
+ @Test
+ fun fullScreen_with_PictureInPicture_private() = runTest {
+ val displayContent =
+ pictureInPictureApp(
+ pip = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE),
+ fullScreen = TaskSpec(taskId = 1003, name = FILES, userId = PERSONAL),
+ )
+ val expectedFocusedTask = displayContent.allTasks().single { it.id == 1003 }
+
+ val result = policy.apply(displayContent, defaultComponent, defaultOwner)
+ assertThat(result)
+ .isEqualTo(
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ contentTask =
+ TaskReference(
+ taskId = expectedFocusedTask.id,
+ component = expectedFocusedTask.componentName,
+ owner = UserHandle.of(expectedFocusedTask.userId),
+ bounds = expectedFocusedTask.bounds,
+ ),
+ owner = UserHandle.of(PRIVATE),
+ )
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
index 30a786c291cb..c1477fe52f0e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -135,7 +135,7 @@ class WorkProfilePolicyTest {
PolicyResult.Matched(
policy = WorkProfilePolicy.NAME,
reason = WORK_TASK_IS_TOP,
- CaptureParameters(
+ LegacyCaptureParameters(
type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN),
component = ComponentName.unflattenFromString(FILES),
owner = UserHandle.of(WORK),
@@ -162,7 +162,7 @@ class WorkProfilePolicyTest {
PolicyResult.Matched(
policy = WorkProfilePolicy.NAME,
reason = WORK_TASK_IS_TOP,
- CaptureParameters(
+ LegacyCaptureParameters(
type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN.splitTop(20)),
component = ComponentName.unflattenFromString(FILES),
owner = UserHandle.of(WORK),
@@ -200,7 +200,7 @@ class WorkProfilePolicyTest {
PolicyResult.Matched(
policy = WorkProfilePolicy.NAME,
reason = WORK_TASK_IS_TOP,
- CaptureParameters(
+ LegacyCaptureParameters(
type = IsolatedTask(taskId = 1003, taskBounds = FULL_SCREEN),
component = ComponentName.unflattenFromString(FILES),
owner = UserHandle.of(WORK),
@@ -226,7 +226,7 @@ class WorkProfilePolicyTest {
PolicyResult.Matched(
policy = WorkProfilePolicy.NAME,
reason = WORK_TASK_IS_TOP,
- CaptureParameters(
+ LegacyCaptureParameters(
type = IsolatedTask(taskId = 1003, taskBounds = FREE_FORM),
component = ComponentName.unflattenFromString(FILES),
owner = UserHandle.of(WORK),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
index 41e2467f798b..ae7719bca2a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
@@ -16,13 +16,19 @@
package com.android.systemui.settings.brightness
+import android.hardware.display.BrightnessInfo
import android.hardware.display.DisplayManager
import android.os.Handler
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.vr.IVrManager
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
import com.android.systemui.settings.DisplayTracker
@@ -30,13 +36,16 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -44,7 +53,8 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class BrightnessControllerTest : SysuiTestCase() {
-
+ @get:Rule
+ public val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private val executor = FakeExecutor(FakeSystemClock())
private val secureSettings = FakeSettings()
@Mock private lateinit var toggleSlider: ToggleSlider
@@ -53,6 +63,7 @@ class BrightnessControllerTest : SysuiTestCase() {
@Mock private lateinit var displayManager: DisplayManager
@Mock private lateinit var iVrManager: IVrManager
@Mock private lateinit var logger: LogBuffer
+ @Mock private lateinit var display: Display
private lateinit var testableLooper: TestableLooper
@@ -63,9 +74,11 @@ class BrightnessControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ val contextSpy = spy(context)
+ whenever(contextSpy.getDisplay()).thenReturn(display)
underTest =
BrightnessController(
- context,
+ contextSpy,
toggleSlider,
userTracker,
displayTracker,
@@ -105,4 +118,21 @@ class BrightnessControllerTest : SysuiTestCase() {
assertThat(messagesProcessed).isEqualTo(1)
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SHOW_TOAST_WHEN_APP_CONTROL_BRIGHTNESS)
+ fun testOnChange_showToastWhenAppOverridesBrightness() {
+ val brightnessInfo = BrightnessInfo(
+ 0.45f, 0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ 1.0f /* highBrightnessTransitionPoint */,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
+ true /* isBrightnessOverrideByWindow */
+ )
+ whenever(display.brightnessInfo).thenReturn(brightnessInfo)
+ underTest.registerCallbacks()
+ testableLooper.processAllMessages()
+
+ underTest.onChanged(true /* tracking */, 100 /* value */, false /* stopTracking */)
+ verify(toggleSlider).showToast(any())
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 637a12c11690..3697c3151333 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.haptics.slider.HapticSlider
import com.android.systemui.haptics.slider.HapticSliderPlugin
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.BrightnessMirrorController
import com.android.systemui.util.mockito.any
@@ -64,6 +65,7 @@ class BrightnessSliderControllerTest : SysuiTestCase() {
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var msdlPlayer: MSDLPlayer
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var brightnessWarningToast: BrightnessWarningToast
@Captor
private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
@@ -94,6 +96,7 @@ class BrightnessSliderControllerTest : SysuiTestCase() {
HapticSlider.SeekBar(seekBar),
),
activityStarter,
+ brightnessWarningToast,
)
mController.init()
mController.setOnChangedListener(listener)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 59d0d70f77d4..041d1a611b55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -238,7 +238,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
primaryBouncerInteractor,
alternateBouncerInteractor,
mock(BouncerViewBinder::class.java),
- mock(ConfigurationForwarder::class.java),
+ { mock(ConfigurationForwarder::class.java) },
brightnessMirrorShowingInteractor,
)
underTest.setupExpandedStatusBar()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 9b91fc7b4558..5d1ce7c5ca05 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -203,7 +203,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
primaryBouncerInteractor,
alternateBouncerInteractor,
mock(),
- configurationForwarder,
+ { configurationForwarder },
brightnessMirrorShowingInteractor,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 2e759a363e20..443595dbdf77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.shade;
import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
@@ -63,8 +62,6 @@ import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -159,8 +156,6 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
protected SysuiStatusBarStateController mStatusBarStateController;
protected ShadeInteractor mShadeInteractor;
- protected ActiveNotificationsInteractor mActiveNotificationsInteractor;
-
protected Handler mMainHandler;
protected LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
@@ -204,11 +199,6 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
),
mKosmos.getShadeModeInteractor());
- mActiveNotificationsInteractor = new ActiveNotificationsInteractor(
- new ActiveNotificationListRepository(),
- StandardTestDispatcher(/* scheduler = */ null, /* name = */ null)
- );
-
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -277,7 +267,7 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
mock(DeviceEntryFaceAuthInteractor.class),
mShadeRepository,
mShadeInteractor,
- mActiveNotificationsInteractor,
+ mKosmos.getActiveNotificationsInteractor(),
new JavaAdapter(mTestScope.getBackgroundScope()),
mCastController,
splitShadeStateController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 943fb62003cb..0f476d0a0455 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -35,8 +35,7 @@ import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInte
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -50,7 +49,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
-import kotlinx.coroutines.test.StandardTestDispatcher
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,8 +63,6 @@ import org.mockito.MockitoAnnotations
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
private val executor = FakeExecutor(FakeSystemClock())
- private val testDispatcher = StandardTestDispatcher()
- private val activeNotificationsRepository = ActiveNotificationListRepository()
private val kosmos = Kosmos()
private val testScope = kosmos.testScope
@@ -95,7 +91,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
FakeKeyguardRepository(),
headsUpManager,
PowerInteractorFactory.create().powerInteractor,
- ActiveNotificationsInteractor(activeNotificationsRepository, testDispatcher),
+ kosmos.activeNotificationsInteractor,
kosmos::sceneInteractor,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt
new file mode 100644
index 000000000000..a9a5cac6112e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.data.repository
+
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadePositionRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val commandRegistry = kosmos.commandRegistry
+ private val pw = PrintWriter(StringWriter())
+
+ private val underTest = ShadePositionRepositoryImpl(commandRegistry)
+
+ @Before
+ fun setUp() {
+ underTest.start()
+ }
+
+ @Test
+ fun commandDisplayOverride_updatesDisplayId() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+ val newDisplayId = 2
+ commandRegistry.onShellCommand(
+ pw,
+ arrayOf("shade_display_override", newDisplayId.toString()),
+ )
+
+ assertThat(displayId).isEqualTo(newDisplayId)
+ }
+
+ @Test
+ fun commandShadeDisplayOverride_resetsDisplayId() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+
+ val newDisplayId = 2
+ commandRegistry.onShellCommand(
+ pw,
+ arrayOf("shade_display_override", newDisplayId.toString()),
+ )
+ assertThat(displayId).isEqualTo(newDisplayId)
+
+ commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "reset"))
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
index fcb366bfd8ee..bbfc66a8d8e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
@@ -92,10 +92,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
AuthenticationMethodModel.Pin
)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
}
@@ -110,10 +107,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
)
setDeviceEntered(true)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
@@ -128,10 +122,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
)
kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
@@ -147,10 +138,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
)
sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
}
@@ -167,10 +155,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
runCurrent()
sceneInteractor.changeScene(Scenes.Gone, "reason")
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(SceneFamilies.Home)
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
@@ -182,8 +167,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(true)
runCurrent()
- assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey)
- .isEqualTo(ToSplitShade)
+ assertThat(actions?.get(Swipe.Up)?.transitionKey).isEqualTo(ToSplitShade)
}
@Test
@@ -193,7 +177,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
shadeRepository.setShadeLayoutWide(false)
runCurrent()
- assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey).isNull()
+ assertThat(actions?.get(Swipe.Up)?.transitionKey).isNull()
}
@Test
@@ -202,10 +186,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, true)
kosmos.shadeStartable.start()
val actions by collectLastValue(underTest.actions)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
.isNull()
}
@@ -215,10 +196,7 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, false)
kosmos.shadeStartable.start()
val actions by collectLastValue(underTest.actions)
- assertThat(
- (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene)
- ?.toScene
- )
+ assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
.isEqualTo(Scenes.QuickSettings)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
index ce9b3beeec9e..7842d75db6c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.withArgCaptor
@@ -52,6 +53,10 @@ import org.mockito.kotlin.never
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class CommunalSmartspaceControllerTest : SysuiTestCase() {
+ @Mock private lateinit var userTracker: UserTracker
+
+ @Mock private lateinit var userContextPrimary: Context
+
@Mock private lateinit var smartspaceManager: SmartspaceManager
@Mock private lateinit var execution: Execution
@@ -113,15 +118,18 @@ class CommunalSmartspaceControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session)
+ `when`(userTracker.userContext).thenReturn(userContextPrimary)
+ `when`(userContextPrimary.getSystemService(SmartspaceManager::class.java))
+ .thenReturn(smartspaceManager)
+
controller =
CommunalSmartspaceController(
- context,
- smartspaceManager,
+ userTracker,
execution,
uiExecutor,
precondition,
Optional.of(targetFilter),
- Optional.of(plugin)
+ Optional.of(plugin),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index e774aed2045b..c83c82dc0914 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -33,6 +33,7 @@ import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.dagger.SmartspaceViewComponent
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.mockito.any
@@ -46,66 +47,51 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.anyInt
import org.mockito.MockitoAnnotations
-import org.mockito.Spy
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class DreamSmartspaceControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var smartspaceManager: SmartspaceManager
+ @Mock private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var execution: Execution
+ @Mock private lateinit var userContextPrimary: Context
- @Mock
- private lateinit var uiExecutor: Executor
+ @Mock private lateinit var smartspaceManager: SmartspaceManager
- @Mock
- private lateinit var viewComponentFactory: SmartspaceViewComponent.Factory
+ @Mock private lateinit var execution: Execution
- @Mock
- private lateinit var viewComponent: SmartspaceViewComponent
+ @Mock private lateinit var uiExecutor: Executor
- @Mock
- private lateinit var weatherViewComponent: SmartspaceViewComponent
+ @Mock private lateinit var viewComponentFactory: SmartspaceViewComponent.Factory
- private val weatherSmartspaceView: SmartspaceView by lazy {
- Mockito.spy(TestView(context))
- }
+ @Mock private lateinit var viewComponent: SmartspaceViewComponent
- @Mock
- private lateinit var targetFilter: SmartspaceTargetFilter
+ @Mock private lateinit var weatherViewComponent: SmartspaceViewComponent
- @Mock
- private lateinit var plugin: BcSmartspaceDataPlugin
+ private val weatherSmartspaceView: SmartspaceView by lazy { Mockito.spy(TestView(context)) }
- @Mock
- private lateinit var weatherPlugin: BcSmartspaceDataPlugin
+ @Mock private lateinit var targetFilter: SmartspaceTargetFilter
- @Mock
- private lateinit var precondition: SmartspacePrecondition
+ @Mock private lateinit var plugin: BcSmartspaceDataPlugin
- private val smartspaceView: SmartspaceView by lazy {
- Mockito.spy(TestView(context))
- }
+ @Mock private lateinit var weatherPlugin: BcSmartspaceDataPlugin
+
+ @Mock private lateinit var precondition: SmartspacePrecondition
- @Mock
- private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener
+ private val smartspaceView: SmartspaceView by lazy { Mockito.spy(TestView(context)) }
- @Mock
- private lateinit var session: SmartspaceSession
+ @Mock private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener
+
+ @Mock private lateinit var session: SmartspaceSession
private lateinit var controller: DreamSmartspaceController
// TODO(b/272811280): Remove usage of real view
- private val fakeParent by lazy {
- FrameLayout(context)
- }
+ private val fakeParent by lazy { FrameLayout(context) }
/**
* A class which implements SmartspaceView and extends View. This is mocked to provide the right
@@ -134,30 +120,44 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
override fun setMediaTarget(target: SmartspaceTarget?) {}
- override fun getSelectedPage(): Int { return 0; }
+ override fun getSelectedPage(): Int {
+ return 0
+ }
- override fun getCurrentCardTopPadding(): Int { return 0; }
+ override fun getCurrentCardTopPadding(): Int {
+ return 0
+ }
}
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(viewComponentFactory.create(any(), eq(plugin), any(), eq(null)))
- .thenReturn(viewComponent)
+ .thenReturn(viewComponent)
`when`(viewComponent.getView()).thenReturn(smartspaceView)
`when`(viewComponentFactory.create(any(), eq(weatherPlugin), any(), any()))
.thenReturn(weatherViewComponent)
`when`(weatherViewComponent.getView()).thenReturn(weatherSmartspaceView)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session)
- controller = DreamSmartspaceController(context, smartspaceManager, execution, uiExecutor,
- viewComponentFactory, precondition, Optional.of(targetFilter), Optional.of(plugin),
- Optional.of(weatherPlugin))
+ `when`(userTracker.userContext).thenReturn(userContextPrimary)
+ `when`(userContextPrimary.getSystemService(SmartspaceManager::class.java))
+ .thenReturn(smartspaceManager)
+
+ controller =
+ DreamSmartspaceController(
+ userTracker,
+ execution,
+ uiExecutor,
+ viewComponentFactory,
+ precondition,
+ Optional.of(targetFilter),
+ Optional.of(plugin),
+ Optional.of(weatherPlugin),
+ )
}
- /**
- * Ensures smartspace session begins on a listener only flow.
- */
+ /** Ensures smartspace session begins on a listener only flow. */
@Test
fun testConnectOnListen() {
`when`(precondition.conditionsMet()).thenReturn(true)
@@ -165,18 +165,18 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
verify(smartspaceManager).createSmartspaceSession(any())
- var targetListener = withArgCaptor<SmartspaceSession.OnTargetsAvailableListener> {
- verify(session).addOnTargetsAvailableListener(any(), capture())
- }
+ var targetListener =
+ withArgCaptor<SmartspaceSession.OnTargetsAvailableListener> {
+ verify(session).addOnTargetsAvailableListener(any(), capture())
+ }
`when`(targetFilter.filterSmartspaceTarget(any())).thenReturn(true)
var target = Mockito.mock(SmartspaceTarget::class.java)
targetListener.onTargetsAvailable(listOf(target))
- var targets = withArgCaptor<List<SmartspaceTarget>> {
- verify(plugin).onTargetsAvailable(capture())
- }
+ var targets =
+ withArgCaptor<List<SmartspaceTarget>> { verify(plugin).onTargetsAvailable(capture()) }
assertThat(targets.contains(target)).isTrue()
@@ -185,17 +185,16 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
verify(session).close()
}
- /**
- * Ensures session begins when a view is attached.
- */
+ /** Ensures session begins when a view is attached. */
@Test
fun testConnectOnViewCreate() {
`when`(precondition.conditionsMet()).thenReturn(true)
controller.buildAndConnectView(Mockito.mock(ViewGroup::class.java))
- val stateChangeListener = withArgCaptor<View.OnAttachStateChangeListener> {
- verify(viewComponentFactory).create(any(), eq(plugin), capture(), eq(null))
- }
+ val stateChangeListener =
+ withArgCaptor<View.OnAttachStateChangeListener> {
+ verify(viewComponentFactory).create(any(), eq(plugin), capture(), eq(null))
+ }
val mockView = Mockito.mock(TestView::class.java)
`when`(precondition.conditionsMet()).thenReturn(true)
@@ -209,9 +208,7 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
verify(session).close()
}
- /**
- * Ensures session is created when weather smartspace view is created and attached.
- */
+ /** Ensures session is created when weather smartspace view is created and attached. */
@Test
fun testConnectOnWeatherViewCreate() {
`when`(precondition.conditionsMet()).thenReturn(true)
@@ -223,8 +220,8 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
// Then weather view is created with custom view and the default weatherPlugin.getView
// should not be called
- verify(viewComponentFactory).create(eq(fakeParent), eq(weatherPlugin), any(),
- eq(customView))
+ verify(viewComponentFactory)
+ .create(eq(fakeParent), eq(weatherPlugin), any(), eq(customView))
verify(weatherPlugin, Mockito.never()).getView(fakeParent)
// And then session is created
@@ -234,9 +231,7 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
verify(weatherSmartspaceView).setDozeAmount(0f)
}
- /**
- * Ensures weather plugin registers target listener when it is added from the controller.
- */
+ /** Ensures weather plugin registers target listener when it is added from the controller. */
@Test
fun testAddListenerInController_registersListenerForWeatherPlugin() {
val customView = Mockito.mock(TestView::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index 44782529e5c3..33a08035a7b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -86,6 +86,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.UserLogoutInteractor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -160,6 +161,8 @@ public class KeyguardIndicationControllerBaseTest extends SysuiTestCase {
@Mock
protected DeviceEntryFingerprintAuthInteractor mDeviceEntryFingerprintAuthInteractor;
@Mock
+ protected UserLogoutInteractor mUserLogoutInteractor;
+ @Mock
protected ScreenLifecycle mScreenLifecycle;
@Mock
protected AuthController mAuthController;
@@ -248,6 +251,9 @@ public class KeyguardIndicationControllerBaseTest extends SysuiTestCase {
when(mFaceHelpMessageDeferralFactory.create()).thenReturn(mFaceHelpMessageDeferral);
when(mDeviceEntryFingerprintAuthInteractor.isEngaged()).thenReturn(mock(StateFlow.class));
+ StateFlow mockLogoutEnabledFlow = mock(StateFlow.class);
+ when(mockLogoutEnabledFlow.getValue()).thenReturn(false);
+ when(mUserLogoutInteractor.isLogoutEnabled()).thenReturn(mockLogoutEnabledFlow);
mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor);
@@ -291,7 +297,8 @@ public class KeyguardIndicationControllerBaseTest extends SysuiTestCase {
KeyguardInteractorFactory.create(mFlags).getKeyguardInteractor(),
mBiometricMessageInteractor,
mDeviceEntryFingerprintAuthInteractor,
- mDeviceEntryFaceAuthInteractor
+ mDeviceEntryFaceAuthInteractor,
+ mUserLogoutInteractor
);
mController.init();
mController.setIndicationArea(mIndicationArea);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index d6b3b919913f..72a2ce50ed9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -85,7 +85,6 @@ class OperatorNameViewControllerTest : SysuiTestCase() {
underTest =
OperatorNameViewController.Factory(
- darkIconDispatcher,
tunerService,
telephonyManager,
keyguardUpdateMonitor,
@@ -94,7 +93,7 @@ class OperatorNameViewControllerTest : SysuiTestCase() {
subscriptionManagerProxy,
javaAdapter,
)
- .create(view)
+ .create(view, darkIconDispatcher)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 60a185537b0d..fb7252b24295 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static android.app.Notification.CATEGORY_CALL;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -197,6 +199,19 @@ public class StatusBarIconViewTest extends SysuiTestCase {
}
@Test
+ public void testContentDescForNotification_noNotifContent() {
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(0)
+ .setContentTitle("hello")
+ .setCategory(CATEGORY_CALL)
+ .build();
+ assertThat(NotificationContentDescription.contentDescForNotification(mContext, n)
+ .toString()).startsWith("com.android.systemui.tests notification");
+ assertThat(NotificationContentDescription.contentDescForNotification(mContext, n)
+ .toString()).doesNotContain("hello");
+ }
+
+ @Test
@EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
public void setIcon_withPreloaded_usesPreloaded() {
Icon mockIcon = mock(Icon.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 6e190965d08b..1b4132910555 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -66,27 +66,43 @@ class NotifChipsViewModelTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.chips)
- setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null)))
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = null,
+ isPromoted = true,
+ )
+ )
+ )
assertThat(latest).isEmpty()
}
@Test
- fun chips_oneNotif_statusBarIconViewMatches() =
+ fun chips_onePromotedNotif_statusBarIconViewMatches() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
val icon = mock<StatusBarIconView>()
- setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = icon,
+ isPromoted = true,
+ )
+ )
+ )
assertThat(latest).hasSize(1)
val chip = latest!![0]
- assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
}
@Test
- fun chips_twoNotifs_twoChips() =
+ fun chips_onlyForPromotedNotifs() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -94,8 +110,21 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val secondIcon = mock<StatusBarIconView>()
setNotifs(
listOf(
- activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon),
- activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon),
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = secondIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "notif3",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = false,
+ ),
)
)
@@ -118,6 +147,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "clickTest",
statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = true,
)
)
)
@@ -138,8 +168,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
companion object {
fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
- assertThat(latest)
- .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
.isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
index 0efd591940f2..11a125a21be0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -16,29 +16,35 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class ScreenRecordChipInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
private val screenRecordRepo = kosmos.screenRecordRepository
private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
@@ -116,6 +122,137 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
+ fun screenRecordState_flagOff_doesNotAutomaticallySwitchToRecordingBasedOnTime() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ // WHEN screen record should start in 900ms
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(900)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(900))
+
+ // WHEN 900ms has elapsed
+ advanceTimeBy(901)
+
+ // THEN we don't automatically update to the recording state if the flag is off
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(900))
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
+ fun screenRecordState_flagOn_automaticallySwitchesToRecordingBasedOnTime() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ // WHEN screen record should start in 900ms
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(900)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(900))
+
+ // WHEN 900ms has elapsed
+ advanceTimeBy(901)
+
+ // THEN we automatically update to the recording state
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
+ fun screenRecordState_recordingBeginsEarly_switchesToRecording() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ // WHEN screen record should start in 900ms
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(900)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(900))
+
+ // WHEN we update to the Recording state earlier than 900ms
+ advanceTimeBy(800)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ val task = createTask(taskId = 1)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ hostDeviceName = null,
+ task,
+ )
+
+ // THEN we immediately switch to Recording, and we have the task
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
+
+ // WHEN more than 900ms has elapsed
+ advanceTimeBy(200)
+
+ // THEN we still stay in the Recording state and we have the task
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
+ fun screenRecordState_secondRecording_doesNotAutomaticallyStart() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ // First recording starts, records, and stops
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(900)
+ advanceTimeBy(900)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ advanceTimeBy(5000)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
+ advanceTimeBy(10000)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.DoingNothing)
+
+ // WHEN a second recording is starting
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(2900)
+
+ // THEN we stay as starting and do not switch to Recording (verifying the auto-start
+ // timer is reset)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(2900))
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
+ fun screenRecordState_startingButThenDoingNothing_doesNotAutomaticallyStart() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ // WHEN a screen recording is starting in 500ms
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(500)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(500))
+
+ // But it's cancelled after 300ms
+ advanceTimeBy(300)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
+
+ // THEN we don't automatically start the recording 200ms later
+ advanceTimeBy(201)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.DoingNothing)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_AUTO_START_SCREEN_RECORD_CHIP)
+ fun screenRecordState_multipleStartingValues_autoStartResets() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(2900)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(2900))
+
+ advanceTimeBy(2800)
+
+ // WHEN there's 100ms left to go before auto-start, but then we get a new start time
+ // that's in 500ms
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(500)
+
+ // THEN we don't auto-start in 100ms
+ advanceTimeBy(101)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(500))
+
+ // THEN we *do* auto-start 400ms later
+ advanceTimeBy(401)
+ assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+ }
+
+ @Test
fun stopRecording_sendsToRepo() =
testScope.runTest {
assertThat(screenRecordRepo.stopRecordingInvoked).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index bfebe184ae2d..48d8add6b33a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -26,9 +26,8 @@ import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -61,7 +61,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordChipViewModelTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
private val screenRecordRepo = kosmos.screenRecordRepository
private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
@@ -254,7 +254,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
MediaProjectionState.Projecting.SingleTask(
"host.package",
hostDeviceName = null,
- FakeActivityTaskManager.createTask(taskId = 1)
+ FakeActivityTaskManager.createTask(taskId = 1),
)
// THEN the start time is still the old start time
@@ -275,12 +275,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
clickListener!!.onClick(chipView)
// EndScreenRecordingDialogDelegate will test that the dialog has the right message
verify(kosmos.mockDialogTransitionAnimator)
- .showFromView(
- eq(mockSystemUIDialog),
- eq(chipBackgroundView),
- any(),
- anyBoolean(),
- )
+ .showFromView(eq(mockSystemUIDialog), eq(chipBackgroundView), any(), anyBoolean())
}
@Test
@@ -297,12 +292,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
clickListener!!.onClick(chipView)
// EndScreenRecordingDialogDelegate will test that the dialog has the right message
verify(kosmos.mockDialogTransitionAnimator)
- .showFromView(
- eq(mockSystemUIDialog),
- eq(chipBackgroundView),
- any(),
- anyBoolean(),
- )
+ .showFromView(eq(mockSystemUIDialog), eq(chipBackgroundView), any(), anyBoolean())
}
@Test
@@ -314,7 +304,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
MediaProjectionState.Projecting.SingleTask(
"host.package",
hostDeviceName = null,
- FakeActivityTaskManager.createTask(taskId = 1)
+ FakeActivityTaskManager.createTask(taskId = 1),
)
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
@@ -323,12 +313,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
clickListener!!.onClick(chipView)
// EndScreenRecordingDialogDelegate will test that the dialog has the right message
verify(kosmos.mockDialogTransitionAnimator)
- .showFromView(
- eq(mockSystemUIDialog),
- eq(chipBackgroundView),
- any(),
- anyBoolean(),
- )
+ .showFromView(eq(mockSystemUIDialog), eq(chipBackgroundView), any(), anyBoolean())
}
@Test
@@ -344,12 +329,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
val cujCaptor = argumentCaptor<DialogCuj>()
verify(kosmos.mockDialogTransitionAnimator)
- .showFromView(
- any(),
- any(),
- cujCaptor.capture(),
- anyBoolean(),
- )
+ .showFromView(any(), any(), cujCaptor.capture(), anyBoolean())
assertThat(cujCaptor.firstValue.cujType)
.isEqualTo(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index e96def6d43a3..c5c2a94cf0ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -29,7 +29,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -48,6 +47,7 @@ import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -72,7 +72,7 @@ import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val systemClock = kosmos.fakeSystemClock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index b12d7c57e1fd..25d5ce50e03f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -293,19 +293,27 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() =
+ fun chips_singlePromotedNotif_primaryIsNotifSecondaryIsHidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
val icon = mock<StatusBarIconView>()
- setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = icon,
+ isPromoted = true,
+ )
+ )
+ )
assertIsNotifChip(latest!!.primary, icon)
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@Test
- fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() =
+ fun chips_twoPromotedNotifs_primaryAndSecondaryAreNotifsInOrder() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -313,8 +321,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val secondIcon = mock<StatusBarIconView>()
setNotifs(
listOf(
- activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
- activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+ activeNotificationModel(
+ key = "firstNotif",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "secondNotif",
+ statusBarChipIcon = secondIcon,
+ isPromoted = true,
+ ),
)
)
@@ -323,7 +339,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun chips_threeNotifChips_topTwoShown() =
+ fun chips_threePromotedNotifs_topTwoShown() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -332,9 +348,21 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val thirdIcon = mock<StatusBarIconView>()
setNotifs(
listOf(
- activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
- activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
- activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon),
+ activeNotificationModel(
+ key = "firstNotif",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "secondNotif",
+ statusBarChipIcon = secondIcon,
+ isPromoted = true,
+ ),
+ activeNotificationModel(
+ key = "thirdNotif",
+ statusBarChipIcon = thirdIcon,
+ isPromoted = true,
+ ),
)
)
@@ -343,7 +371,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() =
+ fun chips_callAndPromotedNotifs_primaryIsCallSecondaryIsNotif() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -351,10 +379,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
val firstIcon = mock<StatusBarIconView>()
setNotifs(
listOf(
- activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+ activeNotificationModel(
+ key = "firstNotif",
+ statusBarChipIcon = firstIcon,
+ isPromoted = true,
+ ),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = true,
),
)
)
@@ -364,7 +397,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
}
@Test
- fun chips_screenRecordAndCallAndNotifs_notifsNotShown() =
+ fun chips_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
testScope.runTest {
val latest by collectLastValue(underTest.chips)
@@ -375,6 +408,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock<StatusBarIconView>(),
+ isPromoted = true,
)
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 643acdbb9277..2a3878c17a1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -16,18 +16,22 @@
package com.android.systemui.statusbar.connectivity
+import android.content.Context
+import android.os.UserHandle
import android.os.UserManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.Lifecycle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.wifitrackerlib.WifiEntry
import com.android.wifitrackerlib.WifiPickerTracker
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,36 +39,28 @@ import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyList
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class AccessPointControllerImplTest : SysuiTestCase() {
- @Mock
- private lateinit var userManager: UserManager
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var wifiPickerTrackerFactory:
- WifiPickerTrackerFactory
- @Mock
- private lateinit var wifiPickerTracker: WifiPickerTracker
- @Mock
- private lateinit var callback: AccessPointController.AccessPointCallback
- @Mock
- private lateinit var otherCallback: AccessPointController.AccessPointCallback
- @Mock
- private lateinit var wifiEntryConnected: WifiEntry
- @Mock
- private lateinit var wifiEntryOther: WifiEntry
- @Captor
- private lateinit var wifiEntryListCaptor: ArgumentCaptor<List<WifiEntry>>
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory
+ @Mock private lateinit var wifiPickerTracker: WifiPickerTracker
+ @Mock private lateinit var callback: AccessPointController.AccessPointCallback
+ @Mock private lateinit var otherCallback: AccessPointController.AccessPointCallback
+ @Mock private lateinit var wifiEntryConnected: WifiEntry
+ @Mock private lateinit var wifiEntryOther: WifiEntry
+ @Captor private lateinit var wifiEntryListCaptor: ArgumentCaptor<List<WifiEntry>>
private val instantExecutor = Executor { it.run() }
private lateinit var controller: AccessPointControllerImpl
@@ -72,19 +68,21 @@ class AccessPointControllerImplTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(wifiPickerTracker)
+ `when`(wifiPickerTrackerFactory.create(any(), any(), any(), any()))
+ .thenReturn(wifiPickerTracker)
`when`(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntryConnected)
- `when`(wifiPickerTracker.wifiEntries).thenReturn(ArrayList<WifiEntry>().apply {
- add(wifiEntryOther)
- })
+ `when`(wifiPickerTracker.wifiEntries)
+ .thenReturn(ArrayList<WifiEntry>().apply { add(wifiEntryOther) })
- controller = AccessPointControllerImpl(
+ controller =
+ AccessPointControllerImpl(
+ mContext,
userManager,
userTracker,
instantExecutor,
- wifiPickerTrackerFactory
- )
+ wifiPickerTrackerFactory,
+ )
controller.init()
}
@@ -183,13 +181,15 @@ class AccessPointControllerImplTest : SysuiTestCase() {
@Test
fun testReturnEmptyListWhenNoWifiPickerTracker() {
- `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(null)
- val otherController = AccessPointControllerImpl(
+ `when`(wifiPickerTrackerFactory.create(any(), any(), any(), any())).thenReturn(null)
+ val otherController =
+ AccessPointControllerImpl(
+ mContext,
userManager,
userTracker,
instantExecutor,
- wifiPickerTrackerFactory
- )
+ wifiPickerTrackerFactory,
+ )
otherController.init()
otherController.addAccessPointCallback(callback)
@@ -244,4 +244,19 @@ class AccessPointControllerImplTest : SysuiTestCase() {
verify(wifiEntryOther).connect(any())
verify(callback, never()).onSettingsActivityTriggered(any())
}
+
+ @Test
+ @EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
+ fun switchUsers() {
+ val primaryUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(UserHandle.of(PRIMARY_USER_ID), primaryUserMockContext)
+ controller.onUserSwitched(PRIMARY_USER_ID)
+ // Create is expected to be called once when the test starts and a second time when the user
+ // is switched.
+ verify(wifiPickerTrackerFactory, times(2)).create(any(), any(), any(), any())
+ }
+
+ private companion object {
+ private const val PRIMARY_USER_ID = 1
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
index 8dcc44463213..c06da4bc5080 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
@@ -17,19 +17,24 @@
package com.android.systemui.statusbar.core
import android.platform.test.annotations.EnableFlags
+import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.data.repository.fakeLightBarControllerStore
+import com.android.systemui.statusbar.data.repository.fakePrivacyDotWindowControllerStore
import com.android.systemui.testKosmos
import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@OptIn(ExperimentalCoroutinesApi::class)
@@ -39,16 +44,13 @@ import org.mockito.kotlin.verify
class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
@get:Rule val expect: Expect = Expect.create()
- private val kosmos =
- testKosmos().also {
- it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory
- it.statusBarInitializerStore = it.fakeStatusBarInitializerStore
- }
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val fakeDisplayRepository = kosmos.displayRepository
private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory
private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore
-
+ private val fakePrivacyDotStore = kosmos.fakePrivacyDotWindowControllerStore
+ private val fakeLightBarStore = kosmos.fakeLightBarControllerStore
// Lazy, so that @EnableFlags is set before initializer is instantiated.
private val underTest by lazy { kosmos.multiDisplayStatusBarStarter }
@@ -83,6 +85,56 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
}
@Test
+ fun start_startsPrivacyDotForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ verify(fakePrivacyDotStore.forDisplay(displayId = 1)).start()
+ verify(fakePrivacyDotStore.forDisplay(displayId = 2)).start()
+ }
+
+ @Test
+ fun start_doesNotStartLightBarControllerForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ verify(fakeLightBarStore.forDisplay(displayId = 1), never()).start()
+ verify(fakeLightBarStore.forDisplay(displayId = 2), never()).start()
+ }
+
+ @Test
+ fun start_createsLightBarControllerForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(1, 2)
+ }
+
+ @Test
+ fun start_doesNotStartPrivacyDotForDefaultDisplay() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = Display.DEFAULT_DISPLAY)
+
+ underTest.start()
+ runCurrent()
+
+ verify(fakePrivacyDotStore.forDisplay(displayId = Display.DEFAULT_DISPLAY), never())
+ .start()
+ }
+
+ @Test
fun displayAdded_orchestratorForNewDisplayIsStarted() =
testScope.runTest {
underTest.start()
@@ -109,6 +161,42 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
}
@Test
+ fun displayAdded_privacyDotForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakePrivacyDotStore.forDisplay(displayId = 3)).start()
+ }
+
+ @Test
+ fun displayAdded_lightBarForNewDisplayIsCreated() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(3)
+ }
+
+ @Test
+ fun displayAdded_lightBarForNewDisplayIsNotStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakeLightBarStore.forDisplay(displayId = 3), never()).start()
+ }
+
+ @Test
fun displayAddedDuringStart_initializerForNewDisplayIsStarted() =
testScope.runTest {
underTest.start()
@@ -129,8 +217,39 @@ class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
fakeDisplayRepository.addDisplay(displayId = 3)
runCurrent()
- expect
- .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
- .isTrue()
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start()
+ }
+
+ @Test
+ fun displayAddedDuringStart_privacyDotForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakePrivacyDotStore.forDisplay(displayId = 3)).start()
+ }
+
+ @Test
+ fun displayAddedDuringStart_lightBarForNewDisplayIsCreated() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ assertThat(fakeLightBarStore.perDisplayMocks.keys).containsExactly(3)
+ }
+
+ @Test
+ fun displayAddedDuringStart_lightBarForNewDisplayIsNotStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakeLightBarStore.forDisplay(displayId = 3), never()).start()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index 20a19a9b1399..009b33b9f808 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -23,14 +23,16 @@ import android.platform.test.annotations.EnableFlags
import android.view.ViewGroup
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.Assert.assertThrows
@@ -45,12 +47,14 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class StatusBarInitializerTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val windowController = mock(StatusBarWindowController::class.java)
private val windowControllerStore = mock(StatusBarWindowControllerStore::class.java)
private val transaction = mock(FragmentTransaction::class.java)
private val fragmentManager = mock(FragmentManager::class.java)
private val fragmentHostManager = mock(FragmentHostManager::class.java)
private val backgroundView = mock(ViewGroup::class.java)
+ private val statusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository
@Before
fun setup() {
@@ -72,24 +76,25 @@ class StatusBarInitializerTest : SysuiTestCase() {
statusBarRootFactory = mock(StatusBarRootFactory::class.java),
componentFactory = mock(HomeStatusBarComponent.Factory::class.java),
creationListeners = setOf(),
+ statusBarModePerDisplayRepository = statusBarModePerDisplayRepository,
)
@Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- fun simpleFragment_startsFromCoreStartable() {
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME)
+ fun flagOn_startsFromCoreStartable() {
underTest.start()
assertThat(underTest.initialized).isTrue()
}
@Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- fun simpleFragment_throwsIfInitializeIsCalled() {
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME)
+ fun flagOn_throwsIfInitializeIsCalled() {
assertThrows(IllegalStateException::class.java) { underTest.initializeStatusBar() }
}
@Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- fun simpleFragment_flagEnabled_doesNotCreateFragment() {
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME)
+ fun flagOn_flagEnabled_doesNotCreateFragment() {
underTest.start()
verify(fragmentManager, never()).beginTransaction()
@@ -97,14 +102,14 @@ class StatusBarInitializerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
fun flagOff_startCalled_stillInitializes() {
underTest.start()
assertThat(underTest.initialized).isTrue()
}
@Test
- @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
fun flagOff_doesNotThrowIfInitializeIsCalled() {
underTest.initializeStatusBar()
assertThat(underTest.initialized).isTrue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt
new file mode 100644
index 000000000000..18eef33813f6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LightBarControllerStoreImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+
+ private val underTest = kosmos.lightBarControllerStoreImpl
+
+ @Before
+ fun start() {
+ underTest.start()
+ }
+
+ @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }
+
+ @Test
+ fun forDisplay_startsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).start()
+ }
+
+ @Test
+ fun beforeDisplayRemoved_doesNotStopInstances() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance, never()).stop()
+ }
+
+ @Test
+ fun displayRemoved_stopsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).stop()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
new file mode 100644
index 000000000000..a2c3c66f4448
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayDarkIconDispatcherStoreTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+
+ // Lazy so that @EnableFlags has time to run before underTest is instantiated.
+ private val underTest by lazy { kosmos.multiDisplayDarkIconDispatcherStore }
+
+ @Before
+ fun start() {
+ underTest.start()
+ }
+
+ @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }
+
+ @Test
+ fun beforeDisplayRemoved_doesNotStopInstances() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance, never()).stop()
+ }
+
+ @Test
+ fun displayRemoved_stopsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).stop()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt
new file mode 100644
index 000000000000..a9920ec5e29b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarModeRepositoryStoreTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+ private val underTest by lazy { kosmos.multiDisplayStatusBarModeRepositoryStore }
+
+ @Before
+ fun start() {
+ underTest.start()
+ }
+
+ @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }
+
+ @Test
+ fun forDisplay_startsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).start()
+ }
+
+ @Test
+ fun displayRemoved_stopsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).stop()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreImplTest.kt
new file mode 100644
index 000000000000..ae734b3ca04f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreImplTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class PrivacyDotWindowControllerStoreImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val underTest by lazy { kosmos.privacyDotWindowControllerStoreImpl }
+
+ @Before
+ fun installDisplays() = runBlocking {
+ kosmos.displayRepository.addDisplay(displayId = Display.DEFAULT_DISPLAY)
+ kosmos.displayRepository.addDisplay(displayId = Display.DEFAULT_DISPLAY + 1)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun forDisplay_defaultDisplay_throws() {
+ underTest.forDisplay(displayId = Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun forDisplay_nonDefaultDisplay_doesNotThrow() {
+ underTest.forDisplay(displayId = Display.DEFAULT_DISPLAY + 1)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt
new file mode 100644
index 000000000000..e65c04c45382
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class SystemEventChipAnimationControllerStoreImplTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+
+ // Lazy so that @EnableFlags has time to run before underTest is instantiated.
+ private val underTest by lazy { kosmos.systemEventChipAnimationControllerStoreImpl }
+
+ @Before
+ fun start() {
+ underTest.start()
+ }
+
+ @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }
+
+ @Test
+ fun beforeDisplayRemoved_doesNotStopInstances() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ verify(instance, never()).stop()
+ }
+
+ @Test
+ fun displayRemoved_stopsInstance() =
+ testScope.runTest {
+ val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+
+ fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
+
+ verify(instance).stop()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationControllerTest.kt
new file mode 100644
index 000000000000..d4007d7df7be
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationControllerTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import android.platform.test.annotations.EnableFlags
+import androidx.core.animation.AnimatorSet
+import androidx.core.animation.ValueAnimator
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.data.repository.systemEventChipAnimationControllerStore
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplaySystemEventChipAnimationControllerTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val displayRepository = kosmos.displayRepository
+ private val store = kosmos.systemEventChipAnimationControllerStore
+
+ // Lazy so that @EnableFlags has time to switch the flags before the instance is created.
+ private val underTest by lazy { kosmos.multiDisplaySystemEventChipAnimationController }
+
+ @Before
+ fun installDisplays() = runBlocking {
+ INSTALLED_DISPLAY_IDS.forEach { displayRepository.addDisplay(displayId = it) }
+ }
+
+ @Test
+ fun init_forwardsToAllControllers() {
+ underTest.init()
+
+ INSTALLED_DISPLAY_IDS.forEach { verify(store.forDisplay(it)).init() }
+ }
+
+ @Test
+ fun stop_forwardsToAllControllers() {
+ underTest.stop()
+
+ INSTALLED_DISPLAY_IDS.forEach { verify(store.forDisplay(it)).stop() }
+ }
+
+ @Test
+ fun announceForAccessibility_forwardsToAllControllers() {
+ val contentDescription = "test content description"
+ underTest.announceForAccessibility(contentDescription)
+
+ INSTALLED_DISPLAY_IDS.forEach {
+ verify(store.forDisplay(it)).announceForAccessibility(contentDescription)
+ }
+ }
+
+ @Test
+ fun onSystemEventAnimationBegin_returnsAnimatorSetWithOneAnimatorPerDisplay() {
+ INSTALLED_DISPLAY_IDS.forEach {
+ val controller = store.forDisplay(it)
+ whenever(controller.onSystemEventAnimationBegin()).thenReturn(ValueAnimator.ofInt(0, 1))
+ }
+ val animator = underTest.onSystemEventAnimationBegin() as AnimatorSet
+
+ assertThat(animator.childAnimations).hasSize(INSTALLED_DISPLAY_IDS.size)
+ }
+
+ @Test
+ fun onSystemEventAnimationFinish_returnsAnimatorSetWithOneAnimatorPerDisplay() {
+ INSTALLED_DISPLAY_IDS.forEach {
+ val controller = store.forDisplay(it)
+ whenever(controller.onSystemEventAnimationFinish(any()))
+ .thenReturn(ValueAnimator.ofInt(0, 1))
+ }
+ val animator =
+ underTest.onSystemEventAnimationFinish(hasPersistentDot = true) as AnimatorSet
+
+ assertThat(animator.childAnimations).hasSize(INSTALLED_DISPLAY_IDS.size)
+ }
+
+ companion object {
+ private const val DISPLAY_ID_1 = 123
+ private const val DISPLAY_ID_2 = 456
+ private val INSTALLED_DISPLAY_IDS = listOf(DISPLAY_ID_1, DISPLAY_ID_2)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerTest.kt
new file mode 100644
index 000000000000..6bcd735e9a9f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerTest.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import android.view.Gravity.BOTTOM
+import android.view.Gravity.LEFT
+import android.view.Gravity.RIGHT
+import android.view.Gravity.TOP
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import android.view.fakeWindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyDotWindowControllerTest : SysuiTestCase() {
+
+ @get:Rule val expect: Expect = Expect.create()
+
+ private val kosmos = testKosmos()
+ private val underTest = kosmos.privacyDotWindowController
+ private val viewController = kosmos.privacyDotViewController
+ private val windowManager = kosmos.fakeWindowManager
+ private val executor = kosmos.fakeExecutor
+
+ @After
+ fun cleanUpCustomDisplay() {
+ context.display = null
+ }
+
+ @Test
+ fun start_beforeUiThreadExecutes_doesNotAddWindows() {
+ underTest.start()
+
+ assertThat(windowManager.addedViews).isEmpty()
+ }
+
+ @Test
+ fun start_beforeUiThreadExecutes_doesNotInitializeViewController() {
+ underTest.start()
+
+ assertThat(viewController.isInitialized).isFalse()
+ }
+
+ @Test
+ fun start_afterUiThreadExecutes_addsWindowsOnUiThread() {
+ underTest.start()
+
+ executor.runAllReady()
+
+ assertThat(windowManager.addedViews).hasSize(4)
+ }
+
+ @Test
+ fun start_afterUiThreadExecutes_initializesViewController() {
+ underTest.start()
+
+ executor.runAllReady()
+
+ assertThat(viewController.isInitialized).isTrue()
+ }
+
+ @Test
+ fun start_initializesTopLeft() {
+ underTest.start()
+ executor.runAllReady()
+
+ assertThat(viewController.topLeft?.id).isEqualTo(R.id.privacy_dot_top_left_container)
+ }
+
+ @Test
+ fun start_initializesTopRight() {
+ underTest.start()
+ executor.runAllReady()
+
+ assertThat(viewController.topRight?.id).isEqualTo(R.id.privacy_dot_top_right_container)
+ }
+
+ @Test
+ fun start_initializesTopBottomLeft() {
+ underTest.start()
+ executor.runAllReady()
+
+ assertThat(viewController.bottomLeft?.id).isEqualTo(R.id.privacy_dot_bottom_left_container)
+ }
+
+ @Test
+ fun start_initializesBottomRight() {
+ underTest.start()
+ executor.runAllReady()
+
+ assertThat(viewController.bottomRight?.id)
+ .isEqualTo(R.id.privacy_dot_bottom_right_container)
+ }
+
+ @Test
+ fun start_viewsAddedInRespectiveCorners() {
+ context.display = mock { on { rotation } doReturn Surface.ROTATION_0 }
+
+ underTest.start()
+ executor.runAllReady()
+
+ expect.that(gravityForView(viewController.topLeft!!)).isEqualTo(TOP or LEFT)
+ expect.that(gravityForView(viewController.topRight!!)).isEqualTo(TOP or RIGHT)
+ expect.that(gravityForView(viewController.bottomLeft!!)).isEqualTo(BOTTOM or LEFT)
+ expect.that(gravityForView(viewController.bottomRight!!)).isEqualTo(BOTTOM or RIGHT)
+ }
+
+ @Test
+ fun start_rotation90_viewsPositionIsShifted90degrees() {
+ context.display = mock { on { rotation } doReturn Surface.ROTATION_90 }
+
+ underTest.start()
+ executor.runAllReady()
+
+ expect.that(gravityForView(viewController.topLeft!!)).isEqualTo(BOTTOM or LEFT)
+ expect.that(gravityForView(viewController.topRight!!)).isEqualTo(TOP or LEFT)
+ expect.that(gravityForView(viewController.bottomLeft!!)).isEqualTo(BOTTOM or RIGHT)
+ expect.that(gravityForView(viewController.bottomRight!!)).isEqualTo(TOP or RIGHT)
+ }
+
+ @Test
+ fun start_rotation180_viewsPositionIsShifted180degrees() {
+ context.display = mock { on { rotation } doReturn Surface.ROTATION_180 }
+
+ underTest.start()
+ executor.runAllReady()
+
+ expect.that(gravityForView(viewController.topLeft!!)).isEqualTo(BOTTOM or RIGHT)
+ expect.that(gravityForView(viewController.topRight!!)).isEqualTo(BOTTOM or LEFT)
+ expect.that(gravityForView(viewController.bottomLeft!!)).isEqualTo(TOP or RIGHT)
+ expect.that(gravityForView(viewController.bottomRight!!)).isEqualTo(TOP or LEFT)
+ }
+
+ @Test
+ fun start_rotation270_viewsPositionIsShifted270degrees() {
+ context.display = mock { on { rotation } doReturn Surface.ROTATION_270 }
+
+ underTest.start()
+ executor.runAllReady()
+
+ expect.that(gravityForView(viewController.topLeft!!)).isEqualTo(TOP or RIGHT)
+ expect.that(gravityForView(viewController.topRight!!)).isEqualTo(BOTTOM or RIGHT)
+ expect.that(gravityForView(viewController.bottomLeft!!)).isEqualTo(TOP or LEFT)
+ expect.that(gravityForView(viewController.bottomRight!!)).isEqualTo(BOTTOM or LEFT)
+ }
+
+ private fun paramsForView(view: View): WindowManager.LayoutParams {
+ return windowManager.addedViews.entries
+ .first { it.key == view || it.key.findViewById<View>(view.id) != null }
+ .value
+ }
+
+ private fun gravityForView(view: View): Int {
+ return paramsForView(view).gravity
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
index 12473cb46793..896f940f8a60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
@@ -34,31 +34,9 @@ class NotificationContentDescriptionTest : SysuiTestCase() {
private val TICKER = "this is a ticker"
@Test
- fun notificationWithAllDifferentFields_descriptionIsTitle() {
+ fun notificationWithAllDifferentFields_descriptionIsAppName() {
val n = createNotification(TITLE, TEXT, TICKER)
val description = contentDescForNotification(context, n)
- assertThat(description).isEqualTo(createDescriptionText(n, TITLE))
- }
-
- @Test
- fun notificationWithAllDifferentFields_titleMatchesAppName_descriptionIsText() {
- val n = createNotification(getTestAppName(), TEXT, TICKER)
- val description = contentDescForNotification(context, n)
- assertThat(description).isEqualTo(createDescriptionText(n, TEXT))
- }
-
- @Test
- fun notificationWithAllDifferentFields_titleMatchesAppNameNoText_descriptionIsTicker() {
- val n = createNotification(getTestAppName(), null, TICKER)
- val description = contentDescForNotification(context, n)
- assertThat(description).isEqualTo(createDescriptionText(n, TICKER))
- }
-
- @Test
- fun notificationWithAllDifferentFields_titleMatchesAppNameNoTextNoTicker_descriptionEmpty() {
- val appName = getTestAppName()
- val n = createNotification(appName, null, null)
- val description = contentDescForNotification(context, n)
assertThat(description).isEqualTo(createDescriptionText(n, ""))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index 3b5d358f7c2c..c4b1b841c6a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification
+import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
@@ -26,11 +26,17 @@ import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -41,9 +47,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -55,16 +58,21 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyFloat
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
+class NotificationWakeUpCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@@ -105,6 +113,18 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
statusBarStateCallback.onDozeAmountChanged(dozeAmount, dozeAmount)
}
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Before
fun setup() {
whenever(bypassController.bypassEnabled).then { bypassEnabled }
@@ -178,6 +198,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
}
@Test
+ @DisableSceneContainer
fun setDozeToZeroWhenCommunalShowingWillFullyHideNotifications() =
testScope.runTest {
val transitionState =
@@ -192,6 +213,17 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun setDozeToZeroWhenCommunalShowingWillFullyHideNotifications_withSceneContainer() =
+ testScope.runTest {
+ kosmos.setSceneTransition(Idle(Scenes.Communal))
+ setDozeAmount(0f)
+ verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f)
+ assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue()
+ }
+
+ @Test
+ @DisableSceneContainer
fun closingCommunalWillShowNotifications() =
testScope.runTest {
val transitionState =
@@ -211,6 +243,20 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun closingCommunalWillShowNotifications_withSceneContainer() =
+ testScope.runTest {
+ kosmos.setSceneTransition(Idle(Scenes.Communal))
+ setDozeAmount(0f)
+ verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f)
+ assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue()
+
+ kosmos.setSceneTransition(Idle(CommunalScenes.Blank))
+ verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f)
+ assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse()
+ }
+
+ @Test
fun switchingToShadeWithBypassEnabledWillShowNotifications() {
setDozeToZeroWithBypassWillFullyHideNotifications()
clearInvocations(stackScrollerController)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index ea5c29ef30aa..3ad41a54ac7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
@@ -32,9 +34,9 @@ import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.compose.animation.scene.ObservableTransitionState;
@@ -42,6 +44,7 @@ import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.BrokenWithSceneContainer;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -78,14 +81,23 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
+import java.util.List;
+
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.test.TestScope;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
public class VisualStabilityCoordinatorTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return parameterizeSceneContainerFlag();
+ }
+
private VisualStabilityCoordinator mCoordinator;
@Mock private DumpManager mDumpManager;
@@ -117,6 +129,11 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
private NotificationEntry mEntry;
private GroupEntry mGroupEntry;
+ public VisualStabilityCoordinatorTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -251,6 +268,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
@Test
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
public void testLockscreenPartlyShowing_groupAndSectionChangesNotAllowed() {
// GIVEN the panel true expanded and device isn't pulsing
setFullyDozed(false);
@@ -267,6 +285,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
@Test
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
public void testLockscreenFullyShowing_groupAndSectionChangesNotAllowed() {
// GIVEN the panel true expanded and device isn't pulsing
setFullyDozed(false);
@@ -520,6 +539,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Test
@EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
public void testNotLockscreenInGoneTransition_invalidationCalled() {
// GIVEN visual stability is being maintained b/c animation is playing
mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
@@ -589,6 +609,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
@Test
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
public void testCommunalShowingWillNotSuppressReordering() {
// GIVEN panel is expanded, communal is showing, and QS is collapsed
setPulsing(false);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index 9f40f60db45f..99bda856818e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -18,11 +18,14 @@
package com.android.systemui.statusbar.notification.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
@@ -44,7 +47,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
private val testScope = kosmos.testScope
private val activeNotificationListRepository = kosmos.activeNotificationListRepository
- private val underTest = kosmos.activeNotificationsInteractor
+ private val underTest by lazy { kosmos.activeNotificationsInteractor }
@Test
fun testAllNotificationsCount() =
@@ -65,14 +68,8 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
val normalNotifs =
listOf(
- activeNotificationModel(
- key = "notif1",
- callType = CallType.None,
- ),
- activeNotificationModel(
- key = "notif2",
- callType = CallType.None,
- )
+ activeNotificationModel(key = "notif1", callType = CallType.None),
+ activeNotificationModel(key = "notif2", callType = CallType.None),
)
activeNotificationListRepository.activeNotifications.value =
@@ -129,10 +126,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
val latest by collectLastValue(underTest.ongoingCallNotification)
val ongoingNotif =
- activeNotificationModel(
- key = "ongoingNotif",
- callType = CallType.Ongoing,
- )
+ activeNotificationModel(key = "ongoingNotif", callType = CallType.Ongoing)
activeNotificationListRepository.activeNotifications.value =
ActiveNotificationsStore.Builder()
@@ -170,6 +164,62 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun promotedOngoingNotifications_flagOff_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.promotedOngoingNotifications)
+
+ val promoted1 = activeNotificationModel(key = "notif1", isPromoted = true)
+ val notPromoted2 = activeNotificationModel(key = "notif2", isPromoted = false)
+
+ activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { addIndividualNotif(promoted1) }
+ .apply { addIndividualNotif(notPromoted2) }
+ .build()
+
+ assertThat(latest!!).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun promotedOngoingNotifications_nonePromoted_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.promotedOngoingNotifications)
+
+ activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { activeNotificationModel(key = "notif1", isPromoted = false) }
+ .apply { activeNotificationModel(key = "notif2", isPromoted = false) }
+ .apply { activeNotificationModel(key = "notif3", isPromoted = false) }
+ .build()
+
+ assertThat(latest!!).isEmpty()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun promotedOngoingNotifications_somePromoted_hasOnlyPromoted() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.promotedOngoingNotifications)
+
+ val promoted1 = activeNotificationModel(key = "notif1", isPromoted = true)
+ val notPromoted2 = activeNotificationModel(key = "notif2", isPromoted = false)
+ val notPromoted3 = activeNotificationModel(key = "notif3", isPromoted = false)
+ val promoted4 = activeNotificationModel(key = "notif4", isPromoted = true)
+
+ activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { addIndividualNotif(promoted1) }
+ .apply { addIndividualNotif(notPromoted2) }
+ .apply { addIndividualNotif(notPromoted3) }
+ .apply { addIndividualNotif(promoted4) }
+ .build()
+
+ assertThat(latest!!).containsExactly(promoted1, promoted4)
+ }
+
+ @Test
fun areAnyNotificationsPresent_isTrue() =
testScope.runTest {
val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 572a0c1b1869..183f9016a23b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -16,22 +16,25 @@
package com.android.systemui.statusbar.notification.domain.interactor
import android.app.Notification
-import android.os.Bundle
+import android.app.Notification.FLAG_PROMOTED_ONGOING
+import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+import com.android.systemui.statusbar.notification.promoted.promotedNotificationsProvider
import com.android.systemui.statusbar.notification.shared.byKey
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,16 +42,16 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class RenderNotificationsListInteractorTest : SysuiTestCase() {
- private val backgroundDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(backgroundDispatcher)
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
- private val notifsRepository = ActiveNotificationListRepository()
- private val notifsInteractor =
- ActiveNotificationsInteractor(notifsRepository, backgroundDispatcher)
+ private val notifsRepository = kosmos.activeNotificationListRepository
+ private val notifsInteractor = kosmos.activeNotificationsInteractor
private val underTest =
RenderNotificationListInteractor(
notifsRepository,
sectionStyleProvider = mock(),
+ promotedNotificationsProvider = kosmos.promotedNotificationsProvider,
)
@Test
@@ -85,12 +88,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
assertThat(ranks)
.containsExactlyEntriesIn(
- mapOf(
- "single" to 0,
- "summary" to 1,
- "child0" to 2,
- "child1" to 3,
- )
+ mapOf("single" to 0, "summary" to 1, "child0" to 2, "child1" to 3)
)
}
@@ -126,6 +124,53 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
assertThat(actual).containsAtLeastEntriesIn(expected)
}
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun setRenderList_setsPromotionStatus() =
+ testScope.runTest {
+ val actual by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+
+ val notPromoted1 = mockNotificationEntry("key1", flag = null)
+ val promoted2 = mockNotificationEntry("key2", flag = FLAG_PROMOTED_ONGOING)
+
+ underTest.setRenderedList(listOf(notPromoted1, promoted2))
+
+ assertThat(actual!!.size).isEqualTo(2)
+
+ val first = actual!![0]
+ assertThat(first.key).isEqualTo("key1")
+ assertThat(first.isPromoted).isFalse()
+
+ val second = actual!![1]
+ assertThat(second.key).isEqualTo("key2")
+ assertThat(second.isPromoted).isTrue()
+ }
+
+ private fun mockNotificationEntry(
+ key: String,
+ rank: Int = 0,
+ flag: Int? = null,
+ ): NotificationEntry {
+ val nBuilder = Notification.Builder(context, "a")
+ if (flag != null) {
+ nBuilder.setFlag(flag, true)
+ }
+ val notification = nBuilder.build()
+
+ val mockSbn =
+ mock<StatusBarNotification>() {
+ whenever(this.notification).thenReturn(notification)
+ whenever(packageName).thenReturn("com.android")
+ }
+ return mock<NotificationEntry> {
+ whenever(this.key).thenReturn(key)
+ whenever(this.icons).thenReturn(mock())
+ whenever(this.representativeEntry).thenReturn(this)
+ whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
+ whenever(this.sbn).thenReturn(mockSbn)
+ }
+ }
}
private fun mockGroupEntry(
@@ -139,19 +184,3 @@ private fun mockGroupEntry(
whenever(this.children).thenReturn(children)
}
}
-
-private fun mockNotificationEntry(key: String, rank: Int = 0): NotificationEntry {
- val mockNotification = mock<Notification> { this.extras = Bundle() }
- val mockSbn =
- mock<StatusBarNotification>() {
- whenever(notification).thenReturn(mockNotification)
- whenever(packageName).thenReturn("com.android")
- }
- return mock<NotificationEntry> {
- whenever(this.key).thenReturn(key)
- whenever(this.icons).thenReturn(mock())
- whenever(this.representativeEntry).thenReturn(this)
- whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
- whenever(this.sbn).thenReturn(mockSbn)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 740abf359e92..76390fddc529 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -18,12 +18,13 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel
import android.content.res.mainResources
import android.platform.test.annotations.DisableFlags
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -51,17 +52,20 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class NotificationIconContainerAlwaysOnDisplayViewModelTest(flags: FlagsParameterization) :
+ SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) }
}
- val underTest =
+ val underTest by lazy {
NotificationIconContainerAlwaysOnDisplayViewModel(
kosmos.testDispatcher,
kosmos.alwaysOnDisplayNotificationIconsInteractor,
@@ -70,11 +74,24 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
kosmos.mainResources,
kosmos.shadeInteractor,
)
+ }
val testScope = kosmos.testScope
val keyguardRepository = kosmos.fakeKeyguardRepository
val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
val powerRepository = kosmos.fakePowerRepository
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
@Before
fun setup() {
keyguardRepository.setKeyguardShowing(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 9367a93a2890..46c360aecd48 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -110,14 +110,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(
- to = DozeStateModel.DOZE_AOD,
- )
+ DozeTransitionModel(to = DozeStateModel.DOZE_AOD)
)
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
runCurrent()
@@ -133,14 +129,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(
- to = DozeStateModel.DOZE_PULSING,
- )
+ DozeTransitionModel(to = DozeStateModel.DOZE_PULSING)
)
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
runCurrent()
@@ -201,9 +193,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
lastSleepReason = WakeSleepReason.OTHER,
)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
runCurrent()
@@ -216,9 +206,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
val animationsEnabled by collectLastValue(underTest.animationsEnabled)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(transitionState = TransitionState.STARTED)
)
keyguardRepository.setKeyguardShowing(true)
runCurrent()
@@ -234,13 +222,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
@Test
fun iconColors_testsDarkBounds() =
testScope.runTest {
- darkIconRepository.darkState.value =
- SysuiDarkIconDispatcher.DarkChange(
- emptyList(),
- 0f,
- 0xAABBCC,
- )
- val iconColorsLookup by collectLastValue(underTest.iconColors)
+ val displayId = 123
+ darkIconRepository.darkState(displayId).value =
+ SysuiDarkIconDispatcher.DarkChange(emptyList(), 0f, 0xAABBCC)
+ val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
assertThat(iconColorsLookup).isNotNull()
val iconColors = iconColorsLookup?.iconColors(Rect())
@@ -257,13 +242,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
@Test
fun iconColors_staticDrawableColor_notInDarkTintArea() =
testScope.runTest {
- darkIconRepository.darkState.value =
- SysuiDarkIconDispatcher.DarkChange(
- listOf(Rect(0, 0, 5, 5)),
- 0f,
- 0xAABBCC,
- )
- val iconColorsLookup by collectLastValue(underTest.iconColors)
+ val displayId = 321
+ darkIconRepository.darkState(displayId).value =
+ SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
+ val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
val iconColors = iconColorsLookup?.iconColors(Rect(1, 1, 4, 4))
val staticDrawableColor = iconColors?.staticDrawableColor(Rect(6, 6, 7, 7))
assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT)
@@ -272,13 +254,10 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
@Test
fun iconColors_notInDarkTintArea() =
testScope.runTest {
- darkIconRepository.darkState.value =
- SysuiDarkIconDispatcher.DarkChange(
- listOf(Rect(0, 0, 5, 5)),
- 0f,
- 0xAABBCC,
- )
- val iconColorsLookup by collectLastValue(underTest.iconColors)
+ val displayId = 987
+ darkIconRepository.darkState(displayId).value =
+ SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
+ val iconColorsLookup by collectLastValue(underTest.iconColors(displayId))
val iconColors = iconColorsLookup?.iconColors(Rect(6, 6, 7, 7))
assertThat(iconColors).isNull()
}
@@ -295,7 +274,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
activeNotificationModel(
key = "notif1",
groupKey = "group",
- statusBarIcon = icon
+ statusBarIcon = icon,
)
)
}
@@ -322,7 +301,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
activeNotificationModel(
key = "notif1",
groupKey = "group",
- statusBarIcon = icon
+ statusBarIcon = icon,
)
)
}
@@ -354,7 +333,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati
activeNotificationModel(
key = "notif1",
groupKey = "group",
- statusBarIcon = icon
+ statusBarIcon = icon,
)
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt
new file mode 100644
index 000000000000..a9dbe63e8f07
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted
+
+import android.app.Notification
+import android.app.Notification.FLAG_PROMOTED_ONGOING
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+
+@SmallTest
+class PromotedNotificationsProviderTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val underTest = kosmos.promotedNotificationsProvider
+
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldPromote_uiFlagOff_false() {
+ val entry = createNotification(FLAG_PROMOTED_ONGOING)
+
+ assertThat(underTest.shouldPromote(entry)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldPromote_uiFlagOn_notifDoesNotHaveFlag_false() {
+ val entry = createNotification(flag = null)
+
+ assertThat(underTest.shouldPromote(entry)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldPromote_uiFlagOn_notifHasFlag_true() {
+ val entry = createNotification(FLAG_PROMOTED_ONGOING)
+
+ assertThat(underTest.shouldPromote(entry)).isTrue()
+ }
+
+ private fun createNotification(flag: Int? = null): NotificationEntry {
+ val n = Notification.Builder(context, "a")
+ if (flag != null) {
+ n.setFlag(flag, true)
+ }
+
+ return NotificationEntryBuilder().setNotification(n.build()).build()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
new file mode 100644
index 000000000000..a1b63b159277
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.row
+
+import android.R
+import android.app.AppOpsManager
+import android.app.INotificationManager
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Intent
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutManager
+import android.graphics.Color
+import android.os.Binder
+import android.os.UserManager
+import android.os.fakeExecutorHandler
+import android.platform.test.flag.junit.FlagsParameterization
+import android.provider.Settings
+import android.service.notification.NotificationListenerService
+import android.testing.TestableLooper.RunWithLooper
+import android.util.ArraySet
+import android.view.View
+import android.view.accessibility.AccessibilityManager
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.domain.interactor.PowerInteractorFactory.create
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.NotificationEntryHelper
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.testKosmos
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+/** Tests for [NotificationGutsManager]. */
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@RunWithLooper
+class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val testNotificationChannel =
+ NotificationChannel(
+ TEST_CHANNEL_ID,
+ TEST_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ )
+
+ private val kosmos = testKosmos()
+
+ private val testScope = kosmos.testScope
+ private val javaAdapter = JavaAdapter(testScope.backgroundScope)
+ private val executor = kosmos.fakeExecutor
+ private val handler = kosmos.fakeExecutorHandler
+ private lateinit var helper: NotificationTestHelper
+ private lateinit var gutsManager: NotificationGutsManager
+
+ @get:Rule val rule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var onUserInteractionCallback: OnUserInteractionCallback
+ @Mock private lateinit var presenter: NotificationPresenter
+ @Mock private lateinit var notificationActivityStarter: NotificationActivityStarter
+ @Mock private lateinit var notificationListContainer: NotificationListContainer
+ @Mock
+ private lateinit var onSettingsClickListener: NotificationGutsManager.OnSettingsClickListener
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var accessibilityManager: AccessibilityManager
+ @Mock private lateinit var highPriorityProvider: HighPriorityProvider
+ @Mock private lateinit var iNotificationManager: INotificationManager
+ @Mock private lateinit var barService: IStatusBarService
+ @Mock private lateinit var launcherApps: LauncherApps
+ @Mock private lateinit var shortcutManager: ShortcutManager
+ @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+ @Mock private lateinit var contextTracker: UserContextProvider
+ @Mock private lateinit var bubblesManager: BubblesManager
+ @Mock private lateinit var shadeController: ShadeController
+ @Mock private lateinit var peopleSpaceWidgetManager: PeopleSpaceWidgetManager
+ @Mock private lateinit var assistantFeedbackController: AssistantFeedbackController
+ @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var headsUpManager: HeadsUpManager
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var userManager: UserManager
+
+ private lateinit var windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
+
+ companion object {
+ private const val TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ helper = NotificationTestHelper(mContext, mDependency)
+ whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(false)
+
+ windowRootViewVisibilityInteractor =
+ WindowRootViewVisibilityInteractor(
+ testScope.backgroundScope,
+ WindowRootViewVisibilityRepository(barService, executor),
+ FakeKeyguardRepository(),
+ headsUpManager,
+ create().powerInteractor,
+ kosmos.activeNotificationsInteractor,
+ ) {
+ kosmos.sceneInteractor
+ }
+
+ gutsManager =
+ NotificationGutsManager(
+ mContext,
+ handler,
+ handler,
+ javaAdapter,
+ accessibilityManager,
+ highPriorityProvider,
+ iNotificationManager,
+ userManager,
+ peopleSpaceWidgetManager,
+ launcherApps,
+ shortcutManager,
+ channelEditorDialogController,
+ contextTracker,
+ assistantFeedbackController,
+ Optional.of(bubblesManager),
+ UiEventLoggerFake(),
+ onUserInteractionCallback,
+ shadeController,
+ windowRootViewVisibilityInteractor,
+ notificationLockscreenUserManager,
+ statusBarStateController,
+ barService,
+ deviceProvisionedController,
+ metricsLogger,
+ headsUpManager,
+ activityStarter,
+ )
+ gutsManager.setUpWithPresenter(
+ presenter,
+ notificationListContainer,
+ onSettingsClickListener,
+ )
+ gutsManager.setNotificationActivityStarter(notificationActivityStarter)
+ gutsManager.start()
+ }
+
+ @Test
+ fun testOpenAndCloseGuts() {
+ val guts = spy(NotificationGuts(mContext))
+ whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+ handler.post(((invocation.arguments[0] as Runnable)))
+ null
+ }
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any())
+
+ val realRow = createTestNotificationRow()
+ val menuItem = createTestMenuItem(realRow)
+
+ val row = spy(realRow)
+ whenever(row.windowToken).thenReturn(Binder())
+ whenever(row.guts).thenReturn(guts)
+
+ assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+ assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong())
+ executor.runAllReady()
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+ verify(headsUpManager).setGutsShown(realRow.entry, true)
+
+ assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong())
+ gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false)
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
+ verify(row, times(1)).setGutsView(any<MenuItem>())
+ executor.runAllReady()
+ verify(headsUpManager).setGutsShown(realRow.entry, false)
+ }
+
+ @Test
+ fun testLockscreenShadeVisible_visible_gutsNotClosed() =
+ testScope.runTest {
+ // First, start out lockscreen or shade as not visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ runCurrent()
+
+ val guts: NotificationGuts = mock()
+ gutsManager.exposedGuts = guts
+
+ // WHEN the lockscreen or shade becomes visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ runCurrent()
+
+ // THEN the guts are not closed
+ verify(guts, never()).removeCallbacks(any())
+ verify(guts, never())
+ .closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun testLockscreenShadeVisible_notVisible_gutsClosed() =
+ testScope.runTest {
+ // First, start out lockscreen or shade as visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ runCurrent()
+
+ val guts: NotificationGuts = mock()
+ gutsManager.exposedGuts = guts
+
+ // WHEN the lockscreen or shade is no longer visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ runCurrent()
+
+ // THEN the guts are closed
+ verify(guts).removeCallbacks(null)
+ verify(guts)
+ .closeControls(
+ /* leavebehinds = */ eq(true),
+ /* controls = */ eq(true),
+ /* x = */ anyInt(),
+ /* y = */ anyInt(),
+ /* force = */ eq(true),
+ )
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testShadeVisible_notVisible_gutsClosed() =
+ testScope.runTest {
+ // First, start with shade as visible
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+ runCurrent()
+
+ val guts: NotificationGuts = mock()
+ gutsManager.exposedGuts = guts
+
+ // WHEN the shade is no longer visible
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ runCurrent()
+
+ // THEN the guts are closed
+ verify(guts).removeCallbacks(null)
+ verify(guts)
+ .closeControls(
+ /* leavebehinds = */ eq(true),
+ /* controls = */ eq(true),
+ /* x = */ anyInt(),
+ /* y = */ anyInt(),
+ /* force = */ eq(true),
+ )
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun testLockscreenShadeVisible_notVisible_listContainerReset() =
+ testScope.runTest {
+ // First, start out lockscreen or shade as visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ runCurrent()
+ clearInvocations(notificationListContainer)
+
+ // WHEN the lockscreen or shade is no longer visible
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ runCurrent()
+
+ // THEN the list container is reset
+ verify(notificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean())
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testShadeVisible_notVisible_listContainerReset() =
+ testScope.runTest {
+ // First, start with shade as visible
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+ runCurrent()
+ clearInvocations(notificationListContainer)
+
+ // WHEN the shade is no longer visible
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ runCurrent()
+
+ // THEN the list container is reset
+ verify(notificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean())
+ }
+
+ @Test
+ fun testChangeDensityOrFontScale() {
+ val guts = spy(NotificationGuts(mContext))
+ whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+ handler.post(((invocation.arguments[0] as Runnable)))
+ null
+ }
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+ val realRow = createTestNotificationRow()
+ val menuItem = createTestMenuItem(realRow)
+
+ val row = spy(realRow)
+
+ whenever(row.windowToken).thenReturn(Binder())
+ whenever(row.guts).thenReturn(guts)
+ doNothing().whenever(row).ensureGutsInflated()
+
+ val realEntry = realRow.entry
+ val entry = spy(realEntry)
+
+ whenever(entry.row).thenReturn(row)
+ whenever(entry.guts).thenReturn(guts)
+
+ assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+ executor.runAllReady()
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+ // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+ verify(row).setGutsView(any<MenuItem>())
+
+ row.onDensityOrFontScaleChanged()
+ gutsManager.onDensityOrFontScaleChanged(entry)
+
+ executor.runAllReady()
+
+ gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
+
+ verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
+
+ // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
+ verify(row, times(2)).setGutsView(any<MenuItem>())
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_mic() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera_mic() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera_mic_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_camera_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_CAMERA)
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action)
+ }
+
+ @Test
+ fun testAppOpsSettingsIntent_mic_overlay() {
+ val row = createTestNotificationRow()
+ val ops = ArraySet<Int>()
+ ops.add(AppOpsManager.OP_RECORD_AUDIO)
+ ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+ gutsManager.startAppOpsSettingsActivity("", 0, ops, row)
+ val captor = argumentCaptor<Intent>()
+ verify(notificationActivityStarter, times(1))
+ .startNotificationGutsIntent(captor.capture(), anyInt(), eq(row))
+ assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testInitializeNotificationInfoView_highPriority() {
+ val notificationInfoView: NotificationInfo = mock()
+ val row = spy(helper.createRow())
+ val entry = row.entry
+ NotificationEntryHelper.modifyRanking(entry)
+ .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
+ .setImportance(NotificationManager.IMPORTANCE_HIGH)
+ .build()
+
+ whenever(row.isNonblockable).thenReturn(false)
+ whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
+ val statusBarNotification = entry.sbn
+ gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ verify(notificationInfoView)
+ .bindNotification(
+ any<PackageManager>(),
+ any<INotificationManager>(),
+ eq(onUserInteractionCallback),
+ eq(channelEditorDialogController),
+ eq(statusBarNotification.packageName),
+ any<NotificationChannel>(),
+ eq(entry),
+ any<NotificationInfo.OnSettingsClickListener>(),
+ any<NotificationInfo.OnAppSettingsClickListener>(),
+ any<UiEventLogger>(),
+ /* isDeviceProvisioned = */ eq(false),
+ /* isNonblockable = */ eq(false),
+ /* wasShownHighPriority = */ eq(true),
+ eq(assistantFeedbackController),
+ eq(metricsLogger),
+ )
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testInitializeNotificationInfoView_PassesAlongProvisionedState() {
+ val notificationInfoView: NotificationInfo = mock()
+ val row = spy(helper.createRow())
+ NotificationEntryHelper.modifyRanking(row.entry)
+ .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
+ .build()
+ whenever(row.isNonblockable).thenReturn(false)
+ val statusBarNotification = row.entry.sbn
+ val entry = row.entry
+
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+
+ gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ verify(notificationInfoView)
+ .bindNotification(
+ any<PackageManager>(),
+ any<INotificationManager>(),
+ eq(onUserInteractionCallback),
+ eq(channelEditorDialogController),
+ eq(statusBarNotification.packageName),
+ any<NotificationChannel>(),
+ eq(entry),
+ any<NotificationInfo.OnSettingsClickListener>(),
+ any<NotificationInfo.OnAppSettingsClickListener>(),
+ any<UiEventLogger>(),
+ /* isDeviceProvisioned = */ eq(true),
+ /* isNonblockable = */ eq(false),
+ /* wasShownHighPriority = */ eq(false),
+ eq(assistantFeedbackController),
+ eq(metricsLogger),
+ )
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testInitializeNotificationInfoView_withInitialAction() {
+ val notificationInfoView: NotificationInfo = mock()
+ val row = spy(helper.createRow())
+ NotificationEntryHelper.modifyRanking(row.entry)
+ .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
+ .build()
+ whenever(row.isNonblockable).thenReturn(false)
+ val statusBarNotification = row.entry.sbn
+ val entry = row.entry
+
+ gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ verify(notificationInfoView)
+ .bindNotification(
+ any<PackageManager>(),
+ any<INotificationManager>(),
+ eq(onUserInteractionCallback),
+ eq(channelEditorDialogController),
+ eq(statusBarNotification.packageName),
+ any<NotificationChannel>(),
+ eq(entry),
+ any<NotificationInfo.OnSettingsClickListener>(),
+ any<NotificationInfo.OnAppSettingsClickListener>(),
+ any<UiEventLogger>(),
+ /* isDeviceProvisioned = */ eq(false),
+ /* isNonblockable = */ eq(false),
+ /* wasShownHighPriority = */ eq(false),
+ eq(assistantFeedbackController),
+ eq(metricsLogger),
+ )
+ }
+
+ private fun createTestNotificationRow(): ExpandableNotificationRow {
+ val nb =
+ Notification.Builder(mContext, testNotificationChannel.id)
+ .setContentTitle("foo")
+ .setColorized(true)
+ .setColor(Color.RED)
+ .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setSmallIcon(R.drawable.sym_def_app_icon)
+
+ try {
+ val row = helper.createRow(nb.build())
+ NotificationEntryHelper.modifyRanking(row.entry)
+ .setChannel(testNotificationChannel)
+ .build()
+ return row
+ } catch (e: Exception) {
+ fail()
+ }
+ }
+
+ private fun createTestMenuItem(
+ row: ExpandableNotificationRow
+ ): NotificationMenuRowPlugin.MenuItem {
+ val menuRow: NotificationMenuRowPlugin =
+ NotificationMenuRow(mContext, peopleNotificationIdentifier)
+ menuRow.createMenu(row, row.entry.sbn)
+
+ val menuItem = menuRow.getLongpressMenuItem(mContext)
+ assertNotNull(menuItem)
+ return menuItem
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index dae5542123ed..50db9f7268e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -21,16 +21,19 @@ import android.service.notification.StatusBarNotification
import android.view.View.VISIBLE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
@@ -52,8 +55,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var stackLayout: NotificationStackScrollLayout
+ @Mock private lateinit var seenNotificationsInteractor: SeenNotificationsInteractor
private val testableResources = mContext.orCreateTestableResources
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
private lateinit var sizeCalculator: NotificationStackSizeCalculator
@@ -72,7 +78,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
lockscreenShadeTransitionController = lockscreenShadeTransitionController,
mediaDataManager = mediaDataManager,
testableResources.resources,
- ResourcesSplitShadeStateController()
+ ResourcesSplitShadeStateController(),
+ seenNotificationsInteractor = seenNotificationsInteractor,
+ scope = testScope,
)
}
@@ -85,7 +93,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
rows,
spaceForNotifications = 0f,
spaceForShelf = 0f,
- shelfHeight = 0f
+ shelfHeight = 0f,
)
assertThat(maxNotifications).isEqualTo(0)
@@ -101,7 +109,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
rows,
spaceForNotifications = Float.MAX_VALUE,
spaceForShelf = Float.MAX_VALUE,
- shelfHeight
+ shelfHeight,
)
assertThat(maxNotifications).isEqualTo(numberOfRows)
@@ -137,7 +145,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
listOf(row),
/* spaceForNotifications= */ 5f,
/* spaceForShelf= */ 0f,
- /* shelfHeight= */ 0f
+ /* shelfHeight= */ 0f,
)
assertThat(maxNotifications).isEqualTo(1)
@@ -148,11 +156,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
setGapHeight(gapHeight)
val shelfHeight = shelfHeight + dividerHeight
val spaceForNotifications =
- listOf(
- rowHeight + dividerHeight,
- gapHeight + rowHeight + dividerHeight,
- )
- .sum()
+ listOf(rowHeight + dividerHeight, gapHeight + rowHeight + dividerHeight).sum()
val spaceForShelf = gapHeight + dividerHeight + shelfHeight
val rows =
listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
@@ -162,7 +166,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
rows,
spaceForNotifications + 1,
spaceForShelf,
- shelfHeight
+ shelfHeight,
)
assertThat(maxNotifications).isEqualTo(2)
@@ -173,12 +177,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
// Each row in separate section.
setGapHeight(gapHeight)
- val notifSpace =
- listOf(
- rowHeight,
- dividerHeight + gapHeight + rowHeight,
- )
- .sum()
+ val notifSpace = listOf(rowHeight, dividerHeight + gapHeight + rowHeight).sum()
val shelfSpace = dividerHeight + gapHeight + shelfHeight
val spaceUsed = notifSpace + shelfSpace
@@ -209,7 +208,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
rows,
spaceForNotifications + 1,
spaceForShelf,
- shelfHeight
+ shelfHeight,
)
assertThat(maxNotifications).isEqualTo(1)
@@ -252,7 +251,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = true
+ onLockscreen = true,
)
assertThat(space.whenEnoughSpace).isEqualTo(10f)
}
@@ -272,7 +271,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = true
+ onLockscreen = true,
)
assertThat(space.whenEnoughSpace).isEqualTo(5)
}
@@ -291,7 +290,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = true
+ onLockscreen = true,
)
assertThat(space.whenSavingSpace).isEqualTo(5)
}
@@ -311,7 +310,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = true
+ onLockscreen = true,
)
assertThat(space.whenSavingSpace).isEqualTo(5)
}
@@ -330,7 +329,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = false
+ onLockscreen = false,
)
assertThat(space.whenEnoughSpace).isEqualTo(rowHeight)
assertThat(space.whenSavingSpace).isEqualTo(rowHeight)
@@ -340,14 +339,14 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
rows: List<ExpandableView>,
spaceForNotifications: Float,
spaceForShelf: Float,
- shelfHeight: Float = this.shelfHeight
+ shelfHeight: Float = this.shelfHeight,
): Int {
setupChildren(rows)
return sizeCalculator.computeMaxKeyguardNotifications(
stackLayout,
spaceForNotifications,
spaceForShelf,
- shelfHeight
+ shelfHeight,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
index 476252737454..d665b3166986 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -24,7 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.coroutines.collectValues
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -75,9 +75,9 @@ open class HideNotificationsInteractorTest : SysuiTestCase() {
configurationController,
context,
testScope.backgroundScope,
- mock()
+ mock(),
)
- private val configurationInteractor = ConfigurationInteractor(configurationRepository)
+ private val configurationInteractor = ConfigurationInteractorImpl(configurationRepository)
private val unfoldTransitionRepository =
UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
@@ -103,7 +103,7 @@ open class HideNotificationsInteractorTest : SysuiTestCase() {
unfoldTransitionInteractor,
configurationInteractor,
animationStatus,
- powerInteractor
+ powerInteractor,
)
}
@@ -140,7 +140,7 @@ open class HideNotificationsInteractorTest : SysuiTestCase() {
updateDisplay(
width = INITIAL_DISPLAY_HEIGHT,
height = INITIAL_DISPLAY_WIDTH,
- rotation = ROTATION_90
+ rotation = ROTATION_90,
)
runCurrent()
@@ -284,7 +284,7 @@ open class HideNotificationsInteractorTest : SysuiTestCase() {
private fun updateDisplay(
width: Int = INITIAL_DISPLAY_WIDTH,
height: Int = INITIAL_DISPLAY_HEIGHT,
- @Surface.Rotation rotation: Int = ROTATION_0
+ @Surface.Rotation rotation: Int = ROTATION_0,
) {
configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height))
configuration.windowConfiguration.displayRotation = rotation
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
index 327a07d6179f..4176d1c1f6fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt
@@ -15,35 +15,55 @@
*
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.stack.domain.interactor
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SharedNotificationContainerInteractorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class SharedNotificationContainerInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardRepository = kosmos.fakeKeyguardRepository
private val configurationRepository = kosmos.fakeConfigurationRepository
private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
- private val underTest = kosmos.sharedNotificationContainerInteractor
+ private val underTest by lazy { kosmos.sharedNotificationContainerInteractor }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Test
+ @DisableSceneContainer
fun validateConfigValues() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
index e9d88ccb0cbc..122cfdd1a045 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
@@ -16,16 +16,22 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.data.repository.windowRootViewVisibilityRepository
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.testKosmos
@@ -33,10 +39,12 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class NotificationLoggerViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class NotificationLoggerViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -46,9 +54,22 @@ class NotificationLoggerViewModelTest : SysuiTestCase() {
private val powerInteractor = kosmos.powerInteractor
private val windowRootViewVisibilityRepository = kosmos.windowRootViewVisibilityRepository
- private val underTest = kosmos.notificationListLoggerViewModel
+ private val underTest by lazy { kosmos.notificationListLoggerViewModel }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Test
+ @DisableSceneContainer
fun isLockscreenOrShadeInteractive_deviceActiveAndShadeIsInteractive_true() =
testScope.runTest {
powerInteractor.setAwakeForTest()
@@ -60,6 +81,7 @@ class NotificationLoggerViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableSceneContainer
fun isLockscreenOrShadeInteractive_deviceIsAsleepAndShadeIsInteractive_false() =
testScope.runTest {
powerInteractor.setAsleepForTest()
@@ -71,6 +93,7 @@ class NotificationLoggerViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableSceneContainer
fun isLockscreenOrShadeInteractive_deviceActiveAndShadeIsNotInteractive_false() =
testScope.runTest {
powerInteractor.setAwakeForTest()
@@ -82,6 +105,54 @@ class NotificationLoggerViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun isLockscreenOrShadeInteractive_deviceAwakeAndShadeIsDisplayed_true() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ kosmos.setSceneTransition(Idle(Scenes.Shade))
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun isLockscreenOrShadeInteractive_deviceAwakeAndLockScreenIsDisplayed_true() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun isLockscreenOrShadeInteractive_deviceIsAsleepOnLockscreen_false() =
+ testScope.runTest {
+ powerInteractor.setAsleepForTest()
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun isLockscreenOrShadeInteractive_deviceActiveAndShadeIsClosed() =
+ testScope.runTest {
+ powerInteractor.setAwakeForTest()
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ val actual by collectLastValue(underTest.isLockscreenOrShadeInteractive)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
fun activeNotifications_hasNotifications() =
testScope.runTest {
activeNotificationListRepository.setActiveNotifs(5)
@@ -135,6 +206,7 @@ class NotificationLoggerViewModelTest : SysuiTestCase() {
assertThat(isOnLockScreen).isTrue()
}
+
@Test
fun isOnLockScreen_false() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
deleted file mode 100644
index 157f8189276a..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ /dev/null
@@ -1,845 +0,0 @@
-/*
- * Copyright (C) 2021 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.statusbar.phone;
-
-
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
-
-import static com.android.systemui.Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.provider.Settings;
-import android.testing.TestableLooper;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.CarrierTextController;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.flags.DisableSceneContainer;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.shade.ShadeViewStateProvider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore;
-import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
-import com.android.systemui.statusbar.phone.ui.TintedIconManager;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
- @Mock
- private CarrierTextController mCarrierTextController;
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private SystemStatusAnimationScheduler mAnimationScheduler;
- @Mock
- private BatteryController mBatteryController;
- @Mock
- private UserInfoController mUserInfoController;
- @Mock
- private StatusBarIconController mStatusBarIconController;
- @Mock
- private TintedIconManager.Factory mIconManagerFactory;
- @Mock
- private TintedIconManager mIconManager;
- @Mock
- private BatteryMeterViewController mBatteryMeterViewController;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardBypassController mKeyguardBypassController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private BiometricUnlockController mBiometricUnlockController;
- @Mock
- private SysuiStatusBarStateController mStatusBarStateController;
- @Mock
- private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider;
- @Mock
- private StatusBarContentInsetsProviderStore mStatusBarContentInsetsProviderStore;
- @Mock
- private UserManager mUserManager;
- @Mock
- private StatusBarUserChipViewModel mStatusBarUserChipViewModel;
- @Captor
- private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor;
- @Captor
- private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
- @Mock private SecureSettings mSecureSettings;
- @Mock private CommandQueue mCommandQueue;
- @Mock private KeyguardLogger mLogger;
- @Mock private StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
-
- private TestShadeViewStateProvider mShadeViewStateProvider;
- private KeyguardStatusBarView mKeyguardStatusBarView;
- private KeyguardStatusBarViewController mController;
- private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
- private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
- private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-
- @Before
- public void setup() throws Exception {
- mShadeViewStateProvider = new TestShadeViewStateProvider();
-
- MockitoAnnotations.initMocks(this);
- when(mStatusBarContentInsetsProviderStore.getDefaultDisplay())
- .thenReturn(mStatusBarContentInsetsProvider);
- when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager);
-
- allowTestableLooperAsMainThread();
- TestableLooper.get(this).runWithLooper(() -> {
- mKeyguardStatusBarView =
- spy((KeyguardStatusBarView) LayoutInflater.from(mContext)
- .inflate(R.layout.keyguard_status_bar, null));
- when(mKeyguardStatusBarView.getDisplay()).thenReturn(mContext.getDisplay());
- });
-
- mController = createController();
- }
-
- private KeyguardStatusBarViewController createController() {
- return new KeyguardStatusBarViewController(
- mKeyguardStatusBarView,
- mCarrierTextController,
- mConfigurationController,
- mAnimationScheduler,
- mBatteryController,
- mUserInfoController,
- mStatusBarIconController,
- mIconManagerFactory,
- mBatteryMeterViewController,
- mShadeViewStateProvider,
- mKeyguardStateController,
- mKeyguardBypassController,
- mKeyguardUpdateMonitor,
- mKosmos.getKeyguardStatusBarViewModel(),
- mBiometricUnlockController,
- mStatusBarStateController,
- mStatusBarContentInsetsProviderStore,
- mUserManager,
- mStatusBarUserChipViewModel,
- mSecureSettings,
- mCommandQueue,
- mFakeExecutor,
- mBackgroundExecutor,
- mLogger,
- mStatusOverlayHoverListenerFactory,
- mKosmos.getCommunalSceneInteractor()
- );
- }
-
- @Test
- @EnableFlags(FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
- public void onViewAttached_updateUserSwitcherFlagEnabled_callbacksRegistered() {
- mController.onViewAttached();
-
- runAllScheduled();
- verify(mConfigurationController).addCallback(any());
- verify(mAnimationScheduler).addCallback(any());
- verify(mUserInfoController).addCallback(any());
- verify(mCommandQueue).addCallback(any());
- verify(mStatusBarIconController).addIconGroup(any());
- verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
- }
-
- @Test
- @DisableFlags(FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
- public void onViewAttached_updateUserSwitcherFlagDisabled_callbacksRegistered() {
- mController.onViewAttached();
-
- verify(mConfigurationController).addCallback(any());
- verify(mAnimationScheduler).addCallback(any());
- verify(mUserInfoController).addCallback(any());
- verify(mCommandQueue).addCallback(any());
- verify(mStatusBarIconController).addIconGroup(any());
- verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
- }
-
- @Test
- @EnableFlags(FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
- public void
- onConfigurationChanged_updateUserSwitcherFlagEnabled_updatesUserSwitcherVisibility() {
- mController.onViewAttached();
- runAllScheduled();
- verify(mConfigurationController).addCallback(mConfigurationListenerCaptor.capture());
- clearInvocations(mUserManager);
- clearInvocations(mKeyguardStatusBarView);
-
- mConfigurationListenerCaptor.getValue().onConfigChanged(null);
-
- runAllScheduled();
- verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
- verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
- }
-
- @Test
- @DisableFlags(FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
- public void
- onConfigurationChanged_updateUserSwitcherFlagDisabled_updatesUserSwitcherVisibility() {
- mController.onViewAttached();
- verify(mConfigurationController).addCallback(mConfigurationListenerCaptor.capture());
- clearInvocations(mUserManager);
- clearInvocations(mKeyguardStatusBarView);
-
- mConfigurationListenerCaptor.getValue().onConfigChanged(null);
- verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
- verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
- }
-
- @Test
- @EnableFlags(FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
- public void
- onKeyguardVisibilityChanged_userSwitcherFlagEnabled_updatesUserSwitcherVisibility() {
- mController.onViewAttached();
- runAllScheduled();
- verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardCallbackCaptor.capture());
- clearInvocations(mUserManager);
- clearInvocations(mKeyguardStatusBarView);
-
- mKeyguardCallbackCaptor.getValue().onKeyguardVisibilityChanged(true);
-
- runAllScheduled();
- verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
- verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
- }
-
- @Test
- @DisableFlags(FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
- public void
- onKeyguardVisibilityChanged_userSwitcherFlagDisabled_updatesUserSwitcherVisibility() {
- mController.onViewAttached();
- verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardCallbackCaptor.capture());
- clearInvocations(mUserManager);
- clearInvocations(mKeyguardStatusBarView);
-
- mKeyguardCallbackCaptor.getValue().onKeyguardVisibilityChanged(true);
- verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
- verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
- }
-
- @Test
- public void onViewDetached_callbacksUnregistered() {
- // Set everything up first.
- mController.onViewAttached();
-
- mController.onViewDetached();
-
- verify(mConfigurationController).removeCallback(any());
- verify(mAnimationScheduler).removeCallback(any());
- verify(mUserInfoController).removeCallback(any());
- verify(mCommandQueue).removeCallback(any());
- verify(mStatusBarIconController).removeIconGroup(any());
- }
-
- @Test
- @DisableSceneContainer
- public void onViewReAttached_flagOff_iconManagerNotReRegistered() {
- mController.onViewAttached();
- mController.onViewDetached();
- reset(mStatusBarIconController);
-
- mController.onViewAttached();
-
- verify(mStatusBarIconController, never()).addIconGroup(any());
- }
-
- @Test
- @EnableSceneContainer
- public void onViewReAttached_flagOn_iconManagerReRegistered() {
- mController.onViewAttached();
- mController.onViewDetached();
- reset(mStatusBarIconController);
-
- mController.onViewAttached();
-
- verify(mStatusBarIconController).addIconGroup(any());
- }
-
- @Test
- @DisableSceneContainer
- public void setBatteryListening_true_callbackAdded() {
- mController.setBatteryListening(true);
-
- verify(mBatteryController).addCallback(any());
- }
-
- @Test
- @DisableSceneContainer
- public void setBatteryListening_false_callbackRemoved() {
- // First set to true so that we know setting to false is a change in state.
- mController.setBatteryListening(true);
-
- mController.setBatteryListening(false);
-
- verify(mBatteryController).removeCallback(any());
- }
-
- @Test
- @DisableSceneContainer
- public void setBatteryListening_trueThenTrue_callbackAddedOnce() {
- mController.setBatteryListening(true);
- mController.setBatteryListening(true);
-
- verify(mBatteryController).addCallback(any());
- }
-
- @Test
- @EnableSceneContainer
- public void setBatteryListening_true_flagOn_callbackNotAdded() {
- mController.setBatteryListening(true);
-
- verify(mBatteryController, never()).addCallback(any());
- }
-
- @Test
- public void updateTopClipping_viewClippingUpdated() {
- int viewTop = 20;
- mKeyguardStatusBarView.setTop(viewTop);
- int notificationPanelTop = 30;
-
- mController.updateTopClipping(notificationPanelTop);
-
- assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(
- notificationPanelTop - viewTop);
- }
-
- @Test
- public void setNotTopClipping_viewClippingUpdatedToZero() {
- // Start out with some amount of top clipping.
- mController.updateTopClipping(50);
- assertThat(mKeyguardStatusBarView.getClipBounds().top).isGreaterThan(0);
-
- mController.setNoTopClipping();
-
- assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(0);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_alphaAndVisibilityGiven_viewUpdated() {
- // Verify the initial values so we know the method triggers changes.
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(1f);
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
-
- float newAlpha = 0.5f;
- int newVisibility = View.INVISIBLE;
- mController.updateViewState(newAlpha, newVisibility);
-
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(newAlpha);
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(newVisibility);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_paramVisibleButIsDisabled_viewIsInvisible() {
- mController.onViewAttached();
- setDisableSystemIcons(true);
-
- mController.updateViewState(1f, View.VISIBLE);
-
- // Since we're disabled, we stay invisible
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_notKeyguardState_nothingUpdated() {
- mController.onViewAttached();
- updateStateToNotKeyguard();
-
- float oldAlpha = mKeyguardStatusBarView.getAlpha();
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(oldAlpha);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
-
- when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(true);
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- onFinishedGoingToSleep();
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_bypassNotEnabled_viewShown() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(true);
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false);
- onFinishedGoingToSleep();
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_shouldNotListenForFace_viewShown() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(false);
- when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- onFinishedGoingToSleep();
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_panelExpandedHeightZero_viewHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mShadeViewStateProvider.setPanelViewExpandedHeight(0);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_dragProgressOne_viewHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mShadeViewStateProvider.setLockscreenShadeDragProgress(1f);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_disableSystemInfoFalse_viewShown() {
- mController.onViewAttached();
- updateStateToKeyguard();
- setDisableSystemInfo(false);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_disableSystemInfoTrue_viewHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
- setDisableSystemInfo(true);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_disableSystemIconsFalse_viewShown() {
- mController.onViewAttached();
- updateStateToKeyguard();
- setDisableSystemIcons(false);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_disableSystemIconsTrue_viewHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
- setDisableSystemIcons(true);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_dozingTrue_flagOff_viewHidden() {
- mController.init();
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mController.setDozing(true);
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateViewState_dozingFalse_flagOff_viewShown() {
- mController.init();
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mController.setDozing(false);
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @EnableSceneContainer
- public void updateViewState_flagOn_doesNothing() {
- mController.init();
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mKeyguardStatusBarView.setVisibility(View.GONE);
- mKeyguardStatusBarView.setAlpha(0.456f);
-
- mController.updateViewState();
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.GONE);
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.456f);
- }
-
- @Test
- @EnableSceneContainer
- public void updateViewStateWithAlphaAndVis_flagOn_doesNothing() {
- mController.init();
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mKeyguardStatusBarView.setVisibility(View.GONE);
- mKeyguardStatusBarView.setAlpha(0.456f);
-
- mController.updateViewState(0.789f, View.VISIBLE);
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.GONE);
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.456f);
- }
-
- @Test
- @EnableSceneContainer
- public void setAlpha_flagOn_doesNothing() {
- mController.init();
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mKeyguardStatusBarView.setAlpha(0.456f);
-
- mController.setAlpha(0.123f);
-
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.456f);
- }
-
- @Test
- @EnableSceneContainer
- public void setDozing_flagOn_doesNothing() {
- mController.init();
- mController.onViewAttached();
- updateStateToKeyguard();
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
-
- mController.setDozing(true);
- mController.updateViewState();
-
- // setDozing(true) should typically cause the view to hide. But since the flag is on, we
- // should ignore these set dozing calls and stay the same visibility.
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void setAlpha_explicitAlpha_setsExplicitAlpha() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mController.setAlpha(0.5f);
-
- assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(0.5f);
- }
-
- @Test
- @DisableSceneContainer
- public void setAlpha_explicitAlpha_thenMinusOneAlpha_setsAlphaBasedOnDefaultCriteria() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- mController.setAlpha(0.5f);
- mController.setAlpha(-1f);
-
- assertThat(mKeyguardStatusBarView.getAlpha()).isGreaterThan(0);
- assertThat(mKeyguardStatusBarView.getAlpha()).isNotEqualTo(0.5f);
- }
-
- // TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized.
-
- @Test
- @DisableSceneContainer
- public void updateForHeadsUp_headsUpShouldBeVisible_viewHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
- mKeyguardStatusBarView.setVisibility(View.VISIBLE);
-
- mShadeViewStateProvider.setShouldHeadsUpBeVisible(true);
- mController.updateForHeadsUp(/* animate= */ false);
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- @Test
- @DisableSceneContainer
- public void updateForHeadsUp_headsUpShouldNotBeVisible_viewShown() {
- mController.onViewAttached();
- updateStateToKeyguard();
-
- // Start with the opposite state.
- mShadeViewStateProvider.setShouldHeadsUpBeVisible(true);
- mController.updateForHeadsUp(/* animate= */ false);
-
- mShadeViewStateProvider.setShouldHeadsUpBeVisible(false);
- mController.updateForHeadsUp(/* animate= */ false);
-
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void testNewUserSwitcherDisablesAvatar_newUiOn() {
- // GIVEN the status bar user switcher chip is enabled
- when(mStatusBarUserChipViewModel.getChipEnabled()).thenReturn(true);
-
- // WHEN the controller is created
- mController = createController();
-
- // THEN keyguard status bar view avatar is disabled
- assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isFalse();
- }
-
- @Test
- public void testNewUserSwitcherDisablesAvatar_newUiOff() {
- // GIVEN the status bar user switcher chip is disabled
- when(mStatusBarUserChipViewModel.getChipEnabled()).thenReturn(false);
-
- // WHEN the controller is created
- mController = createController();
-
- // THEN keyguard status bar view avatar is enabled
- assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isTrue();
- }
-
- @Test
- public void testBlockedIcons_obeysSettingForVibrateIcon_settingOff() {
- String str = mContext.getString(com.android.internal.R.string.status_bar_volume);
-
- // GIVEN the setting is off
- when(mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
- .thenReturn(0);
-
- // WHEN CollapsedStatusBarFragment builds the blocklist
- mController.updateBlockedIcons();
-
- // THEN status_bar_volume SHOULD be present in the list
- boolean contains = mController.getBlockedIcons().contains(str);
- assertTrue(contains);
- }
-
- @Test
- public void testBlockedIcons_obeysSettingForVibrateIcon_settingOn() {
- String str = mContext.getString(com.android.internal.R.string.status_bar_volume);
-
- // GIVEN the setting is ON
- when(mSecureSettings.getIntForUser(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0,
- UserHandle.USER_CURRENT))
- .thenReturn(1);
-
- // WHEN CollapsedStatusBarFragment builds the blocklist
- mController.updateBlockedIcons();
-
- // THEN status_bar_volume SHOULD NOT be present in the list
- boolean contains = mController.getBlockedIcons().contains(str);
- assertFalse(contains);
- }
-
- private void updateStateToNotKeyguard() {
- updateStatusBarState(SHADE);
- }
-
- private void updateStateToKeyguard() {
- updateStatusBarState(KEYGUARD);
- }
-
- private void updateStatusBarState(int state) {
- ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
- ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
- verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
- StatusBarStateController.StateListener callback = statusBarStateListenerCaptor.getValue();
-
- callback.onStateChanged(state);
- }
-
- @Test
- @DisableSceneContainer
- public void animateKeyguardStatusBarIn_isDisabled_viewStillHidden() {
- mController.onViewAttached();
- updateStateToKeyguard();
- setDisableSystemInfo(true);
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
-
- mController.animateKeyguardStatusBarIn();
-
- // Since we're disabled, we don't actually animate in and stay invisible
- assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
- }
-
- /**
- * Calls {@link com.android.keyguard.KeyguardUpdateMonitorCallback#onFinishedGoingToSleep(int)}
- * to ensure values are updated properly.
- */
- private void onFinishedGoingToSleep() {
- ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateCallbackCaptor =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
- verify(mKeyguardUpdateMonitor).registerCallback(keyguardUpdateCallbackCaptor.capture());
- KeyguardUpdateMonitorCallback callback = keyguardUpdateCallbackCaptor.getValue();
-
- callback.onFinishedGoingToSleep(0);
- }
-
- private void setDisableSystemInfo(boolean disabled) {
- CommandQueue.Callbacks callback = getCommandQueueCallback();
- int disabled1 = disabled ? DISABLE_SYSTEM_INFO : 0;
- callback.disable(mContext.getDisplayId(), disabled1, 0, false);
- }
-
- private void setDisableSystemIcons(boolean disabled) {
- CommandQueue.Callbacks callback = getCommandQueueCallback();
- int disabled2 = disabled ? DISABLE2_SYSTEM_ICONS : 0;
- callback.disable(mContext.getDisplayId(), 0, disabled2, false);
- }
-
- private CommandQueue.Callbacks getCommandQueueCallback() {
- ArgumentCaptor<CommandQueue.Callbacks> captor =
- ArgumentCaptor.forClass(CommandQueue.Callbacks.class);
- verify(mCommandQueue).addCallback(captor.capture());
- return captor.getValue();
- }
-
- private void runAllScheduled() {
- mBackgroundExecutor.runAllReady();
- mFakeExecutor.runAllReady();
- }
-
- private static class TestShadeViewStateProvider
- implements ShadeViewStateProvider {
-
- TestShadeViewStateProvider() {}
-
- private float mPanelViewExpandedHeight = 100f;
- private boolean mShouldHeadsUpBeVisible = false;
- private float mLockscreenShadeDragProgress = 0f;
-
- @Override
- public float getPanelViewExpandedHeight() {
- return mPanelViewExpandedHeight;
- }
-
- @Override
- public boolean shouldHeadsUpBeVisible() {
- return mShouldHeadsUpBeVisible;
- }
-
- @Override
- public float getLockscreenShadeDragProgress() {
- return mLockscreenShadeDragProgress;
- }
-
- public void setPanelViewExpandedHeight(float panelViewExpandedHeight) {
- this.mPanelViewExpandedHeight = panelViewExpandedHeight;
- }
-
- public void setShouldHeadsUpBeVisible(boolean shouldHeadsUpBeVisible) {
- this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible;
- }
-
- public void setLockscreenShadeDragProgress(float lockscreenShadeDragProgress) {
- this.mLockscreenShadeDragProgress = lockscreenShadeDragProgress;
- }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
new file mode 100644
index 000000000000..b815c6ce0c51
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -0,0 +1,867 @@
+/*
+ * Copyright (C) 2021 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.statusbar.phone
+
+import android.app.StatusBarManager
+import android.graphics.Insets
+import android.os.UserHandle
+import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.CarrierTextController
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.keyguard.logging.KeyguardLogger
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeViewStateProvider
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.statusbar.ui.viewmodel.keyguardStatusBarViewModel
+import com.android.systemui.statusbar.ui.viewmodel.statusBarUserChipViewModel
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper(setAsMainLooper = true)
+class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
+ private lateinit var kosmos: Kosmos
+ private lateinit var testScope: TestScope
+
+ @Mock private lateinit var carrierTextController: CarrierTextController
+
+ @Mock private lateinit var configurationController: ConfigurationController
+
+ @Mock private lateinit var animationScheduler: SystemStatusAnimationScheduler
+
+ @Mock private lateinit var batteryController: BatteryController
+
+ @Mock private lateinit var userInfoController: UserInfoController
+
+ @Mock private lateinit var statusBarIconController: StatusBarIconController
+
+ @Mock private lateinit var iconManagerFactory: TintedIconManager.Factory
+
+ @Mock private lateinit var iconManager: TintedIconManager
+
+ @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
+
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+
+ @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
+
+ @Mock
+ private lateinit var statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore
+
+ @Mock private lateinit var userManager: UserManager
+
+ @Captor
+ private lateinit var configurationListenerCaptor:
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+
+ @Captor
+ private lateinit var keyguardCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+
+ @Mock private lateinit var secureSettings: SecureSettings
+
+ @Mock private lateinit var commandQueue: CommandQueue
+
+ @Mock private lateinit var logger: KeyguardLogger
+
+ @Mock private lateinit var statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory
+
+ private lateinit var shadeViewStateProvider: TestShadeViewStateProvider
+
+ private lateinit var keyguardStatusBarView: KeyguardStatusBarView
+ private lateinit var controller: KeyguardStatusBarViewController
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+ private val backgroundExecutor = FakeExecutor(FakeSystemClock())
+
+ private lateinit var looper: TestableLooper
+
+ @Before
+ @Throws(Exception::class)
+ fun setup() {
+ looper = TestableLooper.get(this)
+ kosmos = testKosmos()
+ testScope = kosmos.testScope
+ shadeViewStateProvider = TestShadeViewStateProvider()
+
+ Mockito.`when`(
+ kosmos.statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+ )
+ .thenReturn(Insets.of(0, 0, 0, 0))
+
+ MockitoAnnotations.initMocks(this)
+
+ Mockito.`when`(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(iconManager)
+ Mockito.`when`(statusBarContentInsetsProviderStore.defaultDisplay)
+ .thenReturn(kosmos.statusBarContentInsetsProvider)
+ allowTestableLooperAsMainThread()
+ looper.runWithLooper {
+ keyguardStatusBarView =
+ Mockito.spy(
+ LayoutInflater.from(mContext).inflate(R.layout.keyguard_status_bar, null)
+ as KeyguardStatusBarView
+ )
+ Mockito.`when`(keyguardStatusBarView.getDisplay()).thenReturn(mContext.display)
+ }
+
+ controller = createController()
+ }
+
+ private fun createController(): KeyguardStatusBarViewController {
+ return KeyguardStatusBarViewController(
+ kosmos.testDispatcher,
+ keyguardStatusBarView,
+ carrierTextController,
+ configurationController,
+ animationScheduler,
+ batteryController,
+ userInfoController,
+ statusBarIconController,
+ iconManagerFactory,
+ batteryMeterViewController,
+ shadeViewStateProvider,
+ keyguardStateController,
+ keyguardBypassController,
+ keyguardUpdateMonitor,
+ kosmos.keyguardStatusBarViewModel,
+ biometricUnlockController,
+ kosmos.statusBarStateController,
+ statusBarContentInsetsProviderStore,
+ userManager,
+ kosmos.statusBarUserChipViewModel,
+ secureSettings,
+ commandQueue,
+ fakeExecutor,
+ backgroundExecutor,
+ logger,
+ statusOverlayHoverListenerFactory,
+ kosmos.communalSceneInteractor,
+ kosmos.glanceableHubToLockscreenTransitionViewModel,
+ kosmos.lockscreenToGlanceableHubTransitionViewModel,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
+ fun onViewAttached_updateUserSwitcherFlagEnabled_callbacksRegistered() {
+ controller.onViewAttached()
+
+ runAllScheduled()
+ Mockito.verify(configurationController).addCallback(ArgumentMatchers.any())
+ Mockito.verify(animationScheduler).addCallback(ArgumentMatchers.any())
+ Mockito.verify(userInfoController).addCallback(ArgumentMatchers.any())
+ Mockito.verify(commandQueue).addCallback(ArgumentMatchers.any())
+ Mockito.verify(statusBarIconController).addIconGroup(ArgumentMatchers.any())
+ Mockito.verify(userManager).isUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
+ fun onViewAttached_updateUserSwitcherFlagDisabled_callbacksRegistered() {
+ controller.onViewAttached()
+
+ Mockito.verify(configurationController).addCallback(ArgumentMatchers.any())
+ Mockito.verify(animationScheduler).addCallback(ArgumentMatchers.any())
+ Mockito.verify(userInfoController).addCallback(ArgumentMatchers.any())
+ Mockito.verify(commandQueue).addCallback(ArgumentMatchers.any())
+ Mockito.verify(statusBarIconController).addIconGroup(ArgumentMatchers.any())
+ Mockito.verify(userManager).isUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
+ fun onConfigurationChanged_updateUserSwitcherFlagEnabled_updatesUserSwitcherVisibility() {
+ controller.onViewAttached()
+ runAllScheduled()
+ Mockito.verify(configurationController).addCallback(configurationListenerCaptor.capture())
+ Mockito.clearInvocations(userManager)
+ Mockito.clearInvocations(keyguardStatusBarView)
+
+ configurationListenerCaptor.value.onConfigChanged(null)
+
+ runAllScheduled()
+ Mockito.verify(userManager).isUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ Mockito.verify(keyguardStatusBarView).setUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
+ fun onConfigurationChanged_updateUserSwitcherFlagDisabled_updatesUserSwitcherVisibility() {
+ controller.onViewAttached()
+ Mockito.verify(configurationController).addCallback(configurationListenerCaptor.capture())
+ Mockito.clearInvocations(userManager)
+ Mockito.clearInvocations(keyguardStatusBarView)
+
+ configurationListenerCaptor.value.onConfigChanged(null)
+ Mockito.verify(userManager).isUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ Mockito.verify(keyguardStatusBarView).setUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
+ fun onKeyguardVisibilityChanged_userSwitcherFlagEnabled_updatesUserSwitcherVisibility() {
+ controller.onViewAttached()
+ runAllScheduled()
+ Mockito.verify(keyguardUpdateMonitor).registerCallback(keyguardCallbackCaptor.capture())
+ Mockito.clearInvocations(userManager)
+ Mockito.clearInvocations(keyguardStatusBarView)
+
+ keyguardCallbackCaptor.value.onKeyguardVisibilityChanged(true)
+
+ runAllScheduled()
+ Mockito.verify(userManager).isUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ Mockito.verify(keyguardStatusBarView).setUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_UPDATE_USER_SWITCHER_BACKGROUND)
+ fun onKeyguardVisibilityChanged_userSwitcherFlagDisabled_updatesUserSwitcherVisibility() {
+ controller.onViewAttached()
+ Mockito.verify(keyguardUpdateMonitor).registerCallback(keyguardCallbackCaptor.capture())
+ Mockito.clearInvocations(userManager)
+ Mockito.clearInvocations(keyguardStatusBarView)
+
+ keyguardCallbackCaptor.value.onKeyguardVisibilityChanged(true)
+ Mockito.verify(userManager).isUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ Mockito.verify(keyguardStatusBarView).setUserSwitcherEnabled(ArgumentMatchers.anyBoolean())
+ }
+
+ @Test
+ fun onViewDetached_callbacksUnregistered() {
+ // Set everything up first.
+ controller.onViewAttached()
+
+ controller.onViewDetached()
+
+ Mockito.verify(configurationController).removeCallback(ArgumentMatchers.any())
+ Mockito.verify(animationScheduler).removeCallback(ArgumentMatchers.any())
+ Mockito.verify(userInfoController).removeCallback(ArgumentMatchers.any())
+ Mockito.verify(commandQueue).removeCallback(ArgumentMatchers.any())
+ Mockito.verify(statusBarIconController).removeIconGroup(ArgumentMatchers.any())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun onViewReAttached_flagOff_iconManagerNotReRegistered() {
+ controller.onViewAttached()
+ controller.onViewDetached()
+ Mockito.reset(statusBarIconController)
+
+ controller.onViewAttached()
+
+ Mockito.verify(statusBarIconController, Mockito.never())
+ .addIconGroup(ArgumentMatchers.any())
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun onViewReAttached_flagOn_iconManagerReRegistered() {
+ controller.onViewAttached()
+ controller.onViewDetached()
+ Mockito.reset(statusBarIconController)
+
+ controller.onViewAttached()
+
+ Mockito.verify(statusBarIconController).addIconGroup(ArgumentMatchers.any())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun setBatteryListening_true_callbackAdded() {
+ controller.setBatteryListening(true)
+
+ Mockito.verify(batteryController).addCallback(ArgumentMatchers.any())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun setBatteryListening_false_callbackRemoved() {
+ // First set to true so that we know setting to false is a change in state.
+ controller.setBatteryListening(true)
+
+ controller.setBatteryListening(false)
+
+ Mockito.verify(batteryController).removeCallback(ArgumentMatchers.any())
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun setBatteryListening_trueThenTrue_callbackAddedOnce() {
+ controller.setBatteryListening(true)
+ controller.setBatteryListening(true)
+
+ Mockito.verify(batteryController).addCallback(ArgumentMatchers.any())
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun setBatteryListening_true_flagOn_callbackNotAdded() {
+ controller.setBatteryListening(true)
+
+ Mockito.verify(batteryController, Mockito.never()).addCallback(ArgumentMatchers.any())
+ }
+
+ @Test
+ fun updateTopClipping_viewClippingUpdated() {
+ val viewTop = 20
+ keyguardStatusBarView.top = viewTop
+ val notificationPanelTop = 30
+
+ controller.updateTopClipping(notificationPanelTop)
+
+ Truth.assertThat(keyguardStatusBarView.clipBounds.top)
+ .isEqualTo(notificationPanelTop - viewTop)
+ }
+
+ @Test
+ fun setNotTopClipping_viewClippingUpdatedToZero() {
+ // Start out with some amount of top clipping.
+ controller.updateTopClipping(50)
+ Truth.assertThat(keyguardStatusBarView.clipBounds.top).isGreaterThan(0)
+
+ controller.setNoTopClipping()
+
+ Truth.assertThat(keyguardStatusBarView.clipBounds.top).isEqualTo(0)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_alphaAndVisibilityGiven_viewUpdated() {
+ // Verify the initial values so we know the method triggers changes.
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(1f)
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+
+ val newAlpha = 0.5f
+ val newVisibility = View.INVISIBLE
+ controller.updateViewState(newAlpha, newVisibility)
+
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(newAlpha)
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(newVisibility)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_paramVisibleButIsDisabled_viewIsInvisible() {
+ controller.onViewAttached()
+ setDisableSystemIcons(true)
+
+ controller.updateViewState(1f, View.VISIBLE)
+
+ // Since we're disabled, we stay invisible
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_notKeyguardState_nothingUpdated() {
+ controller.onViewAttached()
+ updateStateToNotKeyguard()
+
+ val oldAlpha = keyguardStatusBarView.alpha
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(oldAlpha)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+
+ Mockito.`when`(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
+ Mockito.`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
+ onFinishedGoingToSleep()
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_bypassNotEnabled_viewShown() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ Mockito.`when`(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
+ Mockito.`when`(keyguardBypassController.bypassEnabled).thenReturn(false)
+ onFinishedGoingToSleep()
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_shouldNotListenForFace_viewShown() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ Mockito.`when`(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(false)
+ Mockito.`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
+ onFinishedGoingToSleep()
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_panelExpandedHeightZero_viewHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ shadeViewStateProvider.panelViewExpandedHeight = 0f
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_dragProgressOne_viewHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ shadeViewStateProvider.lockscreenShadeDragProgress = 1f
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_disableSystemInfoFalse_viewShown() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ setDisableSystemInfo(false)
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_disableSystemInfoTrue_viewHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ setDisableSystemInfo(true)
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_disableSystemIconsFalse_viewShown() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ setDisableSystemIcons(false)
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_disableSystemIconsTrue_viewHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ setDisableSystemIcons(true)
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_dozingTrue_flagOff_viewHidden() {
+ controller.init()
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ controller.setDozing(true)
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateViewState_dozingFalse_flagOff_viewShown() {
+ controller.init()
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ controller.setDozing(false)
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateViewState_flagOn_doesNothing() {
+ controller.init()
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ keyguardStatusBarView.visibility = View.GONE
+ keyguardStatusBarView.alpha = 0.456f
+
+ controller.updateViewState()
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.GONE)
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(0.456f)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateViewStateWithAlphaAndVis_flagOn_doesNothing() {
+ controller.init()
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ keyguardStatusBarView.visibility = View.GONE
+ keyguardStatusBarView.alpha = 0.456f
+
+ controller.updateViewState(0.789f, View.VISIBLE)
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.GONE)
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(0.456f)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun setAlpha_flagOn_doesNothing() {
+ controller.init()
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ keyguardStatusBarView.alpha = 0.456f
+
+ controller.setAlpha(0.123f)
+
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(0.456f)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun setDozing_flagOn_doesNothing() {
+ controller.init()
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+
+ controller.setDozing(true)
+ controller.updateViewState()
+
+ // setDozing(true) should typically cause the view to hide. But since the flag is on, we
+ // should ignore these set dozing calls and stay the same visibility.
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun setAlpha_explicitAlpha_setsExplicitAlpha() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ controller.setAlpha(0.5f)
+
+ Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(0.5f)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun setAlpha_explicitAlpha_thenMinusOneAlpha_setsAlphaBasedOnDefaultCriteria() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ controller.setAlpha(0.5f)
+ controller.setAlpha(-1f)
+
+ Truth.assertThat(keyguardStatusBarView.alpha).isGreaterThan(0)
+ Truth.assertThat(keyguardStatusBarView.alpha).isNotEqualTo(0.5f)
+ }
+
+ // TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized.
+ @Test
+ @DisableSceneContainer
+ fun updateForHeadsUp_headsUpShouldBeVisible_viewHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ keyguardStatusBarView.visibility = View.VISIBLE
+
+ shadeViewStateProvider.setShouldHeadsUpBeVisible(true)
+ controller.updateForHeadsUp(/* animate= */ false)
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun updateForHeadsUp_headsUpShouldNotBeVisible_viewShown() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+
+ // Start with the opposite state.
+ shadeViewStateProvider.setShouldHeadsUpBeVisible(true)
+ controller.updateForHeadsUp(/* animate= */ false)
+
+ shadeViewStateProvider.setShouldHeadsUpBeVisible(false)
+ controller.updateForHeadsUp(/* animate= */ false)
+
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testNewUserSwitcherDisablesAvatar_newUiOn() =
+ testScope.runTest {
+ // GIVEN the status bar user switcher chip is enabled
+ kosmos.fakeUserRepository.isStatusBarUserChipEnabled = true
+
+ // WHEN the controller is created
+ controller = createController()
+
+ // THEN keyguard status bar view avatar is disabled
+ Truth.assertThat(keyguardStatusBarView.isKeyguardUserAvatarEnabled).isFalse()
+ }
+
+ @Test
+ fun testNewUserSwitcherDisablesAvatar_newUiOff() {
+ // GIVEN the status bar user switcher chip is disabled
+ kosmos.fakeUserRepository.isStatusBarUserChipEnabled = false
+
+ // WHEN the controller is created
+ controller = createController()
+
+ // THEN keyguard status bar view avatar is enabled
+ Truth.assertThat(keyguardStatusBarView.isKeyguardUserAvatarEnabled).isTrue()
+ }
+
+ @Test
+ fun testBlockedIcons_obeysSettingForVibrateIcon_settingOff() {
+ val str = mContext.getString(com.android.internal.R.string.status_bar_volume)
+
+ // GIVEN the setting is off
+ Mockito.`when`(secureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
+ .thenReturn(0)
+
+ // WHEN CollapsedStatusBarFragment builds the blocklist
+ controller.updateBlockedIcons()
+
+ // THEN status_bar_volume SHOULD be present in the list
+ val contains = controller.blockedIcons.contains(str)
+ Assert.assertTrue(contains)
+ }
+
+ @Test
+ fun testBlockedIcons_obeysSettingForVibrateIcon_settingOn() {
+ val str = mContext.getString(com.android.internal.R.string.status_bar_volume)
+
+ // GIVEN the setting is ON
+ Mockito.`when`(
+ secureSettings.getIntForUser(
+ Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
+ 0,
+ UserHandle.USER_CURRENT,
+ )
+ )
+ .thenReturn(1)
+
+ // WHEN CollapsedStatusBarFragment builds the blocklist
+ controller.updateBlockedIcons()
+
+ // THEN status_bar_volume SHOULD NOT be present in the list
+ val contains = controller.blockedIcons.contains(str)
+ Assert.assertFalse(contains)
+ }
+
+ private fun updateStateToNotKeyguard() {
+ updateStatusBarState(StatusBarState.SHADE)
+ }
+
+ private fun updateStateToKeyguard() {
+ updateStatusBarState(StatusBarState.KEYGUARD)
+ }
+
+ private fun updateStatusBarState(state: Int) {
+ kosmos.statusBarStateController.setState(state)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun animateKeyguardStatusBarIn_isDisabled_viewStillHidden() {
+ controller.onViewAttached()
+ updateStateToKeyguard()
+ setDisableSystemInfo(true)
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+
+ controller.animateKeyguardStatusBarIn()
+
+ // Since we're disabled, we don't actually animate in and stay invisible
+ Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ fun animateToGlanceableHub_affectsAlpha() =
+ testScope.runTest {
+ controller.init()
+ val transitionAlphaAmount = .5f
+ ViewUtils.attachView(keyguardStatusBarView)
+ looper.processAllMessages()
+ updateStateToKeyguard()
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
+ runCurrent()
+ controller.updateCommunalAlphaTransition(transitionAlphaAmount)
+ Truth.assertThat(keyguardStatusBarView.getAlpha()).isEqualTo(transitionAlphaAmount)
+ }
+
+ @Test
+ fun animateToGlanceableHub_alphaResetOnCommunalNotShowing() =
+ testScope.runTest {
+ controller.init()
+ val transitionAlphaAmount = .5f
+ ViewUtils.attachView(keyguardStatusBarView)
+ looper.processAllMessages()
+ updateStateToKeyguard()
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
+ runCurrent()
+ controller.updateCommunalAlphaTransition(transitionAlphaAmount)
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Blank)
+ runCurrent()
+ Truth.assertThat(keyguardStatusBarView.getAlpha()).isNotEqualTo(transitionAlphaAmount)
+ }
+
+ /**
+ * Calls [com.android.keyguard.KeyguardUpdateMonitorCallback.onFinishedGoingToSleep] to ensure
+ * values are updated properly.
+ */
+ private fun onFinishedGoingToSleep() {
+ val keyguardUpdateCallbackCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+ Mockito.verify(keyguardUpdateMonitor)
+ .registerCallback(keyguardUpdateCallbackCaptor.capture())
+ val callback = keyguardUpdateCallbackCaptor.value
+
+ callback.onFinishedGoingToSleep(0)
+ }
+
+ private fun setDisableSystemInfo(disabled: Boolean) {
+ val callback = commandQueueCallback
+ val disabled1 = if (disabled) StatusBarManager.DISABLE_SYSTEM_INFO else 0
+ callback.disable(mContext.displayId, disabled1, 0, false)
+ }
+
+ private fun setDisableSystemIcons(disabled: Boolean) {
+ val callback = commandQueueCallback
+ val disabled2 = if (disabled) StatusBarManager.DISABLE2_SYSTEM_ICONS else 0
+ callback.disable(mContext.displayId, 0, disabled2, false)
+ }
+
+ private val commandQueueCallback: CommandQueue.Callbacks
+ get() {
+ val captor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
+ Mockito.verify(commandQueue).addCallback(captor.capture())
+ return captor.value
+ }
+
+ private fun runAllScheduled() {
+ backgroundExecutor.runAllReady()
+ fakeExecutor.runAllReady()
+ }
+
+ private class TestShadeViewStateProvider : ShadeViewStateProvider {
+ override var panelViewExpandedHeight: Float = 100f
+ private var mShouldHeadsUpBeVisible = false
+ override var lockscreenShadeDragProgress: Float = 0f
+
+ override fun shouldHeadsUpBeVisible(): Boolean {
+ return mShouldHeadsUpBeVisible
+ }
+
+ fun setShouldHeadsUpBeVisible(shouldHeadsUpBeVisible: Boolean) {
+ this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index 88ec18dd65f3..9099334b360c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -48,12 +48,10 @@ import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.data.model.StatusBarAppearance;
import com.android.systemui.statusbar.data.model.StatusBarMode;
-import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModePerDisplayRepository;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.util.kotlin.JavaAdapter;
import kotlinx.coroutines.test.TestScope;
@@ -81,8 +79,8 @@ public class LightBarControllerTest extends SysuiTestCase {
private SysuiDarkIconDispatcher mStatusBarIconController;
private LightBarController mLightBarController;
private final TestScope mTestScope = TestScopeProvider.getTestScope();
- private final FakeStatusBarModeRepository mStatusBarModeRepository =
- new FakeStatusBarModeRepository();
+ private final FakeStatusBarModePerDisplayRepository mStatusBarModeRepository =
+ new FakeStatusBarModePerDisplayRepository();
@Before
public void setup() {
@@ -92,15 +90,16 @@ public class LightBarControllerTest extends SysuiTestCase {
mLightBarTransitionsController = mock(LightBarTransitionsController.class);
when(mStatusBarIconController.getTransitionsController()).thenReturn(
mLightBarTransitionsController);
- mLightBarController = new LightBarController(
- mContext,
- new JavaAdapter(mTestScope),
+ mLightBarController = new LightBarControllerImpl(
+ mContext.getDisplayId(),
+ mTestScope,
mStatusBarIconController,
mock(BatteryController.class),
mock(NavigationModeController.class),
mStatusBarModeRepository,
mock(DumpManager.class),
- new FakeDisplayTracker(mContext));
+ mTestScope.getCoroutineContext(),
+ mock(BiometricUnlockController.class));
mLightBarController.start();
}
@@ -121,7 +120,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds)
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -142,7 +141,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(0 /* appearance */, secondBounds)
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -165,7 +164,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds)
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -190,7 +189,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, thirdBounds)
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -214,7 +213,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(0 /* appearance */, secondBounds)
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -231,7 +230,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -249,7 +248,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(0, new Rect(0, 0, 1, 1))
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -266,7 +265,7 @@ public class LightBarControllerTest extends SysuiTestCase {
new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1))
);
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -276,7 +275,7 @@ public class LightBarControllerTest extends SysuiTestCase {
reset(mStatusBarIconController);
// WHEN the same appearance regions but different status bar mode is sent
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.LIGHTS_OUT_TRANSPARENT,
STATUS_BAR_BOUNDS,
@@ -298,7 +297,7 @@ public class LightBarControllerTest extends SysuiTestCase {
/* start= */ new Rect(0, 0, 10, 10),
/* end= */ new Rect(0, 0, 20, 20));
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
startingBounds,
@@ -311,7 +310,7 @@ public class LightBarControllerTest extends SysuiTestCase {
BoundsPair newBounds = new BoundsPair(
/* start= */ new Rect(0, 0, 30, 30),
/* end= */ new Rect(0, 0, 40, 40));
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue(
+ mStatusBarModeRepository.getStatusBarAppearance().setValue(
new StatusBarAppearance(
StatusBarMode.TRANSPARENT,
newBounds,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index a862fdfeca22..778e8228ab9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.Display
import android.view.DisplayCutout
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,6 +31,7 @@ import com.android.systemui.SysUICutoutProvider
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -1051,7 +1054,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
}
@Test
- fun onMaxBoundsChanged_beforeStart_listenerNotNotified() {
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun onMaxBoundsChanged_beforeStart_flagEnabled_listenerNotNotified() {
// Start out with an existing configuration with bounds
configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
configurationController.onConfigurationChanged(configuration)
@@ -1083,7 +1087,41 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
}
@Test
- fun onDensityOrFontScaleChanged_beforeStart_listenerNotNotified() {
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun onMaxBoundsChanged_beforeStart_flagDisabled_listenerNotNotified() {
+ // Start out with an existing configuration with bounds
+ configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
+ configurationController.onConfigurationChanged(configuration)
+ val provider =
+ StatusBarContentInsetsProviderImpl(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>(),
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
+
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
+ }
+ provider.addCallback(listener)
+
+ // WHEN the config is updated with new bounds
+ // but provider is not started
+ configuration.windowConfiguration.setMaxBounds(0, 0, 456, 789)
+ configurationController.onConfigurationChanged(configuration)
+
+ // THEN the listener is notified
+ assertThat(listener.triggered).isTrue()
+ }
+
+ @Test
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun onDensityOrFontScaleChanged_beforeStart_flagEnabled_listenerNotNotified() {
configuration.densityDpi = 12
val provider =
StatusBarContentInsetsProviderImpl(
@@ -1112,6 +1150,36 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun onDensityOrFontScaleChanged_beforeStart_flagDisabled_listenerNotified() {
+ configuration.densityDpi = 12
+ val provider =
+ StatusBarContentInsetsProviderImpl(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>(),
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
+
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
+ }
+ provider.addCallback(listener)
+
+ // WHEN the config is updated, but the provider is not started
+ configuration.densityDpi = 20
+ configurationController.onConfigurationChanged(configuration)
+
+ // THEN the listener is notified
+ assertThat(listener.triggered).isTrue()
+ }
+
+ @Test
fun onDensityOrFontScaleChanged_afterStart_listenerNotified() {
configuration.densityDpi = 12
val provider =
@@ -1169,7 +1237,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
}
@Test
- fun onThemeChanged_beforeStart_listenerNotNotified() {
+ @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun onThemeChanged_beforeStart_flagEnabled_listenerNotNotified() {
val provider =
StatusBarContentInsetsProviderImpl(
contextMock,
@@ -1193,6 +1262,32 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
assertThat(listener.triggered).isFalse()
}
+ @Test
+ @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ fun onThemeChanged_beforeStart_flagDisabled_listenerNotified() {
+ val provider =
+ StatusBarContentInsetsProviderImpl(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>(),
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
+
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
+ }
+ provider.addCallback(listener)
+
+ configurationController.notifyThemeChanged()
+
+ assertThat(listener.triggered).isTrue()
+ }
+
private fun assertRects(
expected: Rect,
actual: Rect,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
index 11dd587a04ad..cae990769444 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
@@ -31,10 +31,13 @@ import android.widget.LinearLayout
import androidx.annotation.ColorInt
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.statusBarConfigurationControllerStore
+import com.android.systemui.statusbar.data.repository.sysUiDarkIconDispatcherStore
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -50,16 +53,18 @@ import org.mockito.Mockito.verify
@SmallTest
class StatusOverlayHoverListenerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private val viewOverlay = mock<ViewGroupOverlay>()
private val overlayCaptor = argumentCaptor<Drawable>()
- private val darkDispatcher = mock<SysuiDarkIconDispatcher>()
private val darkChange: MutableStateFlow<DarkChange> = MutableStateFlow(DarkChange.EMPTY)
+ private val darkDispatcher = kosmos.sysUiDarkIconDispatcherStore.forDisplay(context.displayId)
private val factory =
StatusOverlayHoverListenerFactory(
context.resources,
FakeConfigurationController(),
- darkDispatcher
+ kosmos.sysUiDarkIconDispatcherStore,
+ kosmos.statusBarConfigurationControllerStore,
)
private val view = TestableStatusContainer(context, viewOverlay)
@@ -186,7 +191,7 @@ class StatusOverlayHoverListenerTest : SysuiTestCase() {
/* action= */ action,
/* x= */ 0f,
/* y= */ 0f,
- /* metaState= */ 0
+ /* metaState= */ 0,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
index e0d9fac0eba5..110dec6c33aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
@@ -36,7 +36,6 @@ import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
@@ -58,7 +57,6 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -144,6 +142,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
controller.start()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
+ onTeardown { controller.tearDownChipView() }
val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture())
@@ -153,11 +152,6 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
.thenReturn(PROC_STATE_INVISIBLE)
}
- @After
- fun tearDown() {
- controller.tearDownChipView()
- }
-
@Test
fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() {
val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
@@ -224,7 +218,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
@@ -241,7 +235,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
@@ -257,7 +251,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
@@ -668,7 +662,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() {
private fun createCallNotifEntry(
callStyle: Notification.CallStyle,
- nullContentIntent: Boolean = false
+ nullContentIntent: Boolean = false,
): NotificationEntry {
val notificationEntryBuilder = NotificationEntryBuilder()
notificationEntryBuilder.modifyNotification(context).style = callStyle
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index a8bcfbcfc539..39a1c106cfcf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.telephony.CellSignalStrength
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
@@ -735,9 +736,10 @@ class MobileIconInteractorTest : SysuiTestCase() {
}
@EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
// See b/346904529 for more context
- fun satBasedIcon_doesNotInflateSignalStrength() =
+ fun satBasedIcon_doesNotInflateSignalStrength_flagOff() =
testScope.runTest {
val latest by collectLastValue(underTest.signalLevelIcon)
@@ -756,7 +758,75 @@ class MobileIconInteractorTest : SysuiTestCase() {
assertThat(latest!!.level).isEqualTo(4)
}
+ @EnableFlags(
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG,
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN,
+ )
+ @Test
+ // See b/346904529 for more context
+ fun satBasedIcon_doesNotInflateSignalStrength_flagOn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.signalLevelIcon)
+
+ // GIVEN a satellite connection
+ connectionRepository.isNonTerrestrial.value = true
+ // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
+ connectionRepository.inflateSignalStrength.value = true
+
+ connectionRepository.satelliteLevel.value = 4
+ assertThat(latest!!.level).isEqualTo(4)
+
+ connectionRepository.inflateSignalStrength.value = true
+ connectionRepository.primaryLevel.value = 4
+
+ // Icon level is unaffected
+ assertThat(latest!!.level).isEqualTo(4)
+ }
+
+ @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @Test
+ fun satBasedIcon_usesPrimaryLevel_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.signalLevelIcon)
+
+ // GIVEN a satellite connection
+ connectionRepository.isNonTerrestrial.value = true
+
+ // GIVEN primary level is set
+ connectionRepository.primaryLevel.value = 4
+ connectionRepository.satelliteLevel.value = 0
+
+ // THEN icon uses the primary level because the flag is off
+ assertThat(latest!!.level).isEqualTo(4)
+ }
+
+ @EnableFlags(
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG,
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN,
+ )
+ @Test
+ fun satBasedIcon_usesSatelliteLevel_flagOn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.signalLevelIcon)
+
+ // GIVEN a satellite connection
+ connectionRepository.isNonTerrestrial.value = true
+
+ // GIVEN satellite level is set
+ connectionRepository.satelliteLevel.value = 4
+ connectionRepository.primaryLevel.value = 0
+
+ // THEN icon uses the satellite level because the flag is on
+ assertThat(latest!!.level).isEqualTo(4)
+ }
+
+ /**
+ * Context (b/377518113), this test will not be needed after FLAG_CARRIER_ROAMING_NB_IOT_NTN is
+ * rolled out. The new API should report 0 automatically if not in service.
+ */
@EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
fun satBasedIcon_reportsLevelZeroWhenOutOfService() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 4c7cdfa7fb67..038722cd9608 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -151,7 +151,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
iconsInteractor.isForceHidden,
repository,
context,
- MobileIconCarrierIdOverridesFake()
+ MobileIconCarrierIdOverridesFake(),
)
createAndSetViewModel()
}
@@ -359,7 +359,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
connectionsRepository.mobileIsDefault.value = true
repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
@@ -406,7 +406,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
repository.setDataEnabled(true)
@@ -426,7 +426,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val initial =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
@@ -448,7 +448,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
repository.dataEnabled.value = true
var latest: Icon? = null
@@ -477,7 +477,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
assertThat(latest).isEqualTo(expected)
@@ -499,7 +499,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
assertThat(latest).isEqualTo(expected)
@@ -520,7 +520,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
assertThat(latest).isEqualTo(expected)
@@ -542,7 +542,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
connectionsRepository.defaultMobileIconGroup.value.dataType,
- ContentDescription.Resource(G.dataContentDescription)
+ ContentDescription.Resource(G.dataContentDescription),
)
assertThat(latest).isEqualTo(expected)
@@ -564,7 +564,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
val expected =
Icon.Resource(
THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription)
+ ContentDescription.Resource(THREE_G.dataContentDescription),
)
assertThat(latest).isEqualTo(expected)
@@ -621,10 +621,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this)
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = true,
- hasActivityOut = true,
- )
+ DataActivityModel(hasActivityIn = true, hasActivityOut = true)
assertThat(inVisible).isFalse()
assertThat(outVisible).isFalse()
@@ -654,10 +651,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = true,
- hasActivityOut = false,
- )
+ DataActivityModel(hasActivityIn = true, hasActivityOut = false)
yield()
@@ -666,20 +660,14 @@ class MobileIconViewModelTest : SysuiTestCase() {
assertThat(containerVisible).isTrue()
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = true,
- )
+ DataActivityModel(hasActivityIn = false, hasActivityOut = true)
assertThat(inVisible).isFalse()
assertThat(outVisible).isTrue()
assertThat(containerVisible).isTrue()
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = false,
- )
+ DataActivityModel(hasActivityIn = false, hasActivityOut = false)
assertThat(inVisible).isFalse()
assertThat(outVisible).isFalse()
@@ -709,10 +697,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = true,
- hasActivityOut = false,
- )
+ DataActivityModel(hasActivityIn = true, hasActivityOut = false)
yield()
@@ -721,20 +706,14 @@ class MobileIconViewModelTest : SysuiTestCase() {
assertThat(containerVisible).isTrue()
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = true,
- )
+ DataActivityModel(hasActivityIn = false, hasActivityOut = true)
assertThat(inVisible).isFalse()
assertThat(outVisible).isTrue()
assertThat(containerVisible).isTrue()
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = false,
- )
+ DataActivityModel(hasActivityIn = false, hasActivityOut = false)
assertThat(inVisible).isFalse()
assertThat(outVisible).isFalse()
@@ -824,10 +803,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
// sets the background on cellular
repository.hasPrioritizedNetworkCapabilities.value = true
repository.dataActivityDirection.value =
- DataActivityModel(
- hasActivityIn = true,
- hasActivityOut = true,
- )
+ DataActivityModel(hasActivityIn = true, hasActivityOut = true)
assertThat(roaming).isFalse()
assertThat(networkTypeIcon).isNull()
@@ -838,11 +814,13 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
- fun nonTerrestrial_usesSatelliteIcon() =
+ fun nonTerrestrial_usesSatelliteIcon_flagOff() =
testScope.runTest {
repository.isNonTerrestrial.value = true
repository.setAllLevels(0)
+ repository.satelliteLevel.value = 0
val latest by
collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
@@ -853,28 +831,66 @@ class MobileIconViewModelTest : SysuiTestCase() {
// 1-2 -> 1 bar
repository.setAllLevels(1)
+ repository.satelliteLevel.value = 1
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
repository.setAllLevels(2)
+ repository.satelliteLevel.value = 2
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
// 3-4 -> 2 bars
repository.setAllLevels(3)
+ repository.satelliteLevel.value = 3
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
repository.setAllLevels(4)
+ repository.satelliteLevel.value = 4
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+ }
+
+ @EnableFlags(
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG,
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN,
+ )
+ @Test
+ fun nonTerrestrial_usesSatelliteIcon_flagOn() =
+ testScope.runTest {
+ repository.isNonTerrestrial.value = true
+ repository.satelliteLevel.value = 0
+
+ val latest by
+ collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+
+ // Level 0 -> no connection
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+ // 1-2 -> 1 bar
+ repository.satelliteLevel.value = 1
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ repository.satelliteLevel.value = 2
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ // 3-4 -> 2 bars
+ repository.satelliteLevel.value = 3
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+ repository.satelliteLevel.value = 4
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
}
@EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
- fun satelliteIcon_ignoresInflateSignalStrength() =
+ fun satelliteIcon_ignoresInflateSignalStrength_flagOff() =
testScope.runTest {
// Note that this is the exact same test as above, but with inflateSignalStrength set to
// true we note that the level is unaffected by inflation
repository.inflateSignalStrength.value = true
repository.isNonTerrestrial.value = true
repository.setAllLevels(0)
+ repository.satelliteLevel.value = 0
val latest by
collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
@@ -885,16 +901,55 @@ class MobileIconViewModelTest : SysuiTestCase() {
// 1-2 -> 1 bar
repository.setAllLevels(1)
+ repository.satelliteLevel.value = 1
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
repository.setAllLevels(2)
+ repository.satelliteLevel.value = 2
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
// 3-4 -> 2 bars
repository.setAllLevels(3)
+ repository.satelliteLevel.value = 3
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
repository.setAllLevels(4)
+ repository.satelliteLevel.value = 4
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+ }
+
+ @EnableFlags(
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG,
+ com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN,
+ )
+ @Test
+ fun satelliteIcon_ignoresInflateSignalStrength_flagOn() =
+ testScope.runTest {
+ // Note that this is the exact same test as above, but with inflateSignalStrength set to
+ // true we note that the level is unaffected by inflation
+ repository.inflateSignalStrength.value = true
+ repository.isNonTerrestrial.value = true
+ repository.satelliteLevel.value = 0
+
+ val latest by
+ collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+
+ // Level 0 -> no connection
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+ // 1-2 -> 1 bar
+ repository.satelliteLevel.value = 1
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ repository.satelliteLevel.value = 2
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ // 3-4 -> 2 bars
+ repository.satelliteLevel.value = 3
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+ repository.satelliteLevel.value = 4
assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
index fc1ea227f1bd..19d5a16deabd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
@@ -66,6 +66,7 @@ class DeviceBasedSatelliteRepositorySwitcherTest : SysuiTestCase() {
logBuffer = FakeLogBuffer.Factory.create(),
verboseLogBuffer = FakeLogBuffer.Factory.create(),
systemClock,
+ context.resources,
)
private val demoDataSource =
mock<DemoDeviceBasedSatelliteDataSource>().also {
@@ -80,7 +81,11 @@ class DeviceBasedSatelliteRepositorySwitcherTest : SysuiTestCase() {
)
}
private val demoImpl =
- DemoDeviceBasedSatelliteRepository(demoDataSource, testScope.backgroundScope)
+ DemoDeviceBasedSatelliteRepository(
+ demoDataSource,
+ testScope.backgroundScope,
+ context.resources,
+ )
private val underTest =
DeviceBasedSatelliteRepositorySwitcher(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
index 87693891a281..a70881aedeb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
@@ -58,7 +58,12 @@ class DemoDeviceBasedSatelliteRepositoryTest : SysuiTestCase() {
whenever(it.satelliteEvents).thenReturn(fakeSatelliteEvents)
}
- underTest = DemoDeviceBasedSatelliteRepository(dataSource, testScope.backgroundScope)
+ underTest =
+ DemoDeviceBasedSatelliteRepository(
+ dataSource,
+ testScope.backgroundScope,
+ context.resources,
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
index 55460bd5b801..41fa9e7be81b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
@@ -28,4 +28,6 @@ class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository {
override val signalStrength = MutableStateFlow(0)
override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(false)
+
+ override var isOpportunisticSatelliteIconEnabled: Boolean = true
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index e7e496938033..509aa7ad67fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -172,6 +172,26 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
}
@Test
+ fun icon_null_allOosAndConfigIsFalse() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN config for opportunistic icon is false
+ repo.isOpportunisticSatelliteIconEnabled = false
+
+ // GIVEN all icons are OOS
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isInService.value = false
+ i1.isEmergencyOnly.value = false
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // THEN icon is null because it is not allowed
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun icon_null_isEmergencyOnly() =
testScope.runTest {
val latest by collectLastValue(underTest.icon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
index cdc7aa2dea2a..9888574071e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
@@ -32,6 +32,8 @@ class FakeHomeStatusBarViewBinder : HomeStatusBarViewBinder {
override fun bind(
view: View,
viewModel: HomeStatusBarViewModel,
+ systemEventChipAnimateIn: ((View) -> Unit)?,
+ systemEventChipAnimateOut: ((View) -> Unit)?,
listener: StatusBarVisibilityChangeListener,
) {
this.listener = listener
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 02c1540d3d8b..eef5753cef8a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import android.view.View
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,11 +54,14 @@ class FakeHomeStatusBarViewModel : HomeStatusBarViewModel {
)
)
- override val isSystemInfoVisible =
+ override val systemInfoCombinedVis =
MutableStateFlow(
- HomeStatusBarViewModel.VisibilityModel(
- visibility = View.GONE,
- shouldAnimateChange = false,
+ HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
+ HomeStatusBarViewModel.VisibilityModel(
+ visibility = View.GONE,
+ shouldAnimateChange = false,
+ ),
+ Idle,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index b3a73d82122f..c4d2569bba89 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -60,11 +60,16 @@ import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepositor
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
@@ -90,6 +95,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
private val activeNotificationListRepository = kosmos.activeNotificationListRepository
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository
+ private val systemStatusEventAnimationRepository = kosmos.systemStatusEventAnimationRepository
private lateinit var underTest: HomeStatusBarViewModel
@@ -546,25 +552,50 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
@Test
fun isSystemInfoVisible_allowedByDisableFlags_visible() =
testScope.runTest {
- val latest by collectLastValue(underTest.isSystemInfoVisible)
+ val latest by collectLastValue(underTest.systemInfoCombinedVis)
transitionKeyguardToGone()
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
- assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
}
@Test
fun isSystemInfoVisible_notAllowedByDisableFlags_gone() =
testScope.runTest {
- val latest by collectLastValue(underTest.isSystemInfoVisible)
+ val latest by collectLastValue(underTest.systemInfoCombinedVis)
transitionKeyguardToGone()
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE)
- assertThat(latest!!.visibility).isEqualTo(View.GONE)
+ assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun systemInfoCombineVis_animationsPassThrough() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.systemInfoCombinedVis)
+ transitionKeyguardToGone()
+
+ assertThat(latest!!.baseVisibility)
+ .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false))
+ assertThat(latest!!.animationState).isEqualTo(Idle)
+
+ // WHEN the animation state changes, but the visibility state doesn't change
+ systemStatusEventAnimationRepository.animationState.value = AnimatingIn
+
+ // THEN the visibility is the same
+ assertThat(latest!!.baseVisibility)
+ .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false))
+ // THEN the animation state updates
+ assertThat(latest!!.animationState).isEqualTo(AnimatingIn)
+
+ systemStatusEventAnimationRepository.animationState.value = AnimatingOut
+ assertThat(latest!!.baseVisibility)
+ .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false))
+ assertThat(latest!!.animationState).isEqualTo(AnimatingOut)
}
@Test
@@ -573,7 +604,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GONE,
@@ -583,7 +614,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -592,13 +623,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -607,7 +638,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
@@ -617,7 +648,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -626,13 +657,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer)
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -641,7 +672,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
@@ -651,7 +682,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -660,14 +691,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null)
assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -676,13 +707,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
transitionKeyguardToGone()
assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -691,14 +722,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
transitionKeyguardToGone()
kosmos.shadeTestUtil.setShadeExpansion(0f)
assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -707,13 +738,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
kosmos.sceneContainerRepository.snapToScene(Scenes.Gone)
assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -722,14 +753,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
transitionKeyguardToGone()
kosmos.shadeTestUtil.setShadeExpansion(1f)
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -738,14 +769,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
transitionKeyguardToGone()
kosmos.sceneContainerRepository.snapToScene(Scenes.Shade)
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -754,7 +785,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
// Secure camera is an occluding activity
keyguardTransitionRepository.sendTransitionSteps(
@@ -766,7 +797,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
@Test
@@ -775,7 +806,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
testScope.runTest {
val clockVisible by collectLastValue(underTest.isClockVisible)
val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
- val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+ val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
// Secure camera is an occluding activity
@@ -784,7 +815,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
- assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+ assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
}
private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index b5dbc3fe1b4d..33223aef11ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoMod
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
@@ -71,6 +72,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() {
private val demoModelFlow = MutableStateFlow<FakeWifiEventModel?>(null)
private val mainExecutor = FakeExecutor(FakeSystemClock())
+ private val userRepository = FakeUserRepository()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -82,10 +84,13 @@ class WifiRepositorySwitcherTest : SysuiTestCase() {
// Never start in demo mode
whenever(demoModeController.isInDemoMode).thenReturn(false)
- whenever(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(wifiPickerTracker)
+ whenever(wifiPickerTrackerFactory.create(any(), any(), any(), any()))
+ .thenReturn(wifiPickerTracker)
realImpl =
WifiRepositoryImpl(
+ mContext,
+ userRepository,
testScope.backgroundScope,
mainExecutor,
testDispatcher,
@@ -97,11 +102,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() {
whenever(demoModeWifiDataSource.wifiEvents).thenReturn(demoModelFlow)
- demoImpl =
- DemoWifiRepository(
- demoModeWifiDataSource,
- testScope.backgroundScope,
- )
+ demoImpl = DemoWifiRepository(demoModeWifiDataSource, testScope.backgroundScope)
underTest =
WifiRepositorySwitcher(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 53e033ef7c93..e7ca1dd3e4b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -51,6 +51,8 @@ import android.graphics.Color;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
@@ -561,6 +563,113 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
+ public void onWallpaperColorsChanged_homeWallpaper_shouldUpdateTheme() {
+ // Should ask for a new theme when the colors of the last applied wallpaper change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+
+ String jsonString =
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ + "\"android.theme.customization.color_index\":\"2\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(1);
+ // Set LOCK wallpaper as the last applied one to verify that theme is no longer based on
+ // latest wallpaper
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(2);
+
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings).putStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
+ anyInt());
+
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+ }
+
+
+
+ @Test
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
+ public void onWallpaperColorsChanged_homeWallpaperWithSameColor_shouldKeepThemeAndReapply() {
+ // Shouldn't ask for a new theme when the colors of the last applied wallpaper change
+ // with the same specified system palette one.
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(0xffa16b00), null);
+
+ String jsonString =
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ + "\"android.theme.customization.color_index\":\"2\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(1);
+ // Set LOCK wallpaper as the last applied one to verify that theme is no longer based on
+ // latest wallpaper
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(2);
+
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+ USER_SYSTEM);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings, never()).putString(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+
+ // Apply overlay by existing theme from secure setting
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
+ public void onWallpaperColorsChanged_lockWallpaper_shouldKeepTheme() {
+ // Should ask for a new theme when the colors of the last applied wallpaper change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+
+ String jsonString =
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ + "\"android.theme.customization.color_index\":\"2\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+ .thenReturn(1);
+ // Set LOCK wallpaper as the last applied one to verify that theme is no longer based on
+ // latest wallpaper
+ when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+ .thenReturn(2);
+
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK,
+ USER_SYSTEM);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings, never()).putString(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+
+ verify(mThemeOverlayApplier, never())
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
public void onWallpaperColorsChanged_resetThemeWhenFromLatestWallpaper() {
// Should ask for a new theme when the colors of the last applied wallpaper change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -594,6 +703,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
public void onWallpaperColorsChanged_keepThemeWhenFromLatestWallpaperAndSpecifiedColor() {
// Shouldn't ask for a new theme when the colors of the last applied wallpaper change
// with the same specified system palette one.
@@ -627,6 +737,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
public void onWallpaperColorsChanged_keepThemeIfNotLatestWallpaper() {
// Shouldn't ask for a new theme when the colors of the wallpaper that is not the last
// applied one change
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index f1015394d7b1..09be93de9f3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -107,9 +107,9 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
configurationController,
context,
testScope.backgroundScope,
- mock()
+ mock(),
)
- private val configurationInteractor = ConfigurationInteractor(configurationRepository)
+ private val configurationInteractor = ConfigurationInteractorImpl(configurationRepository)
private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider()
private val unfoldTransitionRepository =
UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
@@ -145,7 +145,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
testScope.backgroundScope,
displaySwitchLatencyLogger,
systemClock,
- deviceStateManager
+ deviceStateManager,
)
}
@@ -174,7 +174,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
DisplaySwitchLatencyEvent(
latencyMs = 250,
fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
- toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+ toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
)
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
@@ -200,7 +200,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
testScope.backgroundScope,
displaySwitchLatencyLogger,
systemClock,
- deviceStateManager
+ deviceStateManager,
)
areAnimationEnabled.emit(true)
@@ -226,7 +226,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
DisplaySwitchLatencyEvent(
latencyMs = 50,
fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
- toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+ toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
)
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
@@ -259,7 +259,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
DisplaySwitchLatencyEvent(
latencyMs = 50,
fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
- toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+ toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
)
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
@@ -289,7 +289,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
DisplaySwitchLatencyEvent(
latencyMs = 200,
fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
- toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED
+ toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
)
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
@@ -310,7 +310,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
lastWakefulnessEvent.emit(
WakefulnessModel(
internalWakefulnessState = WakefulnessState.ASLEEP,
- lastSleepReason = WakeSleepReason.FOLD
+ lastSleepReason = WakeSleepReason.FOLD,
)
)
screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
@@ -326,7 +326,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
latencyMs = 200,
fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
- toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+ toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD,
)
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
@@ -372,7 +372,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
lastWakefulnessEvent.emit(
WakefulnessModel(
internalWakefulnessState = WakefulnessState.ASLEEP,
- lastSleepReason = WakeSleepReason.FOLD
+ lastSleepReason = WakeSleepReason.FOLD,
)
)
screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
@@ -385,7 +385,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
latencyMs = 0,
fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
- toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
+ toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF,
)
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 9dcbe1b591e5..ff0321b8cf2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -99,6 +99,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
whenever(mainLooper.isCurrentThread).thenReturn(true)
whenever(mainLooper.thread).thenReturn(thread)
whenever(thread.name).thenReturn("backgroundThread")
+ whenever(context.applicationContext).thenReturn(context)
whenever(context.resources).thenReturn(resources)
whenever(context.mainExecutor).thenReturn(mContext.mainExecutor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index b03c679a9c23..8a4593032748 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -17,16 +17,20 @@
package com.android.systemui.user.data.repository
+import android.app.admin.devicePolicyManager
import android.content.pm.UserInfo
+import android.internal.statusbar.fakeStatusBarService
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectedUserModel
@@ -57,6 +61,9 @@ class UserRepositoryImplTest : SysuiTestCase() {
private val testDispatcher = kosmos.testDispatcher
private val testScope = kosmos.testScope
private val globalSettings = kosmos.fakeGlobalSettings
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+ private val devicePolicyManager = kosmos.devicePolicyManager
+ private val statusBarService = kosmos.fakeStatusBarService
@Mock private lateinit var manager: UserManager
@@ -68,6 +75,10 @@ class UserRepositoryImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
tracker = FakeUserTracker()
+ context.orCreateTestableResources.addOverride(
+ R.bool.config_userSwitchingMustGoThroughLoginScreen,
+ false,
+ )
}
@Test
@@ -317,6 +328,10 @@ class UserRepositoryImplTest : SysuiTestCase() {
backgroundDispatcher = testDispatcher,
globalSettings = globalSettings,
tracker = tracker,
+ broadcastDispatcher = broadcastDispatcher,
+ devicePolicyManager = devicePolicyManager,
+ resources = context.resources,
+ statusBarService = statusBarService,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt
new file mode 100644
index 000000000000..f70b42638cda
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.domain.interactor
+
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UserLogoutInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+ private val userRepository = kosmos.fakeUserRepository
+ private val testScope = kosmos.testScope
+
+ private val underTest = kosmos.userLogoutInteractor
+
+ @Before
+ fun setUp() {
+ userRepository.setUserInfos(USER_INFOS)
+ runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[2]) }
+ userRepository.setLogoutToSystemUserEnabled(false)
+ userRepository.setSecondaryUserLogoutEnabled(false)
+ }
+
+ @Test
+ fun logOut_doesNothing_whenBothLogoutOptionsAreDisabled() {
+ testScope.runTest {
+ val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
+ val secondaryUserLogoutCount = userRepository.logOutSecondaryUserCallCount
+ val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
+ assertThat(isLogoutEnabled).isFalse()
+ underTest.logOut()
+ assertThat(userRepository.logOutSecondaryUserCallCount)
+ .isEqualTo(secondaryUserLogoutCount)
+ assertThat(userRepository.logOutToSystemUserCallCount)
+ .isEqualTo(logoutToSystemUserCount)
+ }
+ }
+
+ @Test
+ fun logOut_logsOutSecondaryUser_whenAdminEnabledSecondaryLogout() {
+ testScope.runTest {
+ val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
+ val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
+ val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
+ userRepository.setSecondaryUserLogoutEnabled(true)
+ assertThat(isLogoutEnabled).isTrue()
+ underTest.logOut()
+ assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1)
+ assertThat(userRepository.logOutToSystemUserCallCount)
+ .isEqualTo(logoutToSystemUserCount)
+ }
+ }
+
+ @Test
+ fun logOut_logsOutToSystemUser_whenLogoutToSystemUserIsEnabled() {
+ testScope.runTest {
+ val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
+ val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
+ val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
+ userRepository.setLogoutToSystemUserEnabled(true)
+ assertThat(isLogoutEnabled).isTrue()
+ underTest.logOut()
+ assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount)
+ assertThat(userRepository.logOutToSystemUserCallCount)
+ .isEqualTo(logoutToSystemUserCount + 1)
+ }
+ }
+
+ @Test
+ fun logOut_secondaryUserTakesPrecedence() {
+ testScope.runTest {
+ val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
+ val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
+ val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
+ userRepository.setLogoutToSystemUserEnabled(true)
+ userRepository.setSecondaryUserLogoutEnabled(true)
+ assertThat(isLogoutEnabled).isTrue()
+ underTest.logOut()
+ assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1)
+ assertThat(userRepository.logOutToSystemUserCallCount)
+ .isEqualTo(logoutToSystemUserCount)
+ }
+ }
+
+ companion object {
+ private val USER_INFOS =
+ listOf(
+ UserInfo(0, "System user", 0),
+ UserInfo(10, "Regular user", 0),
+ UserInfo(11, "Secondary user", 0),
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index a0cfab4d2160..ad473c09cf02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -16,96 +16,16 @@
package com.android.systemui.util.settings.repository
-import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.fakeSettings
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
+class UserAwareSecureSettingsRepositoryTest : UserAwareSettingsRepositoryTestBase() {
- private val kosmos = testKosmos()
- private val dispatcher = kosmos.testDispatcher
- private val testScope = kosmos.testScope
- private val secureSettings = kosmos.fakeSettings
- private val userRepository = Kosmos().fakeUserRepository
- private lateinit var repository: UserAwareSecureSettingsRepository
-
- @Before
- fun setup() {
- repository =
- UserAwareSecureSettingsRepositoryImpl(
- secureSettings,
- userRepository,
- dispatcher,
- )
- userRepository.setUserInfos(USER_INFOS)
- setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
- setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
- }
-
- @Test
- fun settingEnabledEmitsValueForCurrentUser() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
-
- val enabled by collectLastValue(repository.boolSettingForActiveUser(SETTING_NAME))
-
- assertThat(enabled).isTrue()
- }
- }
-
- @Test
- fun settingEnabledEmitsNewValueWhenSettingChanges() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
- val enabled by collectValues(repository.boolSettingForActiveUser(SETTING_NAME))
- runCurrent()
-
- setSettingValueForUser(enabled = false, userInfo = SETTING_ENABLED_USER)
-
- assertThat(enabled).containsExactly(true, false).inOrder()
- }
- }
-
- @Test
- fun settingEnabledEmitsValueForNewUserWhenUserChanges() {
- testScope.runTest {
- userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
- val enabled by collectLastValue(repository.boolSettingForActiveUser(SETTING_NAME))
- runCurrent()
-
- userRepository.setSelectedUserInfo(SETTING_DISABLED_USER)
-
- assertThat(enabled).isFalse()
- }
- }
-
- private fun setSettingValueForUser(enabled: Boolean, userInfo: UserInfo) {
- secureSettings.putBoolForUser(SETTING_NAME, enabled, userInfo.id)
- }
-
- private companion object {
- const val SETTING_NAME = "SETTING_NAME"
- val SETTING_ENABLED_USER = UserInfo(/* id= */ 0, "user1", /* flags= */ 0)
- val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
- val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
+ override fun getKosmosUserAwareSettingsRepository(): UserAwareSettingsRepository {
+ return kosmos.userAwareSecureSettingsRepository
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepositoryTestBase.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepositoryTestBase.kt
new file mode 100644
index 000000000000..09db96f8ffb8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepositoryTestBase.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings.repository
+
+import android.content.pm.UserInfo
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+abstract class UserAwareSettingsRepositoryTestBase : SysuiTestCase() {
+
+ protected val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ protected val secureSettings = kosmos.fakeSettings
+ protected val userRepository = kosmos.fakeUserRepository
+ private lateinit var underTest: UserAwareSettingsRepository
+
+ @Before
+ fun setup() {
+ underTest = getKosmosUserAwareSettingsRepository()
+
+ userRepository.setUserInfos(USER_INFOS)
+
+ secureSettings.putBoolForUser(BOOL_SETTING_NAME, true, USER_1.id)
+ secureSettings.putBoolForUser(BOOL_SETTING_NAME, false, USER_2.id)
+ secureSettings.putIntForUser(INT_SETTING_NAME, 1337, USER_1.id)
+ secureSettings.putIntForUser(INT_SETTING_NAME, 818, USER_2.id)
+ }
+
+ abstract fun getKosmosUserAwareSettingsRepository(): UserAwareSettingsRepository
+
+ @Test
+ fun boolSetting_emitsInitialValue() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+
+ val enabled by collectLastValue(underTest.boolSetting(BOOL_SETTING_NAME, false))
+
+ assertThat(enabled).isTrue()
+ }
+ }
+
+ @Test
+ fun boolSetting_whenSettingChanges_emitsNewValue() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+ val enabled by collectValues(underTest.boolSetting(BOOL_SETTING_NAME, false))
+ runCurrent()
+
+ secureSettings.putBoolForUser(BOOL_SETTING_NAME, false, USER_1.id)
+
+ assertThat(enabled).containsExactly(true, false).inOrder()
+ }
+ }
+
+ @Test
+ fun boolSetting_whenWhenUserChanges_emitsNewValue() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+ val enabled by collectLastValue(underTest.boolSetting(BOOL_SETTING_NAME, false))
+ runCurrent()
+
+ userRepository.setSelectedUserInfo(USER_2)
+
+ assertThat(enabled).isFalse()
+ }
+ }
+
+ @Test
+ fun intSetting_emitsInitialValue() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+
+ val number by collectLastValue(underTest.intSetting(INT_SETTING_NAME, 0))
+
+ assertThat(number).isEqualTo(1337)
+ }
+ }
+
+ @Test
+ fun intSetting_whenSettingChanges_emitsNewValue() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+ val number by collectValues(underTest.intSetting(INT_SETTING_NAME, 0))
+ runCurrent()
+
+ secureSettings.putIntForUser(INT_SETTING_NAME, 1338, USER_1.id)
+
+ assertThat(number).containsExactly(1337, 1338).inOrder()
+ }
+ }
+
+ @Test
+ fun intSetting_whenWhenUserChanges_emitsNewValue() {
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+ val number by collectLastValue(underTest.intSetting(INT_SETTING_NAME, 0))
+ runCurrent()
+
+ userRepository.setSelectedUserInfo(USER_2)
+
+ assertThat(number).isEqualTo(818)
+ }
+ }
+
+ @Test
+ fun getInt_returnsInitialValue() =
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+
+ assertThat(underTest.getInt(INT_SETTING_NAME, 0)).isEqualTo(1337)
+ }
+
+ @Test
+ fun getInt_whenSettingChanges_returnsNewValue() =
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_1)
+ secureSettings.putIntForUser(INT_SETTING_NAME, 999, USER_1.id)
+
+ assertThat(underTest.getInt(INT_SETTING_NAME, 0)).isEqualTo(999)
+ }
+
+ @Test
+ fun getInt_whenUserChanges_returnsThatUserValue() =
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(USER_2)
+
+ assertThat(underTest.getInt(INT_SETTING_NAME, 0)).isEqualTo(818)
+ }
+
+ private companion object {
+ const val BOOL_SETTING_NAME = "BOOL_SETTING_NAME"
+ const val INT_SETTING_NAME = "INT_SETTING_NAME"
+ val USER_1 = UserInfo(/* id= */ 0, "user1", /* flags= */ 0)
+ val USER_2 = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
+ val USER_INFOS = listOf(USER_1, USER_2)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepositoryTest.kt
new file mode 100644
index 000000000000..586da8e541f6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepositoryTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.util.settings.data.repository.userAwareSystemSettingsRepository
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UserAwareSystemSettingsRepositoryTest : UserAwareSettingsRepositoryTestBase() {
+
+ override fun getKosmosUserAwareSettingsRepository(): UserAwareSettingsRepository {
+ return kosmos.userAwareSystemSettingsRepository
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
index 403c7c500426..43185fd08613 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
@@ -36,6 +36,9 @@ import java.util.Collection;
public interface DarkIconDispatcher {
int VERSION = 2;
+ /** Called when work should stop and resources should be cleaned up. */
+ default void stop() {}
+
/**
* Sets the dark area so {@link #applyDark} only affects the icons in the specified area.
*
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 7d55169e048a..89da46544f1e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -13,11 +13,20 @@
*/
package com.android.systemui.plugins.clocks
+import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.Drawable
+import android.util.DisplayMetrics
import android.view.View
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.internal.annotations.Keep
+import com.android.internal.policy.SystemBarUtils
import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.Plugin
import com.android.systemui.plugins.annotations.GeneratedImport
@@ -149,7 +158,7 @@ interface ClockFaceLayout {
@ProtectedReturn("return constraints;")
/** Custom constraints to apply to preview ConstraintLayout. */
- fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet
+ fun applyPreviewConstraints(context: Context, constraints: ConstraintSet): ConstraintSet
fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel)
}
@@ -169,13 +178,84 @@ class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
return constraints
}
- override fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet {
- return constraints
+ override fun applyPreviewConstraints(
+ context: Context,
+ constraints: ConstraintSet,
+ ): ConstraintSet {
+ return applyDefaultPreviewConstraints(context, constraints)
}
override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) {
// Default clock doesn't need detailed control of view
}
+
+ companion object {
+ fun applyDefaultPreviewConstraints(
+ context: Context,
+ constraints: ConstraintSet,
+ ): ConstraintSet {
+ constraints.apply {
+ val lockscreenClockViewLargeId = getId(context, "lockscreen_clock_view_large")
+ constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT)
+ constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT)
+ constrainMaxHeight(lockscreenClockViewLargeId, 0)
+
+ val largeClockTopMargin =
+ SystemBarUtils.getStatusBarHeight(context) +
+ getDimen(context, "small_clock_padding_top") +
+ getDimen(context, "keyguard_smartspace_top_offset") +
+ getDimen(context, "date_weather_view_height") +
+ getDimen(context, "enhanced_smartspace_height")
+ connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin)
+ connect(lockscreenClockViewLargeId, START, PARENT_ID, START)
+ connect(lockscreenClockViewLargeId, END, PARENT_ID, END)
+
+ // In preview, we'll show UDFPS icon for UDFPS devices
+ // and nothing for non-UDFPS devices,
+ // and we're not planning to add this vide in clockHostView
+ // so we only need position of device entry icon to constrain clock
+ // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
+ val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom")
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+ val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
+
+ connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin)
+ val smallClockViewId = getId(context, "lockscreen_clock_view")
+ constrainWidth(smallClockViewId, WRAP_CONTENT)
+ constrainHeight(smallClockViewId, getDimen(context, "small_clock_height"))
+ connect(
+ smallClockViewId,
+ START,
+ PARENT_ID,
+ START,
+ getDimen(context, "clock_padding_start") +
+ getDimen(context, "status_view_margin_horizontal"),
+ )
+ val smallClockTopMargin =
+ getDimen(context, "keyguard_clock_top_margin") +
+ SystemBarUtils.getStatusBarHeight(context)
+ connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin)
+ }
+ return constraints
+ }
+
+ fun getId(context: Context, name: String): Int {
+ val packageName = context.packageName
+ val res = context.packageManager.getResourcesForApplication(packageName)
+ val id = res.getIdentifier(name, "id", packageName)
+ return id
+ }
+
+ fun getDimen(context: Context, name: String): Int {
+ val packageName = context.packageName
+ val res = context.packageManager.getResourcesForApplication(packageName)
+ val id = res.getIdentifier(name, "dimen", packageName)
+ return if (id == 0) 0 else res.getDimensionPixelSize(id)
+ }
+ }
}
/** Events that should call when various rendering parameters change */
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index bb3da3ac26e6..ef629961c988 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -49,7 +49,7 @@
<string name="disable_carrier_button_text" msgid="7153361131709275746">"eSIM ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="error_disable_esim_title" msgid="3802652622784813119">"eSIMକୁ ଅକ୍ଷମ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"ଗୋଟିଏ ତ୍ରୁଟି କାରଣରୁ eSIMକୁ ଅକ୍ଷମ କରାଯାଇପାରିବ ନାହିଁ।"</string>
- <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"ଏଣ୍ଟର୍"</string>
+ <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"ଏଣ୍ଟର"</string>
<string name="kg_wrong_pattern" msgid="5907301342430102842">"ଭୁଲ ପାଟର୍ନ"</string>
<string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"ଭୁଲ ପାଟର୍ନ। ପୁଣିଚେଷ୍ଟା କର।"</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"ଭୁଲ ପାସ୍‌ୱର୍ଡ"</string>
diff --git a/packages/SystemUI/res/drawable/ic_qs_notes.xml b/packages/SystemUI/res/drawable/ic_qs_notes.xml
new file mode 100644
index 000000000000..6c1d2e49369c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_notes.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the Lice/packages/SystemUI/res/drawable/ic_qs_notes.xmlnse.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white" android:pathData="M499,673L834,338Q834,338 834,338Q834,338 834,338L782,286Q782,286 782,286Q782,286 782,286L447,621L499,673ZM238,760Q138,755 89,718Q40,681 40,611Q40,546 93.5,505.5Q147,465 242,457Q281,454 300.5,444.5Q320,435 320,418Q320,392 290.5,379Q261,366 193,360L200,280Q303,288 351.5,321.5Q400,355 400,418Q400,471 361.5,501Q323,531 248,537Q184,542 152,560.5Q120,579 120,611Q120,646 148,661.5Q176,677 242,680L238,760ZM518,767L353,602L735,220Q755,200 782.5,200Q810,200 830,220L900,290Q920,310 920,337.5Q920,365 900,385L518,767ZM359,800Q342,804 329,791Q316,778 320,761L353,602L518,767L359,800Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 65005f840598..572f063c20c4 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -32,6 +32,34 @@
android:id="@+id/min_edge_guideline"
app:layout_constraintGuide_begin="@dimen/overlay_action_container_minimum_edge_spacing"
android:orientation="vertical"/>
+ <!-- This toast-like indication layout was forked from text_toast.xml and will have the same
+ appearance as system toast. -->
+ <FrameLayout
+ android:id="@+id/indication_container"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:background="@android:drawable/toast_frame"
+ android:elevation="@*android:dimen/toast_elevation"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+ <TextView
+ android:id="@+id/indication_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:textAppearance="@*android:style/TextAppearance.Toast"/>
+ </FrameLayout>
<!-- Negative horizontal margin because this container background must render beyond the thing
it's constrained by (the actions themselves). -->
<FrameLayout
@@ -47,7 +75,7 @@
app:layout_constraintStart_toStartOf="@id/min_edge_guideline"
app:layout_constraintTop_toTopOf="@id/actions_container"
app:layout_constraintEnd_toEndOf="@id/actions_container"
- app:layout_constraintBottom_toBottomOf="parent"/>
+ app:layout_constraintBottom_toTopOf="@id/indication_container"/>
<HorizontalScrollView
android:id="@+id/actions_container"
android:layout_width="0dp"
@@ -144,7 +172,7 @@
android:visibility="gone"
android:elevation="7dp"
android:padding="8dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/indication_container"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
deleted file mode 100644
index dc560bf2fab7..000000000000
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- Scrollview is necessary to fit everything in landscape layout -->
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_side_padding"
- android:paddingEnd="@dimen/dialog_side_padding"
- android:paddingTop="@dimen/dialog_top_padding"
- android:paddingBottom="@dimen/dialog_bottom_padding"
- android:orientation="vertical">
-
- <!-- Header -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center">
- <ImageView
- android:layout_width="@dimen/screenrecord_logo_size"
- android:layout_height="@dimen/screenrecord_logo_size"
- android:src="@drawable/ic_screenrecord"
- android:tint="@color/screenrecord_icon_color"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Dialog.Title"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:text="@string/screenrecord_permission_dialog_title"
- android:layout_marginTop="22dp"
- android:layout_marginBottom="15dp"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
- android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
- android:gravity="center"
- android:layout_marginBottom="20dp"/>
-
- <!-- Options -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="@dimen/screenrecord_option_icon_size"
- android:layout_height="@dimen/screenrecord_option_icon_size"
- android:src="@drawable/ic_mic_26dp"
- android:tint="?android:attr/textColorSecondary"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:layout_marginEnd="@dimen/screenrecord_option_padding"/>
- <Spinner
- android:id="@+id/screen_recording_options"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:layout_weight="1"
- android:popupBackground="@drawable/screenrecord_spinner_background"
- android:textColor="?androidprv:attr/materialColorOnSurface"
- android:dropDownWidth="274dp"
- android:prompt="@string/screenrecord_audio_label"/>
- <Switch
- android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_height="48dp"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:contentDescription="@string/screenrecord_audio_label"
- android:id="@+id/screenrecord_audio_switch"
- style="@style/ScreenRecord.Switch"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/screenrecord_option_padding">
- <ImageView
- android:layout_width="@dimen/screenrecord_option_icon_size"
- android:layout_height="@dimen/screenrecord_option_icon_size"
- android:layout_weight="0"
- android:src="@drawable/ic_touch"
- android:tint="?android:attr/textColorSecondary"
- android:layout_gravity="center"
- android:layout_marginEnd="@dimen/screenrecord_option_padding"/>
- <TextView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:layout_weight="1"
- android:layout_gravity="fill_vertical"
- android:gravity="center_vertical"
- android:text="@string/screenrecord_taps_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?androidprv:attr/materialColorOnSurface"
- android:importantForAccessibility="no"/>
- <Switch
- android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_height="48dp"
- android:layout_weight="0"
- android:id="@+id/screenrecord_taps_switch"
- android:contentDescription="@string/screenrecord_taps_label"
- style="@style/ScreenRecord.Switch"/>
- </LinearLayout>
- </LinearLayout>
-
- <!-- Buttons -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="36dp">
- <TextView
- android:id="@+id/button_cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_gravity="start"
- android:text="@string/cancel"
- style="@style/Widget.Dialog.Button.BorderButton" />
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- <TextView
- android:id="@+id/button_start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:text="@string/screenrecord_continue"
- style="@style/Widget.Dialog.Button" />
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
deleted file mode 100644
index 8198f0333a94..000000000000
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<!-- Extends LinearLayout -->
-<com.android.systemui.statusbar.policy.SplitClockView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <TextClock
- android:id="@+id/time_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:textSize="@dimen/qs_time_collapsed_size"
- android:textColor="?android:attr/textColorPrimary"
- />
- <TextClock
- android:id="@+id/am_pm_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:textSize="@dimen/qs_time_collapsed_size"
- android:textColor="?android:attr/textColorPrimary"
- android:importantForAccessibility="no"
- />
-
- <!-- Empty text view so we have the same height when expanded/collapsed-->
- <TextView
- android:id="@+id/empty_time_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:textColor="?android:attr/textColorPrimary"
- />
-</com.android.systemui.statusbar.policy.SplitClockView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index e90f055f2538..f187ce62ddb2 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,50 +13,58 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
+ android:id="@+id/volume_dialog_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
+ android:id="@+id/volume_dialog_container"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
+ android:showDividers="middle|end|beginning">
- <LinearLayout
- android:id="@+id/volume_dialog"
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:showDividers="middle">
-
- <include layout="@layout/volume_ringer_drawer" />
-
- <include layout="@layout/volume_dialog_slider" />
-
- <ImageButton
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
+ <LinearLayout
+ android:id="@+id/volume_dialog_floating_sliders_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+ android:gravity="bottom"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
+ android:showDividers="middle" />
+
+ <LinearLayout
+ android:id="@+id/volume_dialog"
+ android:layout_width="@dimen/volume_dialog_width"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_dialog_background"
+ android:clipChildren="false"
+ android:clipToOutline="false"
+ android:clipToPadding="false"
+ android:divider="@drawable/volume_dialog_spacer"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:paddingVertical="@dimen/volume_dialog_vertical_padding"
+ android:showDividers="middle">
+
+ <include layout="@layout/volume_ringer_drawer" />
+
+ <include layout="@layout/volume_dialog_slider" />
+
+ <ImageButton
+ android:id="@+id/volume_dialog_settings"
+ android:layout_width="@dimen/volume_dialog_button_size"
+ android:layout_height="@dimen/volume_dialog_button_size"
+ android:background="@drawable/ripple_drawable_20dp"
+ android:contentDescription="@string/accessibility_volume_settings"
+ android:soundEffectsEnabled="false"
+ android:src="@drawable/horizontal_ellipsis"
+ android:tint="?androidprv:attr/materialColorPrimary" />
+ </LinearLayout>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 9b3af52e9704..7c266e60b503 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -65,7 +65,7 @@
android:background="@drawable/volume_drawer_selection_bg"
android:contentDescription="@string/volume_ringer_change"
android:gravity="center"
- android:padding="10dp"
+ android:padding="@dimen/volume_dialog_ringer_horizontal_padding"
android:src="@drawable/ic_volume_media"
android:tint="?androidprv:attr/materialColorOnPrimary" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index c091cbf6c062..048ddaf735da 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Neem jou skerm op?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Neem een app op"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Neem hele skerm op"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Neem hele skerm op: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wanneer jy jou hele skerm opneem, word enigiets wat op jou skerm wys, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wanneer jy ’n app opneem, word enigiets wat in daardie app gewys of gespeel word, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Neem skerm op"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jy neem tans <xliff:g id="APP_NAME">%1$s</xliff:g> op"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop opname"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deel tans skerm"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Inhoud word gedeel"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hou op om skerm te deel?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Hou op deel?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jy deel tans jou hele skerm met <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jy deel tans jou hele skerm met ’n app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jy deel tans <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jy deel tans ’n app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Jy deel tans met ’n app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hou op deel"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skerm word tans uitgesaai"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hou op uitsaai?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Gehoortoestelle"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Skakel tans aan …"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outodraai"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorafstelling"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Intydse Onderskrifte"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokkeer toestelmikrofoon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokkeer toestelkamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokkeer toestelkamera en mikrofoon?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Sluitskermlegstukke"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Enigiemand kan legstukke op jou sluitskerm sien, selfs al is jou tablet gesluit."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ontkies legstuk"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Verminder hoogte"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Vermeerder hoogte"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sluitskermlegstukke"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vee alles uit"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Bestuur"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geskiedenis"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Kennisgewinginstellings"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Kennisgewinggeskiedenis"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nuut"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Stil"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Kennisgewings"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Begin nou"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Geen kennisgewings nie"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nuwe kennisgewings nie"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Kennisgewingdemping is aan"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jou toestelvolume en -waarskuwings word outomaties vir tot 2 minute lank verminder wanneer jy te veel kennisgewings op een slag kry."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Skakel af"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontsluit om ouer kennisgewings te sien"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vasgestel"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Kopnasporing"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om luiermodus te verander"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"luiermodus"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skakel oor na app regs of onder terwyl jy verdeelde skerm gebruik"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skakel oor na app links of bo terwyl jy verdeelde skerm gebruik"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tydens verdeelde skerm: verplaas ’n app van een skerm na ’n ander"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skakel oor na volgende taal"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skakel oor na vorige taal"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pasmaak kortpadsleutels"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen soekresultate nie"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pasmaak"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeer met jou sleutelbord"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer kortpadsleutels"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeer met jou raakpaneel"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Verskaf deur apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Vertoon"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Stel teëls terug"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Stel teëls terug na hul oorspronklike volgorde en groottes?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Stel alle teëls terug?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Kitsinstellingsteëls sal na die toestel se oorspronklike instellings teruggestel word"</string>
</resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 1b4781d24ebc..4afae3328cf7 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Af"</item>
<item msgid="3028994095749238254">"Aan"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index af2971b2ec81..9d03a911b9d3 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ማያ ገፅዎን ይቀዳሉ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"አንድ መተግበሪያ ቅዳ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"መላው ማያ ገፅን ቅረጽ"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ሙሉ ማያ ገፅን ቅዳ፦ %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"መላው ማያ ገፅዎን በሚቀዱበት ጊዜ፣ በማያ ገፅዎ ላይ የሚታየው ማንኛውም ነገር ይቀዳል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"መተግበሪያን ሲቀዱ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ይቀዳል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ማያ ገፅን ቅረጽ"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"በአሁኑ ጊዜ <xliff:g id="APP_NAME">%1$s</xliff:g> በመቅዳት ላይ ነዎት"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"መቅረጽ አቁም"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ማያ ገፅን በማጋራት ላይ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"ይዘት በማጋራት ላይ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ማያ ገፅን ማጋራት ይቁም?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"ማጋራት ይቁም?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"በአሁኑ ጊዜ ሙሉ ማያ ገፅዎን ከ<xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ጋር በማጋራት ላይ ነዎት"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"በአሁኑ ጊዜ መሉ ማያ ገፅዎን ከመተግበሪያ ጋር በማጋራት ላይ ነዎት"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"በአሁኑ ጊዜ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> በማጋራት ላይ ነዎት"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"በአሁኑ ጊዜ መተግበሪያ በማጋራት ላይ ነዎት"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"በአሁኑ ጊዜ በመተግበሪያ በማጋራት ላይ ነዎት"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ማጋራት አቁም"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ማያ ገፅን cast በማድረግ ላይ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"cast ማድረግ ይቁም?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ግቤት"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"መስሚያ አጋዥ መሣሪያዎች"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"በማብራት ላይ..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"የቀጥታ መግለጫ ጽሑፍ"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"የእርስዎ ጡባዊ ቁልፍ ተቆልፎ ቢሆን እንኳን ማንኛውም ሰው በማያ ገፅ ቁልፍዎ ላይ ምግብሮችን ማየት ይችላል።"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ምግብር አትምረጥ"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ቁመት ቀንስ"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ቁመት ጨምር"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ሁሉንም አጽዳ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ያቀናብሩ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ታሪክ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"የማሳወቂያ ቅንብሮች"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"የማሳወቂያ ታሪክ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"አዲስ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ጸጥ ያለ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ማሳወቂያዎች"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"አሁን ጀምር"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ምንም ማሳወቂያ የለም"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ምንም አዲስ ማሳወቂያዎች የሉም"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"የማሳወቂያ ረጋ ማለት በርቷል"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"በአንድ ጊዜ ብዙ ማሳወቂያዎችን ሲያገኙ የመሣሪያዎ ድምፅ እና ማንቂያዎች እስከ 2 ደቂቃዎች ድረስ በራስ-ሰር ይቀንሳሉ።"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"አጥፋ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"የቆዩ ማሳወቂያዎችን ለማየት ይክፈቱ"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ቋሚ"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"የጭንቅላት ክትትል"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"የደዋይ ሁነታን ለመቀየር መታ ያድርጉ"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ደዋይ ሁነታ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከታች ወዳለ መተግበሪያ ይቀይሩ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከላይ ወዳለ መተግበሪያ ይቀይሩ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"በተከፈለ ማያ ገጽ ወቅት፡- መተግበሪያን ከአንዱ ወደ ሌላው ተካ"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ግቤት"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ወደ ቀጣዩ ቋንቋ ቀይር"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ወደ ቀዳሚ ቋንቋ ቀይር"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"የአሁን መተግበሪያ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ተደራሽነት"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"የቁልፍ ሰሌዳ አቋራጮችን ያብጁ"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ምንም የፍለጋ ውጤቶች የሉም"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"አብጅ"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ተከናውኗል"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"የቁልፍ ሰሌዳ ቅንብሮች"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"የቁልፍ ሰሌዳዎን በመጠቀም ያስሱ"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"የቁልፍ ሰሌዳ አቋራጮችን ይወቁ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"የመዳሰሻ ሰሌዳዎን በመጠቀም ያስሱ"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"በመተግበሪያዎች የቀረበ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ማሳያ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ያልታወቀ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ሰቆችን ዳግም ያስጀምሩ"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ሰቆችን ወደ የመጀመሪያው ቅደም ተከተል እና መጠኖቻቸው ይመለሱ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ሁሉም ሰቆች ዳግም ይጀምሩ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ሁሉም የፈጣን ቅንብሮች ሰቆች ወደ የመሣሪያው የመጀመሪያ ቅንብሮች ዳግም ይጀምራሉ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index a3c590c33a6f..8601132cf141 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"አጥፋ"</item>
<item msgid="3028994095749238254">"አብራ"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 4ebac5a86ec1..062ab78d1b31 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"هل تريد تسجيل محتوى الشاشة؟"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"تسجيل محتوى تطبيق واحد"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"تسجيل محتوى الشاشة بالكامل"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"‏تسجيل محتوى الشاشة بالكامل: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"أثناء تسجيل محتوى الشاشة بالكامل، يتم تسجيل كل المحتوى المعروض على شاشتك. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"أثناء تسجيل محتوى تطبيق، يتم تسجيل أي محتوى يتم عرضه أو تشغيله في ذلك التطبيق. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"تسجيل محتوى الشاشة"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"يتم حاليًا تسجيل محتوى \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"إيقاف التسجيل"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"جارِ مشاركة محتوى الشاشة"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"مشاركة المحتوى"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"هل تريد إيقاف مشاركة الشاشة؟"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"هل تريد إيقاف المشاركة؟"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"تتم حاليًا مشاركة محتوى الشاشة بأكمله مع \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"تتم حاليًا مشاركة محتوى الشاشة بأكمله مع تطبيق"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"تتم حاليًا مشاركة محتوى \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"تتم حاليًا مشاركة محتوى تطبيق"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"تتم حاليًا مشاركة المحتوى مع تطبيق"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"إيقاف المشاركة"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"جارٍ بث محتوى الشاشة"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"هل تريد إيقاف البث؟"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"الإدخال"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعات الأذن الطبية"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"جارٍ التفعيل…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"تعذَّر تعديل الإعداد المسبق"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"الإعدادات المسبقة"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"النسخ النصي التلقائي"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"هل تريد إزالة حظر الكاميرا والميكروفون؟"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"يمكن للجميع رؤية التطبيقات المصغّرة على شاشة القفل، حتى في حال قفل الجهاز اللوحي."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"إلغاء اختيار التطبيق المصغّر"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"تقليل الارتفاع"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"زيادة الارتفاع"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string>
@@ -583,12 +581,10 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"محو الكل"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"إدارة"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"السجلّ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"إعدادات الإشعارات"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"سجلّ الإشعارات"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"الإشعارات الجديدة"</string>
- <string name="notification_section_header_gentle" msgid="6804099527336337197">"صامتة"</string>
+ <string name="notification_section_header_gentle" msgid="6804099527336337197">"إشعارات صامتة"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"الإشعارات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"المحادثات"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"محو جميع الإشعارات الصامتة"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ما مِن إشعارات"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ما مِن إشعارات جديدة"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ميزة \"تخفيض الإشعارات الصوتية والاهتزاز\" مُفعَّلة"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ميزة \"تخفيض الإشعارات الصوتية والاهتزاز\" مفعَّلة الآن"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"يتم تلقائيًا خفض مستوى صوت جهازك والتنبيهات لمدة تصل إلى دقيقتين عند تلقّي إشعارات كثيرة في آنٍ واحد."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"إيقاف"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"افتَح قفل الشاشة لعرض الإشعارات الأقدم."</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"تفعيل"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"تتبُّع حركة الرأس"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"انقر لتغيير وضع الرنين."</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"وضع الرنين"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string>
@@ -765,7 +762,7 @@
<string name="inline_done_button" msgid="6043094985588909584">"تمّ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"تطبيق"</string>
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"إيقاف الإشعارات"</string>
- <string name="notification_silence_title" msgid="8608090968400832335">"صامتة"</string>
+ <string name="notification_silence_title" msgid="8608090968400832335">"إشعارات صامتة"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"تلقائية"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صوت أو اهتزاز"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"التبديل إلى التطبيق على اليسار أو الأسفل أثناء استخدام \"تقسيم الشاشة\""</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"التبديل إلى التطبيق على اليمين أو الأعلى أثناء استخدام \"تقسيم الشاشة\""</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"استبدال تطبيق بآخر في وضع \"تقسيم الشاشة\""</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"الإدخال"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"التبديل إلى اللغة التالية"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"التبديل إلى اللغة السابقة"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"التطبيق الحالي"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"تسهيل الاستخدام"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"تخصيص اختصارات لوحة المفاتيح"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"البحث في الاختصارات"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ما مِن نتائج بحث"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"تخصيص"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تم"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"مقبض السحب"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"إعدادات لوحة المفاتيح"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"التنقّل باستخدام لوحة المفاتيح"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"تعرَّف على اختصارات لوحة المفاتيح"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"التنقّل باستخدام لوحة اللمس"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"مقدَّمة من التطبيقات"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"العرض"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"غير معروفة"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"إعادة ضبط المربّعات"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"هل تريد إعادة ضبط المربّعات إلى ترتيبها وحجمها الأصليَّين؟"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"هل تريد إعادة ضبط كل المربّعات؟"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ستتم إعادة ضبط جميع مربّعات \"الإعدادات السريعة\" إلى الإعدادات الأصلية للجهاز"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index a89650aa6e19..f985e2f001c6 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"غير مفعَّلة"</item>
<item msgid="3028994095749238254">"مفعَّلة"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 85517f3865d2..7b3f0bbbc93c 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপোনাৰ স্ক্ৰীনখন ৰেকৰ্ড কৰিবনে?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"এটা এপ্ ৰেকৰ্ড কৰক"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"গোটেই স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"গোটেই স্ক্ৰীনখন ৰেকৰ্ড কৰক: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"আপুনি গোটেই স্ক্ৰীনখন ৰেকৰ্ডিং কৰিলে, আপোনাৰ স্ক্ৰীনখনত দেখুওৱা যিকোনো বস্তু ৰেকৰ্ড কৰা হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"আপুনি কোনো এপ্ ৰেকৰ্ড কৰিলে, সেই এপত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু ৰেকৰ্ড কৰা হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"বৰ্তমান আপুনি <xliff:g id="APP_NAME">%1$s</xliff:g> ৰেকৰ্ড কৰি আছে"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ৰেকৰ্ডিং বন্ধ কৰক"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"স্ক্ৰীন শ্বেয়াৰ কৰি থকা হৈছে"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"সমল শ্বেয়াৰ কৰি থকা হৈছে"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"স্ক্ৰীন শ্বেয়াৰ কৰা বন্ধ কৰিবনে?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"শ্বেয়াৰ কৰাটো বন্ধ কৰিবনে?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"বৰ্তমান আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>ৰ সৈতে শ্বেয়াৰ কৰি আছে"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"বৰ্তমান আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন এটা এপৰ সৈতে শ্বেয়াৰ কৰি আছে"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"বৰ্তমান আপুনি <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> শ্বেয়াৰ কৰি আছে"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"বৰ্তমান আপুনি এটা এপ্ শ্বেয়াৰ কৰি আছে"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"বৰ্তমান আপুনি এটা এপৰ সৈতে শ্বেয়াৰ কৰি আছে"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"শ্বেয়াৰ কৰা বন্ধ কৰক"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"স্ক্ৰীন কাষ্ট কৰি থকা হৈছে"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"কাষ্ট কৰা বন্ধ কৰিবনে?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"শ্ৰৱণ যন্ত্ৰ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"অন কৰি থকা হৈছে…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"স্বয়ং-ঘূৰ্ণন"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"প্ৰিছেট আপডে’ট কৰিব পৰা নগ’ল"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্ৰিছেট"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ কেপশ্বন"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইচৰ কেমেৰা আৰু মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"লক স্ক্ৰীনৰ ৱিজেট"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"আপোনাৰ টেবলেটটো লক কৰি ৰাখিলেও যিকোনো লোকে আপোনাৰ লক স্ক্ৰীনত ৱিজেট চাব পাৰে।"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ৱিজেট বাছনিৰ পৰা আঁতৰাওক"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"উচ্চতা হ্ৰাস কৰক"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"উচ্চতা বৃদ্ধি কৰক"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্ৰীন ৱিজেট"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"এতিয়াই আৰম্ভ কৰক"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"কোনো জাননী নাই"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"কোনো নতুন জাননী নাই"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"জাননী কুলডাউন কৰাটো অন আছে"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"আপুনি একেলগে বহুতো জাননী পালে আপোনাৰ ডিভাইচটোৰ ভলিউম আৰু সতৰ্কবাৰ্তা স্বয়ংক্ৰিয়ভাৱে ২ মিনিটলৈকে কমোৱা হয়।"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"অফ কৰক"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"পুৰণি জাননী চবলৈ আনলক কৰক"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"নিৰ্ধাৰিত"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হে’ড ট্ৰেকিং"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ৰিংগাৰ ম’ড সলনি কৰিবলৈ টিপক"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ৰিংগাৰ ম’ড"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত সোঁফালে অথবা তলত থকা এপলৈ সলনি কৰক"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত বাওঁফালে অথবা ওপৰত থকা এপলৈ সলনি কৰক"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"বিভাজিত স্ক্ৰীনৰ ব্যৱহাৰ কৰাৰ সময়ত: কোনো এপ্ এখন স্ক্ৰীনৰ পৰা আনখনলৈ নিয়ক"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ইনপুট"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"পৰৱৰ্তী ভাষাটোলৈ সলনি কৰক"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"পূৰ্বৰ ভাষালৈ সলনি কৰক"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"বৰ্তমানৰ এপ্‌"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"সাধ্য সুবিধা"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীব’ৰ্ডৰ শ্বৰ্টকাট কাষ্টমাইজ কৰক"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"সন্ধানৰ কোনো ফলাফল নাই"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাষ্টমাইজ কৰক"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হ’ল"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীব’ৰ্ডৰ ছেটিং"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"কীব’ৰ্ড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহৰ বিষয়ে জানক"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
@@ -1441,7 +1454,7 @@
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"সুন্দৰ!"</string>
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"আপুনি উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"গৃহ পৃষ্ঠালৈ যাওক"</string>
- <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপোনাৰ টাচ্চপেডৰ তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string>
+ <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"বঢ়িয়া!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"শেহতীয়া এপ্‌সমূহ চাওক"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"এপে প্ৰদান কৰা"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিছপ্লে’"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজ্ঞাত"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"টাইল ৰিছেট কৰক"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"টাইলসমূহ সেইসমূহৰ মূল ক্ৰম আৰু আকাৰলৈ ৰিছেট কৰিবনে?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"আটাইবোৰ টাইল ৰিছেট কৰিবনে?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"আটাইবোৰ ক্ষিপ্ৰ ছেটিঙৰ টাইল ডিভাইচৰ মূল ছেটিংছলৈ ৰিছেট হ’ব"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index e978fe279a6e..3ec2f5c67557 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"অফ আছে"</item>
<item msgid="3028994095749238254">"অন আছে"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index f08724a81449..1c84a590d6b9 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran qeydə alınsın?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir tətbiqi qeydə alın"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Bütün ekranı qeydə alın"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Bütün ekranı qeydə alın: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Bütün ekranı qeydə alarkən ekranda göstərilən bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Tətbiq qeydə aldıqda həmin tətbiqdə göstərilən və ya işə salınan bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı qeydə alın"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Hazırda <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqini çəkirsiniz"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Qeydəalmanı dayandırın"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran paylaşılır"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Kontent paylaşmaq"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran paylaşımı dayandırılsın?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Paylaşım dayandırılsın?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Hazırda bütün ekranı <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ilə paylaşırsınız"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Hazırda bütün ekranı tətbiq ilə paylaşırsınız"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Hazırda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> paylaşırsınız"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Hazırda tətbiq paylaşırsınız"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Hazırda tətbiqlə paylaşırsınız"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Paylaşımı dayandırın"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran yayımlanır"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Yayım dayandırılsın?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eşitmə aparatları"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiv edilir..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Məkan"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Kilid ekranı vidcetləri"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Planşet kilidli olsa belə, hər kəs kilid ekranınızdakı vidcetlərə baxa bilər."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"vidcet seçimini silin"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Hündürlüyü azaldın"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Hündürlüyü artırın"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilid ekranı vidcetləri"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hamısını silin"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"İdarə edin"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Tarixçə"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Bildiriş ayarları"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Bildiriş tarixçəsi"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Yeni"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Səssiz"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirişlər"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"İndi başlayın"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Heç bir bildiriş yoxdur"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Yeni bildiriş yoxdur"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildiriş gözləmə müddəti yanılıdır"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Eyni anda çox bildiriş aldıqda cihazın səs və xəbərdarlıqları avtomatik 2 dəqiqəyə qədər azalır."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktiv edin"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Köhnə bildirişləri görmək üçün kilidi açın"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Sabit"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş izləməsi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zəng rejimini dəyişmək üçün toxunun"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"zəng səsi rejimi"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran istifadə edərkən sağda və ya aşağıda tətbiqə keçin"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran istifadə edərkən solda və ya yuxarıda tətbiqə keçin"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran rejimində: tətbiqi birindən digərinə dəyişin"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Daxiletmə"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Növbəti dilə keçin"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Əvvəlki dilə keçin"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Cari tətbiq"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatura qısayollarını fərdiləşdirin"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Axtarış nəticəsi yoxdur"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Fərdiləşdirin"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hazırdır"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura ayarları"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviaturadan istifadə edərək hərəkət edin"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klaviatura qısayolları haqqında öyrənin"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Taçpeddən istifadə edərək hərəkət edin"</string>
@@ -1435,14 +1446,14 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura və taçpeddən istifadə edərək hərəkət edin"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Taçped jestləri, klaviatura qısayolları və s. haqqında öyrənin"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri qayıdın"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Əsas səhifəyə qayıdın"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Əsas səhifəyə keçin"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son tətbiqlərə baxın"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Taçpeddə üç barmaqla sola və ya sağa sürüşdürün"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Əla!"</string>
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Geri getmə jestini tamamladınız."</string>
- <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ana ekrana qayıdın"</string>
+ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Əsas səhifəyə keçin"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Taçpeddə üç barmaqla yuxarı sürüşdürün"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Əla!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Əsas səhifəyə keçid jestini tamamladınız"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Tətbiqlər tərəfindən təmin edilir"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displey"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Naməlum"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Mozaikləri sıfırlayın"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Mozaiklər orijinal sıra və ölçülərinə sıfırlansın?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Bütün mozaiklər sıfırlansın?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Bütün Sürətli Ayarlar mozaiki cihazın orijinal ayarlarına sıfırlanacaq"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index c24f4029e415..4eea105107c1 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Deaktiv"</item>
<item msgid="3028994095749238254">"Aktiv"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f5096915e261..f260f5369f7c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite da snimite ekran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimi jednu aplikaciju"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimi ceo ekran"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Snimite ceo ekran: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate ceo ekran, snima se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snima se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimi ekran"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snimate: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran se deli"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Deljenje sadržaja"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Želite da zaustavite deljenje ekrana?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Želite da zaustavite deljenje?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno delite ceo ekran sa: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno delite ceo ekran sa aplikacijom"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno delite: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno delite aplikaciju"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Trenutno delite sa aplikacijom"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi deljenje"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Prebacuje se ekran"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite da zaustavite prebacivanje?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključuje se..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatska rotacija"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unapred određena podešavanja"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titl uživo"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite da odblokirate mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite da odblokirate kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite da odblokirate kameru i mikrofon uređaja?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti za zaključani ekran"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu da vide vidžete na zaključanom ekranu, čak i kada je tablet zaključan."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništi izbor vidžeta"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Smanji visinu"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Povećaj visinu"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti za zaključani ekran"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nema obaveštenja"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obaveštenja"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Utišavanje obaveštenja je uključeno"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Utišavanje obaveštenja je sada uključeno"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Zvuk i broj upozorenja na uređaju se automatski smanjuju na 2 minuta kada dobijete previše obaveštenja."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starija obaveštenja"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promenili režim zvona"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string>
@@ -814,7 +813,7 @@
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Razmak"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Taster za brisanje unazad"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Taster za reprodukciju/pauziranje"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Taster za zaustavljanje"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Taster Sledeća"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređi u aplikaciju zdesna ili ispod dok je podeljen ekran"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"U režimu podeljenog ekrana: zamena jedne aplikacije drugom"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Pređi na sledeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pređi na prethodni jezik"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite tasterske prečice"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pretražite prečice"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretrage"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Podešavanja tastature"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tasterskim prečicama"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću tačpeda"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Obezbeđuju aplikacije"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetujte pločice"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Želite da resetujete pločice na prvobitni redosled i veličine?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite da resetujete sve pločice?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brzih podešavanja će se resetovati na prvobitna podešavanja uređaja"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index df0b78664cba..3f8841afb4f2 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Isključeno"</item>
<item msgid="3028994095749238254">"Uključeno"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index bc0e2d189c02..a303c11ef0fc 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Запісаць экран?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Запісаць адну праграму"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Запісаць змесціва ўсяго экрана"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Запісваць экран цалкам: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Пры запісе ўсяго экрана запісваецца ўсё, што паказваецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Пры запісе праграмы запісваецца ўсё, што паказваецца або прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Запісаць экран"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Зараз вы запісваеце змесціва праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Спыніць запіс"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экран абагульваецца"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Абагульваецца змесціва"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Спыніць абагульванне экрана?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Спыніць абагульванне?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Зараз вы абагульваеце змесціва ўсяго экрана з праграмай \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Зараз вы абагульваеце змесціва ўсяго экрана з праграмай"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Зараз вы абагульваеце змесціва праграмы \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Зараз вы абагульваеце змесціва праграмы"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Зараз вы абагульваеце змесціва з праграмай"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Спыніць абагульванне"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Экран трансліруецца"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Спыніць трансляцыю?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Увод"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слыхавыя апараты"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Уключэнне…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не ўдалося абнавіць набор налад"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор налад"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Аўтаматычныя субцітры"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблакіраваць камеру і мікрафон прылады?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Віджэты на экране блакіроўкі"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Віджэты на экране блакіроўкі будуць бачныя, нават калі планшэт заблакіраваны."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"скасаваць выбар віджэта"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Паменшыць вышыню"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Павялічыць вышыню"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджэты на экране блакіроўкі"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ачысціць усё"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Кіраваць"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Гісторыя"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Налады апавяшчэнняў"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Гісторыя апавяшчэнняў"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Новае"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Без гуку"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Апавяшчэнні"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Апавяшчэнняў няма"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Няма новых апавяшчэнняў"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Зніжэнне гучнасці апавяшчэнняў уключана"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Зніжэнне гучнасці апавяшчэнняў зараз уключана"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Калі адначасова прыходзіць шмат апавяшчэнняў, гук прылады і абвестак зніжаецца на час да 2 хвілін."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Выключыць"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблакіруйце, каб убачыць усе апавяшчэнні"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Замацавана"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Адсочваць рух галавы"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Націсніце, каб змяніць рэжым званка"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"рэжым званка"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пераключыцца на праграму справа або ўнізе на падзеленым экране"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пераключыцца на праграму злева або ўверсе на падзеленым экране"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"У рэжыме падзеленага экрана замяніць адну праграму на іншую"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Увод"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Пераключыцца на наступную мову"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Пераключыцца на папярэднюю мову"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Бягучая праграма"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Спецыяльныя магчымасці"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Наладзіць спалучэнні клавіш"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма вынікаў пошуку"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Наладзіць"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Гатова"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налады клавіятуры"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігацыя з дапамогай клавіятуры"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Азнаёмцеся са спалучэннямі клавіш"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігацыя з дапамогай сэнсарнай панэлі"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Забяспечваюцца праграмамі"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невядома"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Скінуць пліткі"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Скінуць пліткі да зыходнага парадку і памеру?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скінуць усе пліткі?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усе пліткі хуткіх налад будуць скінуты да першапачатковых налад прылады"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index 33e704cae0b1..85602864dc76 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Выключана"</item>
<item msgid="3028994095749238254">"Уключана"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2ee1f5f0f21e..a127515e8f19 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се записва ли екранът?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записване на едно приложение"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записване на целия екран"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Записване на целия екран: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Когато записвате целия си екран, се записва всичко, което се показва на него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Когато записвате приложение, се записва всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записване на екрана"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"В момента записвате <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Спиране на записа"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Екранът се споделя"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Съдържанието се споделя"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Да се спре ли споделянето на екрана?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Да се спре ли споделянето?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"В момента споделяте целия си екран с(ъс) <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"В момента споделяте целия си екран с приложение"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"В момента споделяте <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"В момента споделяте приложение"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"В момента споделяте с приложение"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Спиране на споделянето"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Екранът се предава"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Да се спре ли предаването?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Вход"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухови апарати"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включва се..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авт. ориентация"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Предварително зададените настройки не бяха актуализирани"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Предварително зададено"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Надписи на живо"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се отблокират ли камерата и микрофонът на устройството?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Приспособления за заключения екран"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Всеки ще вижда приспособленията на закл. екран дори ако таблетът ви е заключен."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"премахване на избора от приспособлението"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Намаляване на височината"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Увеличаване на височината"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Приспособления за заключения екран"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управление"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Настройки за известията"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"История на известията"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Нови"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Беззвучни"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известия"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Няма известия"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Няма нови известия"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Изчакването за известията е включено"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Изчакването за известията вече е включено"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Силата на звука и сигналите на у-вото се намаляват за до 2 минути, когато получавате твърде много известия наведнъж."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Изключване"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отключете за достъп до по-стари известия"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Фиксирано"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Прослед. на движенията на главата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Докоснете, за да промените режима на звънене"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"режим на звънене"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"спиране"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"пускане"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string>
@@ -867,13 +864,15 @@
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отваряне на Асистент"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заключване на екрана"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Създаване на бележка"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Изпълняване на няколко задачи едновременно"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Няколко задачи едновременно"</string>
<string name="system_multitasking_rhs" msgid="8714224917276297810">"Използване на разделен екран с текущото приложение вдясно"</string>
<string name="system_multitasking_lhs" msgid="8402954791206308783">"Използване на разделен екран с текущото приложение вляво"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Превключване от разделен към цял екран"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Превключване към приложението вдясно/отдолу в режима на разделен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Превключване към приложението вляво/отгоре в режима на разделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При разделен екран: замяна на дадено приложение с друго"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Въвеждане"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Превключване към следващия език"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Превключване към предишния език"</string>
@@ -1407,7 +1406,7 @@
<string name="shortcut_helper_category_system" msgid="462110876978937359">"Системни"</string>
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Системни контроли"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Системни приложения"</string>
- <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Изпълняване на няколко задачи едновременно"</string>
+ <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Няколко задачи едновременно"</string>
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Скорошни приложения"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделен екран"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Въвеждане"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Текущо приложение"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Достъпност"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Персонализиране на клавишните комбинации"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма резултати от търсенето"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Персонализиране"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигирайте посредством клавиатурата си"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете за клавишните комбинации"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигирайте посредством сензорния панел"</string>
@@ -1443,7 +1453,7 @@
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Чудесно!"</string>
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Изпълнихте жеста за връщане назад."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Към началния екран"</string>
- <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Прекарайте три пръста нагоре по сензорния панел"</string>
+ <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Плъзнете три пръста нагоре по сензорния панел"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отлично!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Изпълнихте жеста за преминаване към началния екран"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Преглед на скорошните приложения"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Предоставено от приложения"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Нулиране на панелите"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Да се възстанови ли първоначалният ред и размери на панелите?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Да се нулират ли всички панели?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Всички панели с бързи настройки ще бъдат нулирани до първоначалните настройки на устройството"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index e2fd65360020..9b808de65480 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Изкл."</item>
<item msgid="3028994095749238254">"Вкл."</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index c593210c4613..ae810a612d28 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপনার স্ক্রিন রেকর্ড করবেন?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"একটি অ্যাপ রেকর্ড করুন"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"সম্পূর্ণ স্ক্রিন রেকর্ড করুন"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"পুরো স্ক্রিন রেকর্ড করুন: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"আপনার সম্পূর্ণ স্ক্রিন রেকর্ড করার সময়, আপনার স্ক্রিনে দেখানো সব কিছু রেকর্ড করা হয়। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"আপনি কোনও অ্যাপ রেকর্ড করার সময়, সেই অ্যাপে দেখানো বা চালানো সব কিছু রেকর্ড করা হয়। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"স্ক্রিন রেকর্ড করুন"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"আপনি বর্তমানে <xliff:g id="APP_NAME">%1$s</xliff:g> রেকর্ড করছেন"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"রেকর্ড করা বন্ধ করুন"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"স্ক্রিন শেয়ার করা হচ্ছে"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"কন্টেন্ট শেয়ার করা হচ্ছে"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"স্ক্রিন শেয়ার করা বন্ধ করবেন?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"শেয়ার করা বন্ধ করতে চান?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"আপনি বর্তমানে <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> অ্যাপের সাথে আপনার সম্পূর্ণ স্ক্রিন শেয়ার করছেন"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে আপনার সম্পূর্ণ স্ক্রিন শেয়ার করছেন"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"আপনি বর্তমানে <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> অ্যাপের সাথে শেয়ার করছেন"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে শেয়ার করছেন"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে শেয়ার করছেন"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"শেয়ার করা বন্ধ করুন"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"স্ক্রিন কাস্ট করা হচ্ছে"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"কাস্ট করা বন্ধ করবেন?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"হিয়ারিং এড"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"চালু করা হচ্ছে…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ ক্যাপশন"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"লক স্ক্রিন উইজেট"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"আপনার ট্যাবলেট লক থাকলেও যেকোনও ব্যক্তি লক স্ক্রিনে উইজেট দেখতে পাবেন।"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"উইজেট বাদ দিন"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"উচ্চতা কমান"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"উচ্চতা বাড়ান"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্রিন উইজেট"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"এখন শুরু করুন"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"কোনও বিজ্ঞপ্তি নেই"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"নতুন কোনও বিজ্ঞপ্তি নেই"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"নোটিফিকেশন কুলডাউন চালু আছে"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"আপনি একসঙ্গে খুব বেশি বিজ্ঞপ্তি পেলে আপনার ডিভাইসের ভলিউম এবং সতর্কবার্তা সর্বাধিক ২ মিনিটের জন্য অটোমেটিক কমে যায়।"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"বন্ধ করুন"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"পুরনো বিজ্ঞপ্তি দেখতে আনলক করুন"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"চালু আছে"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"হেড ট্র্যাকিং"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"রিঙ্গার মোড পরিবর্তন করতে ট্যাপ করুন"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"রিঙ্গার মোড"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"স্প্লিট স্ক্রিন ব্যবহার করার সময় ডানদিকের বা নিচের অ্যাপে পাল্টে নিন"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"স্প্লিট স্ক্রিন ব্যবহার করার সময় বাঁদিকের বা উপরের অ্যাপে পাল্টে নিন"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"\'স্প্লিট স্ক্রিন\' থাকাকালীন: একটি অ্যাপ থেকে অন্যটিতে পাল্টান"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ইনপুট"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"পরবর্তী ভাষায় পাল্টান"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"আগের ভাষায় পাল্টান"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"বর্তমান অ্যাপ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"অ্যাক্সেসিবিলিটি"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"কীবোর্ড শর্টকাট কাস্টমাইজ করুন"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"শর্টকাট সার্চ করুন"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"কোনও সার্চ ফলাফল নেই"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাস্টমাইজ করুন"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হয়ে গেছে"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"আপনার কীবোর্ড ব্যবহার করে নেভিগেট করুন"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপনার টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"অ্যাপের তরফ থেকে দেওয়া"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিসপ্লে"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজানা"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"টাইল রিসেট করুন"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"টাইলগুলিকে অরিজিনাল অর্ডার ও সাইজ অনুযায়ী রিসেট করবেন?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"সব টাইল রিসেট করবেন?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"সব কুইক সেটিংস টাইল, ডিভাইসের আসল সেটিংসে রিসেট হয়ে যাবে"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 6e4dfbfbf745..dd5b40636fb6 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"বন্ধ আছে"</item>
<item msgid="3028994095749238254">"চালু আছে"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 38f42659245e..df562ef3508a 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Snimati ekran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimaj jednu aplikaciju"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimaj cijeli ekran"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Snimi cijeli ekran: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate cijeli ekran, snimat će se sve što se prikazuje na ekranu. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snimat će se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimaj ekran"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snimate aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Dijeljenje ekrana"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Dijeljenje sadržaja"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Zaustaviti dijeljenje ekrana?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Zaustaviti dijeljenje?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno dijelite cijeli ekran s aplikacijom <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno dijelite cijeli ekran s aplikacijom"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno dijelite aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno dijelite aplikaciju"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Trenutno dijelite s aplikacijom"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi dijeljenje"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitiranje ekrana"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Zaustaviti emitiranje?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ulaz"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti na zaključanom ekranu"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu pregledati vidžete na zaključanom ekranu, čak i ako je tablet zaključan."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira vidžeta"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Smanjenje visine"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Povećanje visine"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti na zaključanom ekranu"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nema obavještenja"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavještenja"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stišavanje obavještenja je uključeno"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Stišavanje obavijesti sada je uključeno"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jačina zvuka uređaja i obavještenja se automatski stišavaju do 2 minute kada odjednom dobijete previše obavještenja."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte da vidite starija obavještenja"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"način rada za zvuk zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -813,7 +812,7 @@
<string name="keyboard_key_back" msgid="4185420465469481999">"Nazad"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Tipka za razmak"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"Tipka za novi red"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Tipka za brisanje"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Pokreni/pauziraj"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Zaustavi"</string>
@@ -869,9 +868,11 @@
<string name="system_multitasking_rhs" msgid="8714224917276297810">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na desnoj strani"</string>
<string name="system_multitasking_lhs" msgid="8402954791206308783">"Korištenje podijeljenog ekrana s trenutnom aplikacijom na lijevoj strani"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Prebacivanje s podijeljenog ekrana na prikaz preko cijelog ekrana"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređite u aplikaciju desno ili ispod dok koristite podijeljeni ekran"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak u aplikaciju desno ili ispod uz podijeljeni ekran"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju lijevo ili iznad dok koristite podijeljeni ekran"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Za vrijeme podijeljenog ekrana: zamjena jedne aplikacije drugom"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prebacivanje na sljedeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prebacivanje na prethodni jezik"</string>
@@ -1011,7 +1012,7 @@
<string name="qs_dnd_prompt_app" msgid="4027984447935396820">"Način rada Ne ometaj uključila je aplikacija <xliff:g id="ID_1">%s</xliff:g>."</string>
<string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Način rada Ne ometaj uključilo je automatsko pravilo ili aplikacija."</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikacije koje rade u pozadini"</string>
- <string name="running_foreground_services_msg" msgid="3009459259222695385">"Dodirnite za detalje o potrošnji baterije i prijenosa podataka"</string>
+ <string name="running_foreground_services_msg" msgid="3009459259222695385">"Dodirnite za detalje o potrošnji baterije i prenosa podataka"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti prijenos podataka na mobilnoj mreži?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ni internetu putem mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem WiFi-ja."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš operater"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite prečice na tastaturi"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagođavanje"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tastature"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o prečicama tastature"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
@@ -1443,7 +1455,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Odlazak na početni ekran"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prevucite nagore s tri prsta na dodirnoj podlozi"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Sjajno!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Savladali ste pokret za otvaranje početnog ekrana"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Savladali ste pokret za odlazak na početni ekran"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaz nedavnih aplikacija"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Pružaju aplikacije"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Vratite kartice na zadano"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vratiti kartice na zadani redoslijed i veličine?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vratiti sve kartice na zadano?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve kartice Brze postavke će se vratiti na originalne postavke uređaja"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index df0b78664cba..3f8841afb4f2 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Isključeno"</item>
<item msgid="3028994095749238254">"Uključeno"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index bcaca5ae16e9..0ecd8c0e0c23 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vols gravar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grava tota la pantalla"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Grava tota la pantalla: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quan graves una aplicació, es grava tot el que es mostra o es reprodueix en aquesta aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grava la pantalla"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ara mateix estàs gravant <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Atura la gravació"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"S\'està compartint la pantalla"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"S\'està compartint contingut"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vols deixar de compartir la pantalla?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Vols deixar de compartir-lo?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ara mateix estàs compartint tota la pantalla amb <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ara mateix estàs compartint tota la pantalla amb una aplicació"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Ara mateix estàs compartint <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Ara mateix estàs compartint una aplicació"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Ara mateix estàs compartint contingut amb una aplicació"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Deixa de compartir"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"S\'està emetent la pantalla"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vols aturar l\'emissió?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiòfons"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"S\'està activant…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítols instantanis"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de la pantalla de bloqueig"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure els widgets de la teva pantalla de bloqueig, fins i tot quan la tauleta està bloquejada."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desselecciona el widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Redueix l\'alçada"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Augmenta l\'alçada"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string>
@@ -565,8 +563,8 @@
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emet tota la pantalla"</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que es mostra en pantalla és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
- <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que s\'hi mostra és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que s\'hi mostra o reprodueix és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emet la pantalla"</string>
<string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Tria una aplicació per emetre"</string>
<string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vols començar a compartir?"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Esborra-ho tot"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestiona"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Configuració de notificacions"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historial de notificacions"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novetats"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciat"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacions"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No hi ha cap notificació"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No hi ha cap notificació nova"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La moderació de notificacions està activada"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volum i les alertes del dispositiu es redueixen automàticament durant 2 minuts com a màxim quan reps massa notificacions alhora."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactiva"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueja per veure notif. anteriors"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Canvia a l\'aplicació de la dreta o de sota amb la pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Canvia a l\'aplicació de l\'esquerra o de dalt amb la pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durant el mode de pantalla dividida: substitueix una app per una altra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Canvia a l\'idioma següent"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Caniva a l\'idioma anterior"</string>
@@ -1118,7 +1118,7 @@
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"suprimir dels preferits"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"Mou a la posició <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Controls"</string>
- <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Tria a quins controls del dispositiu vols accedir ràpidament"</string>
+ <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Tria a quins controls de dispositius vols accedir ràpidament"</string>
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mantén premuts els controls i arrossega\'ls per reordenar-los"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"S\'han suprimit tots els controls"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Els canvis no s\'han desat"</string>
@@ -1129,7 +1129,7 @@
<string name="controls_favorite_load_error" msgid="5126216176144877419">"No s\'han pogut carregar els controls. Consulta l\'aplicació <xliff:g id="APP">%s</xliff:g> per assegurar-te que la configuració de l\'aplicació no hagi canviat."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Els controls compatibles no estan disponibles"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altres"</string>
- <string name="controls_dialog_title" msgid="2343565267424406202">"Afegeix als controls de dispositius"</string>
+ <string name="controls_dialog_title" msgid="2343565267424406202">"Afegeix als controls del dispositiu"</string>
<string name="controls_dialog_ok" msgid="2770230012857881822">"Afegeix"</string>
<string name="controls_dialog_remove" msgid="3775288002711561936">"Suprimeix"</string>
<string name="controls_dialog_message" msgid="342066938390663844">"Suggerit per <xliff:g id="APP">%s</xliff:g>"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicació actual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalitza les tecles de drecera"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hi ha cap resultat de la cerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalitza"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fet"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuració del teclat"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega amb el teclat"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprèn les tecles de drecera"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega amb el ratolí tàctil"</string>
@@ -1435,7 +1446,7 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega amb el teclat i el ratolí tàctil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprèn els gestos del ratolí tàctil, les tecles de drecera i més"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Torna"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pàgina d\'inici"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pantalla d\'inici"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string>
@@ -1460,7 +1471,7 @@
<string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a estalvi de pantalla"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Desfés"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Per tornar enrere, llisca tres dits cap a l\'esquerra o cap a la dreta al ratolí tàctil"</string>
- <string name="home_edu_toast_content" msgid="3381071147871955415">"Per anar a la pantalla d\'inici, llisca tres dits cap amunt al ratolí tàctil"</string>
+ <string name="home_edu_toast_content" msgid="3381071147871955415">"Per anar a la pantalla d\'inici, fes lliscar tres dits cap amunt al ratolí tàctil"</string>
<string name="overview_edu_toast_content" msgid="5797030644017804518">"Per veure les aplicacions recents, llisca cap amunt amb tres dits i mantén premut al ratolí tàctil"</string>
<string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Per veure totes les aplicacions, prem la tecla d\'acció al teclat"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"Emmascarat"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionat per aplicacions"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconegut"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restableix les icones"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vols restablir l\'ordre i les mides originals de les icones?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vols restablir totes les icones?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Totes les icones de configuració ràpida es restabliran a les opcions originals del dispositiu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 67eb853c9ee6..ea1a576d2cc0 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desactivat"</item>
<item msgid="3028994095749238254">"Activat"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 76ae86deddd4..3f00ce42939a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pořídit nahrávku obrazovky?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrát jednu aplikaci"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrát celou obrazovku"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Nahrát celou obrazovku: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Při nahrávání celé obrazovky se zaznamenává veškerý obsah na obrazovce. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané obrazovce zobrazuje nebo přehrává. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrát obrazovku"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Momentálně nahráváte aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ukončit nahrávání"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sdílení obrazovky"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sdílení obsahu"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ukončit sdílení obrazovky?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Ukončit sdílení?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Momentálně sdílíte celou obrazovku s aplikací <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Momentálně sdílíte celou obrazovku s aplikací"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Momentálně sdílíte aplikaci <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Momentálně sdílíte aplikaci"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Momentálně sdílíte obsah s aplikací"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ukončit sdílení"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Odesílání obsahu obrazovky"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ukončit odesílání?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Naslouchátka"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapínání…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. otáčení"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčení obrazovky"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Předvolba"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okamžité titulky"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokovat mikrofon zařízení?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokovat fotoaparát zařízení?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokovat fotoaparát a mikrofon zařízení?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgety na obrazovce uzamčení"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Widgety na obrazovce uzamčení může zobrazit kdokoli, i když je tablet uzamčen."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"zrušit výběr widgetu"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Snížit výšku"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Zvýšit výšku"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgety na obrazovce uzamčení"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string>
@@ -580,13 +578,11 @@
<string name="media_projection_task_switcher_notification_channel" msgid="7613206306777814253">"Přepnutí aplikace"</string>
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blokováno administrátorem IT"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Záznam obrazovky je zakázán zásadami zařízení"</string>
- <string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string>
+ <string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazat vše"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Spravovat"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historie"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Nastavení oznámení"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historie oznámení"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nové"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tichý režim"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Oznámení"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Žádná oznámení"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Žádná nová oznámení"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Oznámení jsou zeslabená"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Když máte moc oznámení najednou, až na dvě minuty se sníží hlasitost zařízení a oznámení se omezí."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnout"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Starší oznámení se zobrazí po odemknutí"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixovaný"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledování hlavy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Klepnutím změníte režim vyzvánění"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"režim vyzvánění"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Přepnout na aplikaci vpravo nebo dole v režimu rozdělené obrazovky"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přepnout na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"V režimu rozdělené obrazovky: nahradit jednu aplikaci druhou"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Přepnout na další jazyk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Přepnout na předchozí jazyk"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuální aplikace"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Přizpůsobení klávesových zkratek"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žádné výsledky hledání"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Přizpůsobit"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavení klávesnice"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigujte pomocí klávesnice"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte se klávesové zkratky"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigujte pomocí touchpadu"</string>
@@ -1441,11 +1452,11 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Přejeďte po touchpadu třemi prsty doleva nebo doprava"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Skvělé!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dokončili jste gesto pro přechod zpět."</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Provedli jste gesto pro přechod zpět."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Přejít na plochu"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Přejeďte po touchpadu třemi prsty nahoru"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Výborně!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dokončili jste gesto pro přechod na plochu"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Provedli jste gesto pro přechod na plochu"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazit nedávné aplikace"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Přejeďte po touchpadu třemi prsty nahoru a podržte je"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Výborně!"</string>
@@ -1453,7 +1464,7 @@
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazit všechny aplikace"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dokončili jste gesto k zobrazení všech aplikací"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Provedli jste gesto k zobrazení všech aplikací"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Poskytováno aplikacemi"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displej"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznámé"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetování dlaždic"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Resetovat dlaždice na původní pořadí a velikosti?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Resetovat všechny dlaždice?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všechny dlaždice Rychlého nastavení se resetují do původní konfigurace zařízení"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index ae533a8623ec..abfe50df0d36 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Vypnuto"</item>
<item msgid="3028994095749238254">"Zapnuto"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index abcd4b8d45ce..9229b18d3363 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du optage din skærm?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Optag én app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Optag hele skærmen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Optag hele skærmen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du optager hele skærmen, bliver alt det, der vises på skærmen, optaget. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du optager en app, optages alt det, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Optag skærm"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du optager i øjeblikket <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop optagelse"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Skærmen deles"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Indhold deles"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vil du stoppe skærmdelingen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Vil du stoppe med at dele?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du deler i øjeblikket hele skærmen med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du deler i øjeblikket hele skærmen med en app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du deler i øjeblikket <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du deler i øjeblikket en app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Du deler i øjeblikket med en app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop deling"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skærmen castes"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vil du stoppe din cast?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverer…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstning"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets på låseskærmen"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Alle kan se widgets på din låseskærm, også selvom din tablet er låst."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"fjern markering af widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Reducer højden"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Forøg højden"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets på låseskærmen"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nu"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ingen notifikationer"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ingen nye notifikationer"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Dæmpning af notifikationer er aktiveret"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enheden skruer automatisk ned for lydstyrken og minimerer underretninger på skærmen i op til 2 minutter, når du får for mange notifikationer på én gang."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktiver"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås op for at se ældre notifikationer"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fast"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Register. af hoved­bevægelser"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryk for at ændre ringetilstand"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringetilstand"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skift til en app til højre eller nedenfor, når du bruger opdelt skærm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skift til en app til venstre eller ovenfor, når du bruger opdelt skærm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ved opdelt skærm: Udskift én app med en anden"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skift til næste sprog"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skift til forrige sprog"</string>
@@ -885,7 +887,7 @@
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"Sms"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musik"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalender"</string>
- <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Lommeregner"</string>
+ <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Lomme­regner"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"Forstyr ikke"</string>
<string name="volume_dnd_silent" msgid="4154597281458298093">"Genvej til lydstyrkeknapper"</string>
@@ -992,7 +994,7 @@
<string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
<string name="notification_channel_setup" msgid="7660580986090760350">"Konfiguration"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Lagerplads"</string>
- <string name="notification_channel_hints" msgid="7703783206000346876">"Tips"</string>
+ <string name="notification_channel_hints" msgid="7703783206000346876">"Tip"</string>
<string name="notification_channel_accessibility" msgid="8956203986976245820">"Hjælpefunktioner"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> kører"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuel app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpas tastaturgenveje"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Der er ingen søgeresultater"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpas"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Udfør"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturindstillinger"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger ved hjælp af dit tastatur"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Se tastaturgenveje"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger ved hjælp af din touchplade"</string>
@@ -1439,7 +1452,7 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Stryg til venstre eller højre med tre fingre på touchpladen"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sådan!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har fuldført bevægelsen for Gå tilbage."</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har udført bevægelsen for Gå tilbage."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gå til startskærmen"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Stryg opad med tre fingre på touchpladen"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Flot!"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fra apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skærm"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukendt"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Nulstil felter"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vil du nulstille felterne til deres oprindelige rækkefølge og størrelser?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vil du nulstille alle handlingsfelter?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle handlingsfelter i kvikmenuen nulstilles til enhedens oprindelige indstillinger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 2c3b0535cb33..9009eedc39cc 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Fra"</item>
<item msgid="3028994095749238254">"Til"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 9799a932bcd9..e8dcae9828ce 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Bildschirm aufnehmen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Einzelne App aufnehmen"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gesamten Bildschirm aufnehmen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Gesamten Bildschirm aufnehmen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wenn du den gesamten Bildschirm aufnimmst, ist in der Aufnahme alles zu sehen, was auf dem Bildschirm angezeigt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wenn du eine App aufnimmst, ist in der Aufnahme alles zu sehen, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Bildschirm aufnehmen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du zeichnest momentan Inhalte der App <xliff:g id="APP_NAME">%1$s</xliff:g> auf"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Aufzeichnung beenden"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bildschirm wird geteilt"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Inhalte teilen"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Bildschirmfreigabe beenden?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Teilen beenden?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du teilst momentan deinen gesamten Bildschirm mit der App <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du teilst momentan deinen gesamten Bildschirm mit einer App"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du teilst momentan die App <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du teilst momentan Inhalte einer App"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Du teilst momentan Inhalte mit einer App"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Freigabe beenden"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Bildschirm wird übertragen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Streaming beenden?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Eingabe"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörgerät"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Wird aktiviert…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatische Untertitel"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Sperrbildschirm-Widgets"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Jeder kann Widgets auf deinem Sperrbildschirm sehen, auch bei gesperrtem Tablet."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"Auswahl für Widget aufheben"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Höhe verringern"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Höhe vergrößern"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sperrbildschirm-Widgets"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alle löschen"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Verwalten"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Verlauf"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Benachrichtigungseinstellungen"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Benachrichtigungs­verlauf"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Neu"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Lautlos"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Benachrichtigungen"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Keine Benachrichtigungen"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Keine neuen Benachrichtigungen"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"„Benachrichtigungen reduzieren” ist aktiviert"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Wenn du zu viele Benachrichtigungen auf einmal erhältst, wird die Lautstärke automatisch bis zu 2 min lang verringert und Benachrichtigungen werden minimiert."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktivieren"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Für ältere Benachrichtigungen entsperren"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Statisch"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Erfassung von Kopfbewe­gungen"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Zum Ändern des Klingeltonmodus tippen"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"Klingeltonmodus"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Stummschalten"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Aufheben der Stummschaltung"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string>
@@ -854,9 +852,9 @@
<string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Tastenkombinationen für die Eingabe werden angezeigt"</string>
<string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Tastenkombinationen zum Öffnen von Apps werden angezeigt"</string>
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Tastenkombinationen für die aktuelle App werden angezeigt"</string>
- <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Benachrichtigungen ansehen"</string>
+ <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Benachrichti­gungen ansehen"</string>
<string name="group_system_full_screenshot" msgid="5742204844232667785">"Screenshot erstellen"</string>
- <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tasten­kombinationen anzeigen"</string>
+ <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tasten­kürzel anzeigen"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Zurück"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Zum Startbildschirm wechseln"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Letzte Apps aufrufen"</string>
@@ -874,11 +872,13 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Im Splitscreen-Modus zu einer App rechts oder unten wechseln"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Im Splitscreen-Modus zu einer App links oder oben wechseln"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Im Splitscreen: eine App durch eine andere ersetzen"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Eingabe"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Zur nächsten Sprache wechseln"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Zur vorherigen Sprache wechseln"</string>
- <string name="input_access_emoji" msgid="8105642858900406351">"Auf Emojis zugreifen"</string>
- <string name="input_access_voice_typing" msgid="7291201476395326141">"Auf Spracheingabe zugreifen"</string>
+ <string name="input_access_emoji" msgid="8105642858900406351">"Emojis aufrufen"</string>
+ <string name="input_access_voice_typing" msgid="7291201476395326141">"Spracheingabe aufrufen"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Apps"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Assistant"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Browser"</string>
@@ -1408,26 +1408,37 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"System­steuerelemente"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"System-Apps"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Zuletzt aktive Apps"</string>
+ <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Zuletzt verwendete Apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Splitscreen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-Verknüpfungen"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-Verknüp­fungen"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelle App"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkombinationen anpassen"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Keine Suchergebnisse"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassen"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fertig"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigation mit der Tastatur"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informationen zu Tastenkombinationen"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigation mit dem Touchpad"</string>
@@ -1435,7 +1446,7 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigation mit Tastatur und Touchpad"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Informationen zu Touchpad-Gesten, Tastenkombinationen und mehr"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zurück"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zur Startseite"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zum Startbildschirm"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Letzte Apps aufrufen"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Von Apps bereitgestellt"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unbekannt"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Kacheln zurücksetzen"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Kacheln auf die ursprüngliche Reihenfolge und Größe zurücksetzen?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Alle Kacheln zurücksetzen?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Schnelleinstellungen-Kacheln werden auf die Standardeinstellungen des Geräts zurückgesetzt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 0606cc79f2e9..e7f5b574755a 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Aus"</item>
<item msgid="3028994095749238254">"An"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 7200a91b618e..cf9adddcc3ea 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Να γίνει εγγραφή της οθόνης σας;"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Εγγραφή μίας εφαρμογής"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Εγγραφή ολόκληρης της οθόνης"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Εγγραφή ολόκληρης της οθόνης: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Όταν κάνετε εγγραφή ολόκληρης της οθόνη σας, καταγράφεται οτιδήποτε εμφανίζεται σε αυτήν. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Όταν κάνετε εγγραφή μιας εφαρμογής, καταγράφεται οτιδήποτε εμφανίζεται ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Εγγραφή οθόνης"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Αυτή τη στιγμή εγγράφετε το <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Διακοπή εγγραφής"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Γίνεται κοινοποίηση οθόνης"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Κοινή χρήση περιεχομένου"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Διακοπή κοινής χρήσης οθόνης;"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Να διακοπεί η κοινή χρήση;"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Αυτή τη στιγμή μοιράζεστε ολόκληρη την οθόνη σας με το <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Αυτή τη στιγμή μοιράζεστε ολόκληρη την οθόνη σας με μια εφαρμογή"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Αυτή τη στιγμή μοιράζεστε το <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Αυτή τη στιγμή μοιράζεστε μια εφαρμογή"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Αυτή τη στιγμή, μοιράζεστε περιεχόμενο με μια εφαρμογή"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Διακοπή κοινής χρήσης"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Μετάδοση οθόνης"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Τερματισμός μετάδοσης;"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Είσοδος"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Βοηθήματα ακοής"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ενεργοποίηση…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ζωντανοί υπότιτλοι"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Όλοι μπορούν να δουν γραφικά στοιχεία στην οθόνη κλειδώματος, ακόμα και αν το tablet είναι κλειδωμένο."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"αποεπιλογή γραφικού στοιχείου"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Μείωση του ύψους"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Αύξηση του ύψους"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Διαγραφή όλων"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Διαχείριση"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ιστορικό"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Ρυθμίσεις ειδοποιήσεων"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Ιστορικό ειδοποιήσεων"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Νέα"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Σίγαση"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ειδοποιήσεις"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Δεν υπάρχουν ειδοποιήσεις"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Δεν υπάρχουν νέες ειδοποιήσεις"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Η ρύθμιση cooldown ειδοποιήσεων είναι ενεργή"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Η ρύθμιση cooldown ειδοποιήσεων είναι πλέον ενεργή"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Αυτόματη μείωση έντασης ήχου συσκευής και ειδοποιήσεων για έως 2 λεπτά όταν λαμβάνετε πολλές ειδοποιήσεις ταυτόχρονα."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Απενεργοποίηση"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ξεκλειδώστε για εμφάνιση παλαιότ. ειδοπ."</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Σταθερός"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλής"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"λειτουργία ειδοποίησης ήχου"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Εναλλαγή στην εφαρμογή δεξιά ή κάτω κατά τη χρήση διαχωρισμού οθόνης"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Εναλλαγή σε εφαρμογή αριστερά ή επάνω κατά τη χρήση διαχωρισμού οθόνης"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Κατά τον διαχωρισμό οθόνης: αντικατάσταση μιας εφαρμογής με άλλη"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Είσοδος"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Εναλλαγή στην επόμενη γλώσσα"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Εναλλαγή στην προηγούμενη γλώσσα"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Τρέχουσα εφαρμογή"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Προσβασιμότητα"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Προσαρμογή συντομεύσεων πληκτρολογίου"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Κανένα αποτέλεσμα αναζήτησης"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Προσαρμογή"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Τέλος"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Πλοήγηση με το πληκτρολόγιο"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Μάθετε συντομεύσεις πληκτρολογίου"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Πλοήγηση με την επιφάνεια αφής"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Παρέχεται από εφαρμογές"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Προβολή"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Άγνωστο"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Επαναφορά πλακιδίων"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Επαναφορά των πλακιδίων στην αρχική τους σειρά και μεγέθη;"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Επαναφορά σε όλα τα πλακάκια;"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Σε όλα τα πλακάκια Γρήγορων ρυθμίσεων θα γίνει επαναφορά στις αρχικές ρυθμίσεις της συσκευής"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index d4545ffa6641..1276fb4addbc 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Ανενεργή"</item>
<item msgid="3028994095749238254">"Ενεργή"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 1ecf4f17cac3..66659599dc94 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Record entire screen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sharing content"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Stop sharing?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"You\'re currently sharing with an app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Decrease height"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Increase height"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Notification settings"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Notification history"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 39dd7c84b13e..c0bbabea4d78 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Off"</item>
<item msgid="3028994095749238254">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index f775513ca418..9a3bedaabe4c 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Record entire screen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -327,6 +326,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being\n controlled by the top app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -415,6 +415,7 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+ <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -590,7 +591,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
@@ -698,6 +699,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head Tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -867,6 +869,7 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to app on right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to app on left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: replace an app from one to another"</string>
+ <string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string>
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1408,19 +1411,23 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current App"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
- <skip />
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customize keyboard shortcuts"</string>
+ <string name="shortcut_helper_customize_mode_sub_title" msgid="2479732335876820286">"Press key to assign shortcut"</string>
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
- <skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
- <skip />
+ <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string>
+ <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string>
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customize"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard Settings"</string>
+ <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string>
+ <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancel"</string>
+ <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Press key"</string>
+ <string name="shortcut_helper_customize_dialog_error_message" msgid="5954264095841845768">"Key combination already in use. Try another key."</string>
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1478,6 +1485,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device’s original settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 39dd7c84b13e..1b60921d3237 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -191,4 +191,9 @@
<item msgid="3079622119444911877">"Off"</item>
<item msgid="3028994095749238254">"On"</item>
</string-array>
+ <string-array name="tile_states_notes">
+ <item msgid="5894333929299989301">"Unavailable"</item>
+ <item msgid="6419996398343291862">"Off"</item>
+ <item msgid="5908720590832378783">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 1ecf4f17cac3..66659599dc94 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Record entire screen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sharing content"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Stop sharing?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"You\'re currently sharing with an app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Decrease height"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Increase height"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Notification settings"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Notification history"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 39dd7c84b13e..c0bbabea4d78 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Off"</item>
<item msgid="3028994095749238254">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 1ecf4f17cac3..66659599dc94 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Record entire screen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sharing content"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Stop sharing?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"You\'re currently sharing with an app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Decrease height"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Increase height"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Notification settings"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Notification history"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Customise keyboard shortcuts"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard settings"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 39dd7c84b13e..c0bbabea4d78 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Off"</item>
<item msgid="3028994095749238254">"On"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 77c2a639f903..3dc305811498 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Quieres grabar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Grabar toda la pantalla: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabes toda la pantalla, se grabará todo lo que se muestre en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabes una app, se registrará todo lo que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Actualmente, estás grabando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener grabación"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartiendo pantalla"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Compartiendo contenido"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"¿Quieres dejar de compartir la pantalla?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"¿Quieres dejar de compartir?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Actualmente, estás compartiendo toda la pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Actualmente, estás compartiendo toda la pantalla con una app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Actualmente, estás compartiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Actualmente, estás compartiendo una app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Actualmente, estás compartiendo con una app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dejar de compartir"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitiendo pantalla"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"¿Detener la transmisión?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ajuste predeterminado"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitulado instantáneo"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Quieres desbloquear el micrófono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Quieres desbloquear la cámara del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Quieres desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets en la pantalla de bloqueo"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Los widgets de la pantalla de bloqueo podrán verse incluso si bloqueas la tablet."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anular la selección del widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Reducir la altura"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar la altura"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets en la pantalla de bloqueo"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Reducción de sonido de notificaciones activada"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Reducción de sonido de notificaciones ahora está activada"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volumen y las alertas se reducen por hasta 2 minutos si recibes muchas notificaciones a la vez."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fijar"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Monitoreo de cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Presiona para cambiar el modo de timbre"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ubicar la app a la derecha o abajo cuando usas la pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ubicar la app a la izquierda o arriba cuando usas la pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durante pantalla dividida: Reemplaza una app con otra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar al próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar al idioma anterior"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App actual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personaliza las combinaciones de teclas"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"La búsqueda no arrojó resultados"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Listo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega con el teclado"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega con el panel táctil"</string>
@@ -1433,7 +1445,7 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega con el teclado y el panel táctil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende sobre los gestos del panel táctil, las combinaciones de teclas y mucho más"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atrás"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página de inicio"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página principal"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recientes"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
@@ -1445,7 +1457,7 @@
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaste el gesto para ir a la página principal"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recientes"</string>
- <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos en el panel táctil y mantenlos presionados."</string>
+ <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos en el panel táctil y mantenlos presionados"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaste el gesto para ver las apps recientes."</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las apps"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restablecer tarjetas"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"¿Quieres restablecer las tarjetas al orden y el tamaño originales?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Quieres restablecer todas las tarjetas?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Se restablecerán todas las tarjeta de Configuración rápida a la configuración original del dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index 869efff07bdf..dec68dae3dc1 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desactivados"</item>
<item msgid="3028994095749238254">"Activados"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 81b1562ed533..4660d9879e76 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Grabar la pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una aplicación"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Grabar toda la pantalla: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabas toda la pantalla, se graba todo lo que se muestre en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabas una aplicación, se graba todo lo que se muestre o reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Estás grabando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener grabación"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartiendo pantalla"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Compartiendo contenido"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"¿Dejar de compartir pantalla?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"¿Dejar de compartir?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Estás compartiendo toda tu pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Estás compartiendo toda tu pantalla con una aplicación"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Estás compartiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Estás compartiendo una aplicación"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Estás compartiendo una aplicación"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dejar de compartir"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Enviando pantalla"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"¿Dejar de enviar?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos automáticos"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets para la pantalla de bloqueo"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Cualquiera puede ver los widgets de tu pantalla de bloqueo, aunque tu tablet esté bloqueada."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deseleccionar widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Reducir altura"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar altura"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets para la pantalla de bloqueo"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Ajustes de notificaciones"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historial de notificaciones"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nuevas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Empezar ahora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bajar el volumen de notificaciones está activado"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volumen y las alertas de tu dispositivo se reducen durante hasta 2 minutos si recibes muchas notificaciones a la vez."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fijo"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimiento de cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar el modo de timbre"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -814,7 +812,7 @@
<string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"Espa-cio"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Tecla de retroceso"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Reproducir/Pausa"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la aplicación de la derecha o de abajo en pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Con pantalla dividida: reemplazar una aplicación por otra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar a siguiente idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar a idioma anterior"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación en uso"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar las combinaciones de teclas"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hay resultados de búsqueda"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hecho"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ajustes del teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Desplázate con el teclado"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Desplázate con el panel táctil"</string>
@@ -1447,7 +1458,7 @@
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Has completado el gesto para ir a la pantalla de inicio"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver aplicaciones recientes"</string>
- <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba y mantén pulsado con tres dedos en el panel táctil"</string>
+ <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos y mantén pulsado en el panel táctil"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completado el gesto para ver las aplicaciones recientes."</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las aplicaciones"</string>
@@ -1471,7 +1482,7 @@
<string name="home_edu_notification_title" msgid="6097902076909654045">"Usa el panel táctil para ir a la pantalla de inicio"</string>
<string name="home_edu_notification_content" msgid="6631697734535766588">"Desliza hacia arriba con tres dedos. Toca para aprender a usar más gestos."</string>
<string name="overview_edu_notification_title" msgid="1265824157319562406">"Usa el panel táctil para ver las aplicaciones recientes"</string>
- <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba y mantén pulsado con tres dedos. Toca para aprender a usar más gestos."</string>
+ <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba con tres dedos y mantén pulsado. Toca para aprender a usar más gestos."</string>
<string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa el teclado para ver todas las aplicaciones"</string>
<string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pulsa la tecla de acción en cualquier momento. Toca para aprender a usar más gestos."</string>
<string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"La atenuación extra ahora forma parte del control deslizante de brillo"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por aplicaciones"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restablecer recuadros"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"¿Restablecer recuadros a su orden y tamaño originales?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Borrar todos los recuadros?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos los recuadros de ajustes rápidos se restablecerán a los ajustes originales del dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 08644e1cbe40..e872c263f1e6 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desactivados"</item>
<item msgid="3028994095749238254">"Activado"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 56039da5acc8..f2a92f299081 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Kas salvestada ekraanikuvast video?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ühe rakenduse salvestamine"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Kogu ekraanikuva salvestamine"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Kogu ekraanikuva salvestamine: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kui salvestate kogu ekraani, salvestatakse kõik ekraanil kuvatud andmed. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kui salvestate rakendust, salvestatakse kõik, mida selles rakenduses näidatakse või esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekraanikuva jäädvustamine"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Salvestate praegu rakendust <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Peata salvestamine"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekraani jagamine"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sisu jagamine"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Kas lõpetada ekraanikuva jagamine?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Kas lõpetada jagamine?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jagate praegu kogu oma ekraanikuva rakendusega <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jagate praegu kogu oma ekraanikuva rakendusega"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jagate praegu rakenduse <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> kuva"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jagate praegu rakenduse kuva"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Jagate praegu rakendusega"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Lõpeta jagamine"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekraanikuva ülekandmine"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Kas peatada ülekandmine?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sisend"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuuldeaparaadid"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Sisselülitamine …"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Eelseadistus"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Reaalajas subtiitrid"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kas tühistada seadme mikrofoni blokeerimine?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kas tühistada seadme kaamera blokeerimine?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kas tühistada seadme kaamera ja mikrofoni blokeerimine?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Lukustuskuva vidinad"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Igaüks saab vaadata luk.kuval olevaid vidinaid, isegi kui tahvelarvuti on lukus."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"tühistage vidina valimine"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Vähenda kõrgust"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Suurenda kõrgust"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukustuskuva vidinad"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tühjenda kõik"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Haldamine"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ajalugu"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Märguandeseaded"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Märguannete ajalugu"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Uued"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Hääletu"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Märguanded"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Alusta kohe"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Märguandeid pole"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Uusi märguandeid ei ole"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Märguannete summutamine on sees"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Kui saate korraga liiga palju märguandeid, vähendab seade automaatselt helitugevust ja minimeerib märguanded kuni kaheks minutiks."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Lülita välja"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Vanemate märguannete nägemiseks avage"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fikseeritud"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pea jälgimine"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Puudutage telefonihelina režiimi muutmiseks"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"telefonihelina režiim"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Paremale või alumisele rakendusele lülitamine jagatud ekraani ajal"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vasakule või ülemisele rakendusele lülitamine jagatud ekraani ajal"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ekraanikuva jagamise ajal: ühe rakenduse asendamine teisega"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sisend"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Järgmisele keelele lülitamine"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Eelmisele keelele lülitamine"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Praegune rakendus"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klaviatuuri otseteede kohandamine"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsige otseteid"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Otsingutulemused puuduvad"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohandamine"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Õppige klaviatuuri otseteid"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeerige puuteplaadi abil"</string>
@@ -1435,14 +1446,14 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeerige klaviatuuri ja puuteplaadi abil"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Õppige puuteplaadi liigutusi, klaviatuuri otseteid ja palju muud"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Mine tagasi"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Avalehele"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Avakuvale"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Hiljutiste rakenduste vaatamine"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pühkige puuteplaadil kolme sõrmega vasakule või paremale"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Tubli töö!"</string>
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Tegite tagasiliikumise liigutuse."</string>
- <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Avalehele"</string>
+ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Avakuvale"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pühkige puuteplaadil kolme sõrmega üles"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Väga hea!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Tegite avakuvale minemise liigutuse"</string>
@@ -1460,8 +1471,8 @@
<string name="home_controls_dream_description" msgid="4644150952104035789">"Juurdepääs kodu juhtelementidele ekraanisäästjalt"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"Võta tagasi"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Tagasiliikumiseks pühkige puuteplaadil kolme sõrmega vasakule või paremale"</string>
- <string name="home_edu_toast_content" msgid="3381071147871955415">"Avakuvale liikumiseks pühkige puuteplaadil kolme sõrmega üles"</string>
- <string name="overview_edu_toast_content" msgid="5797030644017804518">"Hiljutiste rakenduste kuvamiseks pühkige puuteplaadil kolme sõrmega üles ja hoidke sõrmi puuteplaadil"</string>
+ <string name="home_edu_toast_content" msgid="3381071147871955415">"Avakuvale liikumiseks pühkige puuteplaadil kolme sõrmega üles."</string>
+ <string name="overview_edu_toast_content" msgid="5797030644017804518">"Hiljutiste rakenduste kuvamiseks pühkige puuteplaadil kolme sõrmega üles ja hoidke sõrmi puuteplaadil."</string>
<string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Kõigi oma rakenduste kuvamiseks vajutage klaviatuuril toiminguklahvi"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"Peidetud"</string>
<string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Vaatamiseks avage"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Rakendustelt"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kuva"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Teadmata"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Paanide lähtestamine"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Kas soovite paanid lähtestada nende algsesse järjekorda ja suurusesse?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Kas lähtestada kõik paanid?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kõik kiirseadete paanid lähtestatakse seadme algseadetele"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 704649e77b86..3af8dea15541 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Välja lülitatud"</item>
<item msgid="3028994095749238254">"Sisse lülitatud"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 426d1d754c94..daca2a37635e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pantaila grabatu nahi duzu?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabatu aplikazio bat"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabatu pantaila osoa"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Grabatu pantaila osoa: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pantaila osoa grabatzen ari zarenean, pantailan agertzen den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Aplikazio bat grabatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabatu pantaila"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"<xliff:g id="APP_NAME">%1$s</xliff:g> grabatzen ari zara"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Utzi grabatzeari"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Pantaila partekatzen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Edukia partekatzen"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Pantaila partekatzeari utzi nahi diozu?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Partekatzeari utzi nahi diozu?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Pantaila osoa <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> aplikazioarekin partekatzen ari zara"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Pantaila osoa aplikazio batekin partekatzen ari zara"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> partekatzen ari zara"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Aplikazio bat partekatzen ari zara"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Aplikazio batekin edukia partekatzen ari zara"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Utzi partekatzeari"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Pantaila igortzen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Igortzeari utzi nahi diozu?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sarrera"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audifonoak"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktibatzen…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Istanteko azpitituluak"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Pantaila blokeatuko widgetak"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Edonork ikus ditzake pantaila blokeatuko widgetak, tableta blokeatuta badago ere."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desautatu widgeta"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Txikitu altuera"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Handitu altuera"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pantaila blokeatuko widgetak"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Garbitu guztiak"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kudeatu"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Jakinarazpen-ezarpenak"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Jakinarazpenen historia"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Berria"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Isila"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Jakinarazpenak"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Hasi"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ez dago jakinarazpenik"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ez dago jakinarazpen berririk"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Jakinarazpenak arintzeko ezarpena aktibatuta dago"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Aldi berean jakinarazpen gehiegi jasotzen badituzu, gailuaren bolumena eta alertak automatikoki murriztuko dira 2 minutuz (gehienez)."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desaktibatu"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Jakinarazpen zaharragoak ikusteko, desblokeatu"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Finkoa"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Buruaren jarraipena"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Sakatu tonu-jotzailearen modua aldatzeko"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"tonu-jotzailearen modua"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Aldatu eskuineko edo beheko aplikaziora pantaila zatitua erabiltzean"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Aldatu ezkerreko edo goiko aplikaziora pantaila zatitua erabiltzean"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Pantaila zatituan zaudela, ordeztu aplikazio bat beste batekin"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sarrera"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Aldatu hurrengo hizkuntzara"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Aldatu aurreko hizkuntzara"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Oraingo aplikazioa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pertsonalizatu lasterbideak"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ez dago bilaketa-emaitzarik"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pertsonalizatu"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Eginda"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Teklatuaren ezarpenak"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nabigatu teklatua erabilita"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ikasi lasterbideak"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nabigatu ukipen-panela erabilita"</string>
@@ -1468,8 +1479,8 @@
<string name="contextual_education_dialog_title" msgid="4630392552837487324">"Testuinguruaren araberako hezkuntza"</string>
<string name="back_edu_notification_title" msgid="5624780717751357278">"Erabili ukipen-panela atzera egiteko"</string>
<string name="back_edu_notification_content" msgid="2497557451540954068">"Pasatu 3 hatz ezkerrera edo eskuinera. Sakatu keinu gehiago ikasteko."</string>
- <string name="home_edu_notification_title" msgid="6097902076909654045">"Erabili ukipen-panela hasierako pantailara joateko"</string>
- <string name="home_edu_notification_content" msgid="6631697734535766588">"Pasatu 3 hatz. Sakatu keinu gehiago ikasteko."</string>
+ <string name="home_edu_notification_title" msgid="6097902076909654045">"Erabili ukipen-panela orri nagusira joateko"</string>
+ <string name="home_edu_notification_content" msgid="6631697734535766588">"Pasatu 3 hatz gora. Sakatu keinu gehiago ikasteko."</string>
<string name="overview_edu_notification_title" msgid="1265824157319562406">"Erabili ukipen-panela azkenaldiko aplikazioak ikusteko"</string>
<string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasatu 3 hatz gora eta eduki sakatuta. Sakatu keinu gehiago ikasteko."</string>
<string name="all_apps_edu_notification_title" msgid="372262997265569063">"Erabili teklatua aplikazio guztiak ikusteko"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Aplikazioenak"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantaila"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ezezagunak"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Berrezarri lauzak"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Lauzen jatorrizko ordena eta tamainak berrezarri nahi dituzu?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Lauza guztiak berrezarri nahi dituzu?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Gailuaren jatorrizko ezarpenak berrezarriko dira ezarpen bizkorren lauza guztietan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index 13e14e0502c4..8ada72a9f3ae 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desaktibatuta"</item>
<item msgid="3028994095749238254">"Aktibatuta"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index b0d23aa81898..4f2c89e57216 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"صفحه‌نمایش ضبط شود؟"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ضبط یک برنامه"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ضبط کل صفحه‌نمایش"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"‏ضبط کردن کل صفحه‌نمایش: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"وقتی کل صفحه‌نمایش را ضبط می‌کنید، هر چیزی که در صفحه‌نمایش نشان داده شود ضبط خواهد شد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"وقتی برنامه‌ای را ضبط می‌کنید، هر چیزی که در آن برنامه نشان داده شود یا پخش شود ضبط خواهد شد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ضبط صفحه‌نمایش"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"اکنون درحال ضبط <xliff:g id="APP_NAME">%1$s</xliff:g> هستید"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"توقف ضبط"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"درحال هم‌رسانی صفحه"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"هم‌رسانی محتوا"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"هم‌رسانی صفحه متوقف شود؟"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"هم‌رسانی متوقف شود؟"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> هستید"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با یک برنامه هستید"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"اکنون درحال هم‌رسانی <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> هستید"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"اکنون درحال هم‌رسانی با یک برنامه هستید"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"درحال هم‌رسانی با یک برنامه هستید"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"توقف هم‌رسانی"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"درحال پخش محتوای صفحه‌نمایش"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"پخش محتوا متوقف شود؟"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ورودی"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سمعک"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"روشن کردن…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحه‌نمایش"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string>
@@ -417,7 +415,9 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"پیش‌تنظیم به‌روزرسانی نشد"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیش‌تنظیم"</string>
- <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"زیرنویس ناشنوایان زنده"</string>
+ <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"زیرنویس زنده ناشنوایان"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ابزاره‌های صفحه قفل"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"همه می‌توانند ابزاره‌ها را در صفحه قفل شما ببینند، حتی اگر رایانه لوحی قفل باشد."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"لغو انتخاب ابزاره"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"کاهش ارتفاع"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"افزایش ارتفاع"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزاره‌های صفحه قفل"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"اعلانی موجود نیست"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"اعلان جدیدی وجود ندارد"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"«استراحت اعلان‌ها» روشن است"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"«استراحت اعلان‌ها» اکنون روشن است"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"وقتی به‌طور هم‌زمان تعداد بسیار زیادی اعلان دریافت کنید، میزان صدای دستگاه و هشدارها به‌طور خودکار تا ۲ دقیقه کاهش می‌یابد."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"خاموش کردن"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"برای دیدن اعلان‌های قبلی قفل را باز کنید"</string>
@@ -653,7 +651,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"غیرفعال کردن"</string>
<string name="sound_settings" msgid="8874581353127418308">"صدا و لرزش"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"تنظیمات"</string>
- <string name="volume_panel_captioning_title" msgid="5984936949147684357">"زیرنویس ناشنوایان زنده"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"زیرنویس زنده ناشنوایان"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"صدا به سطح ایمن‌تر کاهش یافت"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"صدای هدفون برای مدتی طولانی‌تر از حد توصیه‌شده بلند بوده است"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"صدای هدفون از حد ایمن برای این هفته فراتر رفته است"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ثابت"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ردیابی سر"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، تک‌ضرب بزنید"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"حالت زنگ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"رفتن به برنامه سمت راست یا پایین درحین استفاده از صفحهٔ دونیمه"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"رفتن به برنامه سمت چپ یا بالا درحین استفاده از صفحهٔ دونیمه"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"درحین صفحهٔ دونیمه: برنامه‌ای را با دیگری جابه‌جا می‌کند"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ورودی"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"رفتن به زبان بعدی"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"رفتن به زبان قبلی"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"برنامه فعلی"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"دسترس‌پذیری"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"میان‌برهای صفحه‌کلید"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"سفارشی‌سازی کردن میان‌برهای صفحه‌کلید"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میان‌برها"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"نتیجه‌ای برای جستجو پیدا نشد"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"سفارشی‌سازی کردن"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تمام"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحه‌کلید"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"پیمایش کردن بااستفاده از صفحه‌کلید"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"آشنایی با میان‌برهای صفحه‌کلید"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"پیمایش کردن بااستفاده از صفحه لمسی"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ارائه‌شده از برنامه‌ها"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"نمایشگر"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامشخص"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"بازنشانی کردن کاشی‌ها"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"اندازه و ترتیب کاشی‌ها به حالت اولیه‌شان بازنشانی شود؟"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"همه کاشی‌ها بازنشانی شود؟"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"همه کاشی‌های «تنظیمات فوری» به تنظیمات اصلی دستگاه بازنشانی خواهد شد"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index 756b442f5fc4..b7f4830db666 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"خاموش"</item>
<item msgid="3028994095749238254">"روشن"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 690228f9d333..581f0eada3ac 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Tallennetaanko näytön toimintaa?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yhdestä sovelluksesta"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tallenna koko näyttö"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Tallenna koko näyttö: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kun tallennat koko näyttöä, kaikki näytöllä näkyvä sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kun tallennat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Tallenna näyttö"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Laite, jonka sisältöä tallennat: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Lopeta tallennus"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Näyttöä jaetaan"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Jaetaan sisältöä"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Lopetetaanko näytön jakaminen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Lopetetaanko jakaminen?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jaat tällä hetkellä koko näyttöä: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jaat tällä hetkellä koko näyttöä sovellukselle"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jaat tällä hetkellä tätä: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jaat tällä hetkellä sovellusta"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Jaat tällä hetkellä sovellukseen"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Lopeta jakaminen"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Näyttöä striimataan"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Lopetetaanko striimaus?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Syöttölaite"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuulolaitteet"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Otetaan käyttöön…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstitys"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Lukitusnäytön widgetit"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Kaikki voivat nähdä widgetit lukitusnäytöllä, vaikka tabletti olisi lukittuna."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poista widgetin valinta"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Vähennä korkeutta"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Lisää korkeutta"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukitusnäytön widgetit"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tyhjennä kaikki"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Muuta asetuksia"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Ilmoitusasetukset"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Ilmoitushistoria"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Uudet"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Äänetön"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ilmoitukset"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Aloita nyt"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ei ilmoituksia"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ei uusia ilmoituksia"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ilmoitusten vaimennus on päällä"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Äänenvoimakkuus ja ilmoitukset vaimennetaan enintään 2 minuutiksi, kun saat paljon ilmoituksia."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Laita pois päältä"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Avaa lukitus niin näet ilmoituksia"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Kiinteä"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pään seuranta"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Vaihda soittoäänen tilaa napauttamalla"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"Soittoäänen tila"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Vaihda sovellukseen oikealla tai alapuolella jaetussa näytössä"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vaihda sovellukseen vasemmalla tai yläpuolella jaetussa näytössä"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Jaetun näytön aikana: korvaa sovellus toisella"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Syöttötapa"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Vaihda seuraavaan kieleen"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Vaihda aiempaan kieleen"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Nykyinen sovellus"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Pikanäppäimien muokkaaminen"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ei hakutuloksia"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Muokkaa"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Näppäimistön asetukset"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Siirry käyttämällä näppäimistöä"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Opettele pikanäppäimiä"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Siirry käyttämällä kosketuslevyä"</string>
@@ -1445,7 +1456,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Siirry etusivulle"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pyyhkäise ylös kolmella sormella kosketuslevyllä"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Hienoa!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Olet oppinut aloitusnäytölle palaamiseleen"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Olet oppinut eleen, jolla pääset takaisin aloitusnäytölle"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Katso viimeisimmät sovellukset"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Hienoa!"</string>
@@ -1453,7 +1464,7 @@
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Näytä kaikki sovellukset"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hienoa!"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen."</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Sovellusten tarjoama"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Näyttö"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tuntematon"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Palauta laatat"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Palautetaanko laatat alkuperäiseen järjestykseen ja kokoon?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Nollataanko kaikki laatat?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kaikki pika-asetuslaatat palautetaan laitteen alkuperäisiin asetuksiin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 5ecc95956d1c..e323b8a46bc4 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Pois päältä"</item>
<item msgid="3028994095749238254">"Päällä"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 5ea58ce1e574..bae94715ae6b 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer votre écran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer l\'écran entier"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Enregistrer tout l\'écran : %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'affiche sur votre écran est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans cette appli est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Vous êtes en train d\'enregistrer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Arrêter l\'enregistrement"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Partage d\'écran en cours…"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Partage de contenu en cours…"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Arrêter le partage d\'écran?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Arrêter le partage?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Vous partagez actuellement l\'intégralité de votre écran avec <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Vous partagez actuellement l\'intégralité de votre écran avec une appli"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Vous partagez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Vous partagez actuellement une appli"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Vous partagez actuellement un élément avec une appli"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Arrêter le partage"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Diffusion de l\'écran en cours…"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter la diffusion?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le microphone de l\'appareil?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le microphone?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de l\'écran de verrouillage"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut voir les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Diminuer la hauteur"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Augmenter la hauteur"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de l\'écran de verrouillage"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Paramètres de notification"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historique des notifications"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nouvelles"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Mode silencieux"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"L\'atténuation des notifications est activée"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"L\'atténuation des notifications est maintenant activée"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes maximum quand vous recevez trop de notifications à la fois."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverr. pour voir les anciennes notif."</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Activé"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -843,7 +840,7 @@
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Effacez la requête de recherche"</string>
<string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Raccourcis-clavier"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Recherchez des raccourcis"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Rechercher des raccourcis"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Aucun raccourci trouvé"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Système"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrée"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'Écran divisé"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'appli à gauche ou au-dessus avec l\'Écran divisé"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En mode d\'écran divisé : remplacer une appli par une autre"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrée"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Passer à la langue précédente"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis-clavier"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Terminé"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide de votre clavier"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis-clavier"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
@@ -1477,7 +1487,7 @@
<string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"La réduction supplémentaire de la luminosité fait désormais partie du curseur de luminosité"</string>
<string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Vous pouvez désormais réduire la luminosité de l\'écran encore plus.\n\nÉtant donné que cette fonctionnalité fait maintenant partie du curseur de luminosité, les raccourcis de la réduction supplémentaire de la luminosité sont retirés."</string>
<string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Retirer les raccourcis de la réduction supplémentaire de la luminosité"</string>
- <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Les raccourcis de la réduction supplémentaire de la luminosité ont été retirés"</string>
+ <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Raccourcis de la réduction supplémentaire de la luminosité retirés"</string>
<string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivité"</string>
<string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibilité"</string>
<string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitaires"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournies par des applis"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Affichage"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Réinitialiser les tuiles"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Réinitialiser les tuiles à leur ordre et à leur taille par défaut?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser toutes les tuiles?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toutes les tuiles des paramètres rapides seront réinitialisées aux paramètres par défaut de l\'appareil."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index f12634faecbb..0bbacd09259c 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Désactivé"</item>
<item msgid="3028994095749238254">"Activé"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 2be31b11c1f5..2006ea6d0348 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer l\'écran ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer tout l\'écran"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Enregistrer tout l\'écran : %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'y affiche est enregistré. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos, et les contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans celle-ci est enregistré. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Vous enregistrez actuellement <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Arrêter l\'enregistrement"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Partage de l\'écran…"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Partage de contenu"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Arrêter le partage d\'écran ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Arrêter le partage ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Vous partagez actuellement l\'intégralité de votre écran avec <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Vous partagez actuellement l\'intégralité de votre écran avec une appli"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Vous partagez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Vous partagez actuellement une appli"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Vous partagez actuellement du contenu avec une appli"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Arrêter le partage"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Cast de l\'écran"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter de caster ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Appareils auditifs"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le micro de l\'appareil ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer la caméra de l\'appareil ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le micro de l\'appareil ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets sur l\'écran de verrouillage"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut consulter les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Diminuer la hauteur"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Augmenter la hauteur"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets pour l\'écran de verrouillage"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Paramètres de notification"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historique des notifications"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nouvelles notifications"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencieux"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La limitation des notifications est activée"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes maximum quand vous recevez trop de notifications à la fois."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverrouiller pour voir anciennes notifications"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Activé"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string>
@@ -860,8 +858,8 @@
<string name="group_system_go_back" msgid="2730322046244918816">"Retour"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Accéder à l\'écran d\'accueil"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Afficher les applis récentes"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Avancer dans les applications récentes"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Revenir sur les applications récentes"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Faire défiler les applications récentes"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Faire défiler les applications récentes à l\'envers"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Ouvrir la liste d\'applications"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Ouvrir les paramètres"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ouvrir l\'Assistant"</string>
@@ -871,9 +869,11 @@
<string name="system_multitasking_rhs" msgid="8714224917276297810">"Utiliser l\'écran partagé avec l\'appli actuelle sur la droite"</string>
<string name="system_multitasking_lhs" msgid="8402954791206308783">"Utiliser l\'écran partagé avec l\'appli actuelle sur la gauche"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Passer de l\'écran partagé au plein écran"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passez à l\'appli à droite ou en dessous avec l\'écran partagé"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'écran partagé"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passez à l\'appli à gauche ou au-dessus avec l\'écran partagé"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En mode écran partagé : Remplacer une appli par une autre"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Saisie"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Revenir à la langue précédente"</string>
@@ -1415,21 +1415,32 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personnaliser les raccourcis clavier"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Rechercher des raccourcis"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"OK"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Paramètres du clavier"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide du clavier"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Découvrir les raccourcis clavier"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis clavier"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Découvrir les gestes au pavé tactile"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournis par des applis"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Écran"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Réinitialiser les blocs"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Rétablir l\'ordre et la taille d\'origine des blocs ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser tous les blocs ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tous les blocs \"Réglages rapides\" seront réinitialisés aux paramètres d\'origine de l\'appareil"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index fcdd9f0e6e3a..d0853f4a3185 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Désactivé"</item>
<item msgid="3028994095749238254">"Activé"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 2bd0b30988e7..ebc19bd883f7 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Queres gravar a túa pantalla?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar unha aplicación"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar pantalla completa"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Gravar pantalla completa: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cando gravas a pantalla completa, recóllese todo o que se mostra nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cando gravas unha aplicación, recóllese todo o que se mostra ou reproduce nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar pantalla"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Estás gravando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Deter gravación"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartindo pantalla"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Compartindo contido"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Queres deixar de compartir a pantalla?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Queres deixar de compartir o contido?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Estás compartindo toda a pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Estás compartindo toda a pantalla cunha aplicación"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Estás compartindo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Estás compartindo unha aplicación"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Estás compartindo contido cunha aplicación"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Deixar de compartir"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitindo pantalla"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Queres deter a emisión?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiófonos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos instantáneos"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da pantalla de bloqueo"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Calquera pode ver os widgets na pantalla de bloqueo, mesmo coa tableta bloqueada"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anular a selección do widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Reducir a altura"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar a altura"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da pantalla de bloqueo"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Eliminar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Xestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Configuración de notificacións"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historial de notificacións"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Notificacións novas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacións"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Non hai notificacións"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Non hai notificacións novas"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"A opción Amainar notificacións está activada"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ao recibir moitas notificacións, o volume e as alertas redúcense automaticamente ata dous minutos."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver máis notificacións"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixado"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimento da cabeza"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar o modo de timbre"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -826,7 +824,7 @@
<string name="keyboard_key_page_up" msgid="173914303254199845">"Re Páx"</string>
<string name="keyboard_key_page_down" msgid="9035902490071829731">"Av Páx"</string>
<string name="keyboard_key_forward_del" msgid="5325501825762733459">"Supr"</string>
- <string name="keyboard_key_esc" msgid="6230365950511411322">"Escape"</string>
+ <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
<string name="keyboard_key_move_home" msgid="3496502501803911971">"Inicio"</string>
<string name="keyboard_key_move_end" msgid="99190401463834854">"Fin"</string>
<string name="keyboard_key_insert" msgid="4621692715704410493">"Inserir"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar á aplicación da dereita ou de abaixo coa pantalla dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar á aplicación da esquerda ou de arriba coa pantalla dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"En modo de pantalla dividida: Substituír unha aplicación por outra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar ao seguinte idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar ao idioma anterior"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación actual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar os atallos de teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Busca atallos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Non hai resultados de busca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Feito"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración do teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega co teclado"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende a usar os atallos de teclado"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega co panel táctil"</string>
@@ -1441,11 +1452,11 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pasa tres dedos cara á esquerda ou cara á dereita no panel táctil"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Excelente!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaches o xesto de retroceso."</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaches o titorial do xesto de retroceso."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir ao inicio"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pasa tres dedos cara arriba no panel táctil"</string>
- <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelente traballo."</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaches o titorial do xesto de ir ao inicio"</string>
+ <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Ben feito!"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaches o titorial do xesto para ir á pantalla de inicio"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Consultar aplicacións recentes"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pasa tres dedos cara arriba e mantenos premidos no panel táctil"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Moi ben!"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provenientes de aplicacións"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Visualización"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Categoría descoñecida"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Restablecer as tarxetas"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Queres restablecer as tarxetas ao seu tamaño e orde orixinais?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Queres restablecer todos os atallos?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Restablecerase a configuración orixinal do dispositivo para todos os atallos de Configuración rápida"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index 03b934eb9cb6..18ad3df0a8c1 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desactivados"</item>
<item msgid="3028994095749238254">"Activados"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index becca8f8b37f..d5e7ca4177aa 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"તમારી સ્ક્રીન રેકોર્ડ કરીએ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"એક ઍપ રેકોર્ડ કરો"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"પૂર્ણ સ્ક્રીન રેકોર્ડ કરો"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"પૂરી સ્ક્રીન રેકોર્ડ કરો: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"જ્યારે તમે તમારી પૂર્ણ સ્ક્રીન રેકોર્ડ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર બતાવવામાં આવતી હોય તેવી બધી વસ્તુ રેકોર્ડ કરવામાં આવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"જ્યારે તમે કોઈ ઍપને રેકોર્ડ કરી રહ્યાં હો, ત્યારે એ ઍપમાં બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ રેકોર્ડ કરવામાં આવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"સ્ક્રીન રેકોર્ડ કરો"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"તમે હાલમાં <xliff:g id="APP_NAME">%1$s</xliff:g> રેકોર્ડ કરી રહ્યાં છો"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"રેકોર્ડિંગ રોકો"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"સ્ક્રીન શેર કરી રહ્યાં છીએ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"કન્ટેન્ટ શેર કરી રહ્યાં છીએ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"સ્ક્રીન શેર કરવાનું રોકીએ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"શેર કરવાનું રોકીએ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"તમે હાલમાં <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> વડે તમારી પૂર્ણ સ્ક્રીન શેર કરી રહ્યાં છો"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"તમે હાલમાં ઍપ વડે તમારી પૂર્ણ સ્ક્રીન શેર કરી રહ્યાં છો"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"તમે હાલમાં <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> શેર કરી રહ્યાં છો"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"તમે હાલમાં ઍપ શેર કરી રહ્યાં છો"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"તમે હાલમાં ઍપ સાથે શેર કરી રહ્યાં છો"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"શેર કરવાનું રોકો"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"સ્ક્રીન કાસ્ટ કરી રહ્યાં છીએ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"કાસ્ટ કરવાનું રોકીએ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ઇનપુટ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"સાંભળવામાં મદદ આપતા યંત્રો"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ચાલુ કરી રહ્યાં છીએ…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"લોકેશન"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"પ્રીસેટ અપડેટ કરી શક્યા નથી"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"પ્રીસેટ"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"લાઇવ કૅપ્શન"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ડિવાઇસના કૅમેરા અને માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"લૉક સ્ક્રીન વિજેટ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ તમારી લૉક સ્ક્રીન પર વિજેટ જોઈ શકે છે."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"વિજેટ નાપસંદ કરો"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ઊંચાઈ ઘટાડો"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ઊંચાઈ વધારો"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"લૉક સ્ક્રીન વિજેટ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"કોઈ નોટિફિકેશન નથી"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"કોઈ નવું નોટિફિકેશન નથી"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"નોટિફિકેશન કૂલડાઉન ચાલુ છે"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"નોટિફિકેશન કૂલડાઉનની સુવિધા ચાલુ છે"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"જ્યારે તમને એકસાથે ઘણા બધા નોટિફિકેશન મળે ત્યારે તમારા ડિવાઇસનું વૉલ્યૂમ અને અલર્ટ ઑટોમૅટિક રીતે 2 મિનિટ જેટલા સમય માટે ઘટાડવામાં આવે છે."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"બંધ કરો"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"જૂના નોટિફિકેશન જોવા માટે અનલૉક કરો"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ફિક્સ્ડ"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"હૅડ ટ્રૅકિંગ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"રિંગર મોડ બદલવા માટે ટૅપ કરો"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"રિંગર મોડ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે જમણી બાજુ કે નીચેની ઍપ પર સ્વિચ કરો"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે ડાબી બાજુની કે ઉપરની ઍપ પર સ્વિચ કરો"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"વિભાજિત સ્ક્રીન દરમિયાન: એક ઍપને બીજી ઍપમાં બદલો"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ઇનપુટ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"આગલી ભાષા પર સ્વિચ કરો"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"પાછલી ભાષા પર સ્વિચ કરો"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"હાલની ઍપ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ઍક્સેસિબિલિટી"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"કીબોર્ડ શૉર્ટકટને કસ્ટમાઇઝ કરો"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"કોઈ શોધ પરિણામો નથી"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"કસ્ટમાઇઝ કરો"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"થઈ ગયું"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"કીબોર્ડના સેટિંગ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"કીબોર્ડ શૉર્ટકર્ટ જાણો"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"તમારા ટચપૅડ વડે નૅવિગેટ કરો"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ઍપ દ્વારા પ્રદાન કરવામાં આવેલી"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ડિસ્પ્લે"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"અજાણ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ટાઇલને રીસેટ કરો"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ટાઇલને તેમના મૂળ ક્રમ અને કદમાં રીસેટ કરીએ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"તમામ ટાઇલ રીસેટ કરીએ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"તમામ ઝડપી સેટિંગ ટાઇલને ડિવાઇસના ઑરિજિનલ સેટિંગ પર રીસેટ કરવામાં આવશે"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 5c4a4784c13f..e6202321148f 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"બંધ છે"</item>
<item msgid="3028994095749238254">"ચાલુ છે"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index a6fc05e2c7e0..cad756d0cebb 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"क्या आपको स्क्रीन रिकॉर्ड करनी है?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक ऐप्लिकेशन की रिकॉर्डिंग करें"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरी स्क्रीन रिकॉर्ड करें"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"पूरी स्क्रीन रिकॉर्ड करें: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"पूरी स्क्रीन रिकॉर्ड करते समय, स्क्रीन पर दिखने वाली हर चीज़ रिकॉर्ड की जाती है. इसलिए पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, डिवाइस पर चल रहे ऑडियो और वीडियो, और फ़ोटो जैसी चीज़ों को लेकर सावधानी बरतें."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"किसी ऐप्लिकेशन को रिकॉर्ड करने के दौरान, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी रिकॉर्ड होता है. इसलिए, रिकॉर्ड करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रिकॉर्ड करें"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"फ़िलहाल, <xliff:g id="APP_NAME">%1$s</xliff:g> की रिकॉर्डिंग की जा रही है"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रिकॉर्ड करना बंद करें"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रीन शेयर की जा रही है"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"कॉन्टेंट शेयर करें"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रीन शेयर करना बंद करना है?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"क्या आपको शेयर करने की प्रोसेस बंद करनी है?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"फ़िलहाल, <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> पर पूरी स्क्रीन शेयर की जा रही है"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"फ़िलहाल, किसी ऐप्लिकेशन पर पूरी स्क्रीन शेयर की जा रही है"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> शेयर किया जा रहा है"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"फ़िलहाल, कोई ऐप्लिकेशन शेयर किया जा रहा है"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"फ़िलहाल, ऐप्लिकेशन के साथ कुछ शेयर किया जा रहा है"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"शेयर करना बंद करें"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्क्रीन कास्ट की जा रही है"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट करना बंद करना है?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"कान की मशीनें"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ब्लूटूथ चालू हो रहा है…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"जगह की जानकारी"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव कैप्शन"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको माइक्रोफ़ोन का ऐक्सेस अनब्लॉक करना है?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको कैमरे का ऐक्सेस अनब्लॉक करना है?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"लॉक स्क्रीन विजेट"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"टैबलेट लॉक होने के बावजूद, कोई भी व्यक्ति इसकी लॉक स्क्रीन पर विजेट देख सकता है."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेट से चुने हुए का निशान हटाएं"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ऊंचाई घटाएं"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ऊंचाई बढ़ाएं"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"सभी हटाएं"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"मैनेज करें"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"सूचना सेटिंग"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"सूचनाओं का इतिहास"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"साइलेंट मोड में मिली सूचनाएं"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"कोई सूचना नहीं है"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"कोई नई सूचना नहीं है"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"लगातार सूचनाएं आने पर आवाज़ कम करने की सेटिंग चालू है"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"एक साथ कई सूचनाएं मिलने पर, डिवाइस में सूचनाओं से होने वाली आवाज़ और सूचनाएं, दो मिनट के लिए अपने-आप कम हो जाएंगी."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करें"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुरानी सूचाएं देखने के लिए अनलॉक करें"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"चालू है"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रैकिंग चालू है"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलने के लिए टैप करें"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन पर, दाईं ओर या नीचे के ऐप पर स्विच करने के लिए"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन पर, बाईं ओर या ऊपर के ऐप पर स्विच करने के लिए"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीन के दौरान: एक ऐप्लिकेशन को दूसरे ऐप्लिकेशन से बदलें"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"अगली भाषा पर स्विच करने के लिए"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"पिछली भाषा पर स्विच करने के लिए"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"मौजूदा ऐप्लिकेशन"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सुलभता"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट को पसंद के मुताबिक बनाएं"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शॉर्टकट खोजें"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"खोज का कोई नतीजा नहीं मिला"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"पसंद के मुताबिक बनाएं"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"हो गया"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"कीबोर्ड का इस्तेमाल करके नेविगेट करें"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट के बारे में जानें"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचपैड का इस्तेमाल करके नेविगेट करें"</string>
@@ -1445,7 +1456,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होम स्क्रीन पर जाएं"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"बहुत बढ़िया!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"अब आपको इस बारे में जानकारी है कि हाथ के जेस्चर का इस्तेमाल करके होम स्क्रीन पर कैसे जाते हैं"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"अब आपको हाथ के जेस्चर का इस्तेमाल करके होम स्क्रीन पर जाने का तरीका पता चल गया है"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और फिर होल्ड करें"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"बहुत बढ़िया!"</string>
@@ -1469,9 +1480,9 @@
<string name="back_edu_notification_title" msgid="5624780717751357278">"वापस जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
<string name="back_edu_notification_content" msgid="2497557451540954068">"तीन उंगलियों से बाईं या दाईं ओर स्वाइप करें. ज़्यादा जेस्चर के बारे में जानने के लिए टैप करें."</string>
<string name="home_edu_notification_title" msgid="6097902076909654045">"होम पर जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
- <string name="home_edu_notification_content" msgid="6631697734535766588">"तीन उंगलियों से ऊपर की ओर स्वाइप करें. जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
+ <string name="home_edu_notification_content" msgid="6631697734535766588">"तीन उंगलियों से ऊपर की ओर स्वाइप करें. ज़्यादा जेस्चर के बारे में जानने के लिए टैप करें."</string>
<string name="overview_edu_notification_title" msgid="1265824157319562406">"हाल ही में इस्तेमाल हुए ऐप्लिकेशन देखने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
- <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन उंगलियों से ऊपर की ओर स्वाइप करें और दबाकर रखें. जेस्चर की ज़्यादा जानकारी पाने के लिए टैप करें."</string>
+ <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन उंगलियों से ऊपर की ओर स्वाइप करके दबाकर रखें. ज़्यादा जेस्चर के बारे में जानने के लिए टैप करें."</string>
<string name="all_apps_edu_notification_title" msgid="372262997265569063">"सभी ऐप्लिकेशन देखने के लिए, कीबोर्ड का इस्तेमाल करें"</string>
<string name="all_apps_edu_notification_content" msgid="3255070575694025585">"किसी भी समय ऐक्शन बटन दबाएं. हाथ के जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
<string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा, अब ब्राइटनेस स्लाइडर का हिस्सा है"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ऐप्लिकेशन से मिली जानकारी"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिसप्ले"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"कोई जानकारी नहीं है"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"टाइल रीसेट करें"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"क्या आपको टाइल, उनके ओरिजनल क्रम और साइज़ पर रीसेट करने हैं?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"क्या सभी टाइल रीसेट करनी हैं?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"क्विक सेटिंग टाइल, डिवाइस की ओरिजनल सेटिंग पर रीसेट हो जाएंगी"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index b89eeb3c0f0f..6aa90789c246 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"बंद हैं"</item>
<item msgid="3028994095749238254">"चालू हैं"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index cb7a1931be4d..3a01d94ef510 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite li snimati zaslon?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimanje jedne aplikacije"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimanje cijelog zaslona"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Snimanje cijelog zaslona: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kad snimate cijeli zaslon, snima se sve što se prikazuje na zaslonu. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kad snimate aplikaciju, snima se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimanje zaslona"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutačno snimate aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Dijeljenje zaslona"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Dijeljenje sadržaja"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Želite li zaustaviti dijeljenje zaslona?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Želite li zaustaviti dijeljenje?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutačno dijelite cijeli zaslon s aplikacijom <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutačno dijelite cijeli zaslon s aplikacijom"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutačno dijelite aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutačno dijelite aplikaciju"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Trenutačno dijelite sadržaj s aplikacijom"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi dijeljenje"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitiranje zaslona"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite li prestati emitirati?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati kameru uređaja?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati kameru i mikrofon uređaja?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeti zaključanog zaslona"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Svi vide widgete na vašem zaključanom zaslonu, čak i ako je tablet zaključan."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira widgeta"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Smanjenje visine"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Povećanje visine"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeti na zaključanom zaslonu"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Povijest"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Postavke obavijesti"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Povijest obavijesti"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Bešumno"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavijesti"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nema obavijesti"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavijesti"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stišavanje obavijesti je uključeno"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Stišavanje obavijesti sada je uključeno"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Glasnoća/upozorenja uređaja automatski se stišavaju do 2 min kad primite previše obavijesti odjednom"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starije obavijesti"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promijenili način softvera zvona"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"način softvera zvona"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak na aplikaciju zdesna ili ispod uz podijeljeni zaslon"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prelazak na aplikaciju slijeva ili iznad uz podijeljeni zaslon"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tijekom podijeljenog zaslona: zamijeni aplikaciju drugom"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prelazak na sljedeći jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prelazak na prethodni jezik"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutačna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodba tipkovnih prečaca"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tipkovnice"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tipkovnim prečacima"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Pružaju aplikacije"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Vraćanje kartica na zadano"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Želite li vratiti kartice na zadani redoslijed i veličine?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite li poništiti sve pločice?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brze postavke vratit će se na izvorne postavke uređaja"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index df0b78664cba..3f8841afb4f2 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Isključeno"</item>
<item msgid="3028994095749238254">"Uključeno"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index b09947fc2600..c9e20f8359be 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rögzíti a képernyőt?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Egyetlen alkalmazás rögzítése"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Teljes képernyő rögzítése"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Teljes képernyő rögzítése: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"A teljes képernyő rögzítése esetén a képernyőn megjelenő minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Alkalmazás rögzítésekor az adott alkalmazásban megjelenített vagy lejátszott minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Képernyő rögzítése"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jelenleg a következőről készít felvételt: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Felvétel leállítása"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Képernyő megosztása…"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Tartalom megosztása…"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Leállítja a képernyőmegosztást?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Leállítja a megosztást?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jelenleg megosztja a teljes képernyőt a következővel: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jelenleg megosztja a teljes képernyőt egy alkalmazással"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jelenleg megosztja a következőt: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jelenleg megoszt egy alkalmazást"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Jelenleg megoszt valamit egy alkalmazással"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Megosztás leállítása"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Képernyőtartalom átküldése…"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Leállítja az átküldést?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Bevitel"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hallókészülék"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Bekapcsolás…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Beállításkészlet"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Élő feliratozás"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Feloldja az eszköz mikrofonjának letiltását?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Feloldja az eszköz kamerájának letiltását?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Feloldja az eszköz kamerájának és mikrofonjának letiltását?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"A lezárási képernyő moduljai"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Bárki megtekintheti a modulokat a lezárási képernyőjén, még ha a táblagépe zárolva is van."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"modul kijelölésének megszüntetése"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Magasság csökkentése"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Magasság növelése"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"A lezárási képernyő moduljai"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Az összes törlése"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kezelés"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Előzmények"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Értesítési beállítások"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Értesítéselőzmények"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Új"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Néma"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Értesítések"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Indítás most"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nincs értesítés"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nincsenek új értesítések"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Értesítések befagyasztása bekapcsolva"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Az eszköz hangerejét és értesítéseit a rendszer automatikusan legfeljebb két percig csökkenti, ha egyszerre túl sok értesítést kap."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Igen"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"A régebbiek feloldás után láthatók"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Rögzített"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Fejkövetés"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Koppintson a csengés módjának módosításához"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"csengés módja"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Váltás a jobb oldalt, illetve lent lévő appra osztott képernyő esetén"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Váltás a bal oldalt, illetve fent lévő appra osztott képernyő esetén"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Osztott képernyőn: az egyik alkalmazás lecserélése egy másikra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Bevitel"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Váltás a következő nyelvre"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Váltás az előző nyelvre"</string>
@@ -1411,23 +1411,34 @@
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Legutóbbi alkalmazások"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Osztott képernyő"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazás-parancsikonok"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazásikonok"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Jelenlegi alkalmazás"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"A billentyűparancsok személyre szabása"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nincsenek keresési találatok"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Személyre szabás"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kész"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Billentyűzetbeállítások"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigáció a billentyűzet segítségével"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Billentyűparancsok megismerése"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigálás az érintőpaddal"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Alkalmazás által biztosított"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kijelző"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ismeretlen"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Mozaikok visszaállítása"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Visszaállítja a mozaikok eredeti sorrendjét és méretét?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Visszaállítja az összes mozaikot?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Az összes Gyorsbeállítások mozaik visszaáll az eszköz eredeti beállításaira"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index bbd6bc0ebbbb..76b3410ab6cb 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Ki"</item>
<item msgid="3028994095749238254">"Be"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 174b52622fd2..1d46bc2c86da 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Տեսագրե՞լ ձեր էկրանը"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Տեսագրել մեկ հավելված"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Տեսագրել ամբողջ էկրանը"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Տեսագրել ամբողջ էկրանը՝ %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Երբ դուք տեսագրում եք ամբողջ էկրանը, էկրանին ցուցադրվող ամեն ինչ տեսագրվում է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Երբ դուք որևէ հավելված եք տեսագրում, հավելվածում ցուցադրվող կամ նվագարկվող ամեն ինչ տեսագրվում է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Տեսագրել էկրանը"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Դուք ներկայումս տեսագրում եք <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Կանգնեցնել տեսագրումը"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Միացված է էկրանի ցուցադրումը"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Բովանդակության փոխանցում"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Դադարեցնե՞լ էկրանի ցուցադրումը"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Չեղարկե՞լ փոխանցումը"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Դուք ներկայումս կիսվում եք ձեր էկրանով <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> հավելվածի հետ"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Դուք ներկայումս կիսվում եք ձեր էկրանով հավելվածի հետ"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Դուք ներկայումս կիսվում եք <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> հավելվածով"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Դուք ներկայումս կիսվում եք հավելվածով"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Դուք ներկայումս բովանդակություն եք փոխանցում այս հավելվածին"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Դադարեցնել էկրանի ցուցադրումը"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Էկրանի հեռարձակում"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Կանգնեցնե՞լ հեռարձակումը"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Մուտքագրում"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Լսողական սարք"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Միացում…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Չհաջողվեց թարմացնել կարգավորումների հավաքածուն"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Կարգավորումների հավաքածու"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Կենդանի ենթագրեր"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Արգելահանե՞լ սարքի տեսախցիկը և խոսափողը"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Կողպէկրանի վիջեթներ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Բոլորը կարող են դիտել ձեր կողպէկրանի վիջեթները, նույնիսկ եթե պլանշետը կողպված է"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"չեղարկել վիջեթի ընտրությունը"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Նվազեցնել բարձրությունը"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Ավելացնել բարձրությունը"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Կողպէկրանի վիջեթներ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Մաքրել բոլորը"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Կառավարել"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Պատմություն"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Ծանուցումների կարգավորումներ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Ծանուցումների պատմություն"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Նոր"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Անձայն"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ծանուցումներ չկան"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Նոր ծանուցումներ չկան"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ծանուցումների ձայնի իջեցումը միացված է"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Ծանուցումների ձայնի իջեցումը միացված է"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Սարքի և ծանուցումների ձայնն ավտոմատ իջեցվում է մինչև 2 րոպեով, երբ շատ ծանուցումներ եք ստանում։"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Անջատել"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ապակողպեք՝ տեսնելու հին ծանուցումները"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Ֆիքսված"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Գլխի շարժումների հետագծում"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Հպեք՝ զանգակի ռեժիմը փոխելու համար"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"զանգակի ռեժիմ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string>
@@ -815,7 +812,7 @@
<string name="keyboard_key_back" msgid="4185420465469481999">"Հետ"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Բացատ"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"Մուտք"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Նվագարկում/դադար"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Կանգնեցնել"</string>
@@ -860,20 +857,22 @@
<string name="group_system_go_back" msgid="2730322046244918816">"Հետ գնալ"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Անցնել հիմնական էկրան"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Դիտել վերջին հավելվածները"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Առաջ անցնել վերջին հավելվածների միջով"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Հետ անցնել վերջին հավելվածների միջով"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Առաջ անցնել վերջին հավելվածներով"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Հետ անցնել վերջին հավելվածներով"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Բացել հավելվածների ցանկը"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Բացել կարգավորումները"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Բացել Օգնականը"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Կողպէկրան"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Ստեղծել նշում"</string>
- <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Բազմախնդրություն"</string>
+ <string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Բազմախնդրու­թյուն"</string>
<string name="system_multitasking_rhs" msgid="8714224917276297810">"Տրոհել էկրանը և տեղավորել այս հավելվածը աջում"</string>
<string name="system_multitasking_lhs" msgid="8402954791206308783">"Տրոհել էկրանը և տեղավորել այս հավելվածը ձախում"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Տրոհված էկրանից անցնել լիաէկրան ռեժիմ"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Անցեք աջ կողմի կամ ներքևի հավելվածին տրոհված էկրանի միջոցով"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Տրոհված էկրանի ռեժիմում մեկ հավելվածը փոխարինել մյուսով"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ներածում"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Անցնել հաջորդ լեզվին"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Անցնել նախորդ լեզվին"</string>
@@ -1407,7 +1406,7 @@
<string name="shortcut_helper_category_system" msgid="462110876978937359">"Համակարգ"</string>
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Համակարգի կառավարման տարրեր"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Համակարգային հավելվածներ"</string>
- <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմախնդրություն"</string>
+ <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմախնդրու­թյուն"</string>
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Վերջին օգտագործած հավելվածները"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Տրոհված էկրան"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ներածում"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Այս հավելվածը"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Հատուկ գործառույթներ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Կարգավորեք ստեղնային դյուրանցումներ"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Որոնման արդյունքներ չկան"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Կարգավորել"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Պատրաստ է"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Սովորեք օգտագործել ստեղնային դյուրանցումները"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Կողմնորոշվեք ձեր հպահարթակի օգնությամբ"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Տրամադրվել են հավելվածների կողմից"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Էկրան"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Անհայտ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Սալիկների վերակայում"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Վերականգնե՞լ սալիկների սկզբնական դասավորությունն ու չափսերը"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Զրոյացնե՞լ բոլոր սալիկները"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Արագ կարգավորումների բոլոր սալիկները կզրոյացվեն սարքի սկզբնական կարգավորումների համաձայն։"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index eb77ccf8c1fc..ce930c39aa83 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Անջատված է"</item>
<item msgid="3028994095749238254">"Միացված է"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index e706b272458a..1535314317a8 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekam layar Anda?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekam satu aplikasi"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekam seluruh layar"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Rekam seluruh layar: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Saat Anda merekam seluruh layar, semua hal yang ditampilkan di layar akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Jika Anda merekam aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekam layar"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Anda sedang merekam <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Berhenti merekam"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Membagikan layar"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Membagikan konten"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hentikan berbagi layar?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Berhenti berbagi?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Anda sedang berbagi seluruh layar dengan <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Anda sedang berbagi seluruh layar dengan aplikasi"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Anda sedang berbagi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Anda sedang berbagi aplikasi"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Anda sedang berbagi dengan aplikasi"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Berhenti berbagi"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Mentransmisikan layar"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hentikan transmisi?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu dengar"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Mengaktifkan…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Teks Otomatis"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget layar kunci"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Siapa saja dapat melihat widget di layar kunci Anda, meskipun tablet terkunci."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"batalkan pilihan widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Kurangi tinggi"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Tambah tinggi"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget layar kunci"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hapus semua"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kelola"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histori"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Setelan notifikasi"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Histori notifikasi"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Baru"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Senyap"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikasi"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulai sekarang"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Tidak ada notifikasi"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Tidak ada notifikasi baru"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Pengurangan suara dan getaran notifikasi aktif"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Saat Anda menerima terlalu banyak notifikasi sekaligus, volume dan getaran perangkat akan otomatis dikurangi hingga selama 2 menit."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Nonaktifkan"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat notifikasi lama"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Tetap"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pelacakan Gerak Kepala"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketuk untuk mengubah mode pendering"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"mode pendering"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -814,7 +812,7 @@
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"Spasi"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Play/Pause"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Beralih ke aplikasi di bagian kanan atau bawah saat menggunakan layar terpisah"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Beralih ke aplikasi di bagian kiri atau atas saat menggunakan layar terpisah"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Dalam layar terpisah: ganti salah satu aplikasi dengan yang lain"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih ke bahasa berikutnya"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih ke bahasa sebelumnya"</string>
@@ -1368,7 +1368,7 @@
<string name="call_from_work_profile_text" msgid="2856337395968118274">"Organisasi Anda hanya mengizinkan menelepon dari aplikasi kerja"</string>
<string name="call_from_work_profile_action" msgid="2937701298133010724">"Beralih ke profil kerja"</string>
<string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Instal aplikasi telepon kerja"</string>
- <string name="call_from_work_profile_close" msgid="5830072964434474143">"Batalkan"</string>
+ <string name="call_from_work_profile_close" msgid="5830072964434474143">"Batal"</string>
<string name="lock_screen_settings" msgid="6152703934761402399">"Sesuaikan layar kunci"</string>
<string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Buka kunci untuk menyesuaikan layar kunci"</string>
<string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi tidak tersedia"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikasi Saat Ini"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Menyesuaikan pintasan keyboard"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Telusuri pintasan"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tidak ada hasil penelusuran"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setelan Keyboard"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menavigasi menggunakan keyboard"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Pelajari pintasan keyboard"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Menavigasi menggunakan touchpad"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disediakan oleh aplikasi"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Tampilan"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset kartu"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset kartu ke urutan dan ukuran default?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset semua kartu?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua kartu Setelan Cepat akan direset ke setelan asli perangkat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index a415f644fb48..5570edb557d4 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Nonaktif"</item>
<item msgid="3028994095749238254">"Aktif"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 267291069366..edead8fdc433 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Viltu taka upp skjáinn?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Taka upp eitt forrit"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Taka upp allan skjáinn"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Taka upp allan skjáinn: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Þegar þú tekur upp allan skjáinn verður allt sem er sýnilegt á skjánum tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Þegar þú tekur upp forrit verður allt sem er sýnilegt eða spilað í forritinu tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Taka upp skjá"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Þú ert að taka upp <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stöðva upptöku"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deilir skjá"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Deilir efni"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hætta að deila skjá?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Hætta að deila?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Þú ert að deila öllum skjánum með <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Þú ert að deila öllum skjánum með forriti"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Þú ert að deila <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Þú ert að deila forriti"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Þú ert að deila með forriti"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hætta að deila"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Varpar skjá"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hætta að varpa?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Inntak"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Heyrnartæki"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Kveikir…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Skjátextar í rauntíma"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Græjur á lásskjá"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Hver sem er getur séð græjur á lásskjánum þínum, jafnvel þótt spjaldtölvan sé læst."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"afturkalla val á græju"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Lækka"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Hækka"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Græjur fyrir lásskjá"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Engar tilkynningar"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Engar nýjar tilkynningar"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Kveikt er á tilkynningadempun"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Nú er kveikt á tilkynningadempun"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Lækkað er sjálfkrafa í hljóðstyrk og áminningum tækisins í allt að tvær mínútur þegar þú færð of margar tilkynningar í einu."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Slökkva"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Taktu úr lás til að sjá eldri tilkynningar"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fast"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rakning höfuðhreyfinga"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ýta til að skipta um hringjarastillingu"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"hringistilling"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skiptu í forrit til hægri eða fyrir neðan þegar skjáskipting er notuð"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skiptu í forrit til vinstri eða fyrir ofan þegar skjáskipting er notuð"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Í skjáskiptingu: Skipta forriti út fyrir annað forrit"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Innsláttur"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Skipta yfir í næsta tungumál"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skipta yfir í fyrra tungumál"</string>
@@ -1408,24 +1409,35 @@
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string>
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nýleg forrit"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skjáskipting"</string>
- <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inntak"</string>
+ <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Innsláttur"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Núverandi forrit"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sérsníddu flýtilykla"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leita að flýtileiðum"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Engar leitarniðurstöður"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sérsníða"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Lokið"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dragkló"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Stillingar lyklaborðs"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Flettu með því að nota lyklaborðið"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Kynntu þér flýtilykla"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Flettu með því að nota snertiflötinn"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Frá forritum"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjár"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Óþekkt"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Endurstilla flísar"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Endurstilla flísar í upphaflega röð og stærð?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Endurstilla alla reiti?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Allir flýtistillingareitir munu endurstillast á upprunalegar stillingar tækisins"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index c9befd6574c4..893ab6ce3bb6 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Slökkt"</item>
<item msgid="3028994095749238254">"Kveikt"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index fd56c5b69f5a..d7a279be03ea 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Registrare lo schermo?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Registra un\'app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Registra l\'intero schermo"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Registra l\'intero schermo: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando registri l\'intero schermo, tutto ciò che viene mostrato sullo schermo viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando registri un\'app, tutto ciò che viene mostrato o riprodotto al suo interno viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Registra lo schermo"</string>
@@ -327,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingresso"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Apparecchi acustici"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Attivazione…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotazione automatica"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotazione automatica dello schermo"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Posizione"</string>
@@ -415,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sottotitoli in tempo reale"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vuoi sbloccare il microfono del dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vuoi sbloccare la fotocamera del dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vuoi sbloccare la fotocamera e il microfono del dispositivo?"</string>
@@ -578,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Cancella tutto"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestisci"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Cronologia"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Impostazioni di notifica"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Cronologia delle notifiche"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nuove"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Notifiche silenziose"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifiche"</string>
@@ -592,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Avvia adesso"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nessuna notifica"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nessuna nuova notifica"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Attenuazione delle notifiche attivata"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e avvisi vengono ridotti automaticamente per un massimo di 2 minuti quando ricevi troppe notifiche contemporaneamente."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Disattiva"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Sblocca per vedere le notifiche meno recenti"</string>
@@ -700,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fisso"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rilev. movim. testa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tocca per cambiare la modalità della suoneria"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modalità suoneria"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenzia"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string>
@@ -869,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passa all\'app a destra o sotto mentre usi lo schermo diviso"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passa all\'app a sinistra o sopra mentre usi lo schermo diviso"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Con lo schermo diviso: sostituisci un\'app con un\'altra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inserimento"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Passa alla lingua successiva"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Passa alla lingua precedente"</string>
@@ -1410,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App corrente"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizza scorciatoie da tastiera"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nessun risultato di ricerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizza"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fine"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Punto di trascinamento"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Impostazioni tastiera"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviga usando la tastiera"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informazioni sulle scorciatoie da tastiera"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviga usando il touchpad"</string>
@@ -1480,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Forniti dalle app"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Sconosciuti"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reimposta riquadri"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Ripristinare l\'ordine e le dimensioni originali dei riquadri?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reimpostare tutti i riquadri?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tutti i riquadri Impostazioni rapide verranno reimpostati sulle impostazioni originali del dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 2fd4f6d49aef..784a3091ef19 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Disattivi"</item>
<item msgid="3028994095749238254">"Attivi"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 384c680b116f..9064d3dac5cd 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"להקליט את המסך?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"הקלטה של אפליקציה אחת"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"הקלטה של כל המסך"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"‏הקלטת התוכן של כל המסך: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"כשמקליטים את כל המסך, כל מה שמופיע במסך מוקלט. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"כשמקליטים אפליקציה, כל מה שרואים או מפעילים בה מוקלט. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"הקלטת המסך"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"מתבצעת כרגע הקלטה של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"הפסקת ההקלטה"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"שיתוף המסך מתבצע"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"התוכן משותף"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"להפסיק את שיתוף המסך?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"להפסיק את השיתוף?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"מתבצע כרגע שיתוף של כל המסך שלך עם <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"מתבצע כרגע שיתוף של כל המסך שלך עם אפליקציה"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"מתבצע כרגע שיתוף של <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"מתבצע כרגע שיתוף של אפליקציה"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"מתבצע כרגע שיתוף עם אפליקציה"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"הפסקת השיתוף"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"‏הפעלת Cast של המסך מתבצעת"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"‏להפסיק את פעולת ה-Cast?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"קלט"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"מכשירי שמיעה"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ההפעלה מתבצעת…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"לא ניתן לעדכן את ההגדרה הקבועה מראש"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"הגדרה קבועה מראש"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"כתוביות מיידיות"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"לבטל את חסימת המצלמה והמיקרופון של המכשיר?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ווידג\'טים במסך הנעילה"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"כולם יכולים לראות את הווידג\'טים במסך הנעילה שלך, גם אם הטאבלט נעול."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ביטול הבחירה בווידג\'ט"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"הקטנת הגובה"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"הגדלת הגובה"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ווידג\'טים במסך הנעילה"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"אין התראות"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"אין התראות חדשות"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"הפוגת התראות מופעלת"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"עוצמת הקול וההתראות במכשיר מופחתות אוטומטית למשך עד 2 דקות כשמתקבלות יותר מדי התראות בבת אחת."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"השבתה"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"יש לבטל את הנעילה כדי לראות התראות ישנות"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב סטטי"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב אחר תנועות הראש"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"מצב תוכנת הצלצול"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"מעבר לאפליקציה משמאל או למטה בזמן שימוש במסך מפוצל"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"מעבר לאפליקציה מימין או למעלה בזמן שימוש במסך מפוצל"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"כשהמסך מפוצל: החלפה בין אפליקציה אחת לאחרת"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"קלט"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"מעבר לשפה הבאה"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"מעבר לשפה הקודמת"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"האפליקציה הנוכחית"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"נגישות"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"התאמה אישית של מקשי הקיצור"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"אין תוצאות חיפוש"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"התאמה אישית"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"סיום"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ניווט באמצעות המקלדת"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"מידע על מקשי קיצור"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ניווט באמצעות לוח המגע"</string>
@@ -1443,7 +1456,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר למסך הבית"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"מחליקים כלפי מעלה עם שלוש אצבעות על לוח המגע"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"מעולה!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"השלמת את תנועת החזרה למסך הבית"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"סיימת לתרגל את תנועת החזרה למסך הבית"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"הצגת האפליקציות האחרונות"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string>
@@ -1459,7 +1472,7 @@
<string name="volume_undo_action" msgid="5815519725211877114">"ביטול"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"כדי לחזור אחורה, מחליקים שמאלה או ימינה עם שלוש אצבעות על לוח המגע."</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"כדי לעבור למסך הבית, מחליקים למעלה עם שלוש אצבעות על לוח המגע"</string>
- <string name="overview_edu_toast_content" msgid="5797030644017804518">"כדי לראות את האפליקציות האחרונות, מחליקים למעלה עם שלוש אצבעות על לוח המגע ולוחצים לחיצה ארוכה"</string>
+ <string name="overview_edu_toast_content" msgid="5797030644017804518">"כדי לראות את האפליקציות האחרונות, מחליקים למעלה לוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string>
<string name="all_apps_edu_toast_content" msgid="8807496014667211562">"כדי לראות את כל האפליקציות, מקישים על מקש הפעולה במקלדת"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"מצונזר"</string>
<string name="redacted_notification_single_line_text" msgid="8684166405005242945">"צריך לבטל את הנעילה כדי לראות"</string>
@@ -1467,9 +1480,9 @@
<string name="back_edu_notification_title" msgid="5624780717751357278">"אפשר להשתמש בלוח המגע כדי לחזור אחורה"</string>
<string name="back_edu_notification_content" msgid="2497557451540954068">"מחליקים ימינה או שמאלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
<string name="home_edu_notification_title" msgid="6097902076909654045">"איך להשתמש בלוח המגע כדי לעבור למסך הבית"</string>
- <string name="home_edu_notification_content" msgid="6631697734535766588">"מחליקים למעלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
+ <string name="home_edu_notification_content" msgid="6631697734535766588">"מחליקים למעלה עם שלוש אצבעות. אפשר להקיש כדי לקבל מידע נוסף על התנועות."</string>
<string name="overview_edu_notification_title" msgid="1265824157319562406">"איך להשתמש בלוח המגע כדי לראות את האפליקציות האחרונות"</string>
- <string name="overview_edu_notification_content" msgid="3578204677648432500">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
+ <string name="overview_edu_notification_content" msgid="3578204677648432500">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות. אפשר להקיש כדי לקבל מידע נוסף על התנועות."</string>
<string name="all_apps_edu_notification_title" msgid="372262997265569063">"איך להשתמש במקלדת כדי לראות את כל האפליקציות"</string>
<string name="all_apps_edu_notification_content" msgid="3255070575694025585">"בכל שלב אפשר ללחוץ על מקש הפעולה. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
<string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"התכונה \'מעומעם במיוחד\' נוספה לפס ההזזה לבהירות"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"מסופקים על ידי אפליקציות"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"מסך"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"איפוס המשבצות"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"לאפס את המשבצות לסדר ולגודל המקורי שלהן?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"לאפס את כל הלחצנים?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"כל הלחצנים ב\'הגדרות מהירות\' יאופסו להגדרות המקוריות של המכשיר"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index b5cb476484b2..e2ba375763c2 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"מצב מושבת"</item>
<item msgid="3028994095749238254">"מצב פעיל"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a0a3b2bdd403..a23dac840796 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"画面を録画しますか?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"1 つのアプリを録画"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"画面全体を録画"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"画面全体を録画: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"画面全体を録画すると、画面に表示されるものがすべて録画されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"アプリを録画すると、そのアプリで表示または再生される内容がすべて録画されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"画面を録画"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"現在、<xliff:g id="APP_NAME">%1$s</xliff:g>を録画しています"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"録画を停止"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"画面を共有しています"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"コンテンツの共有"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"画面の共有を停止しますか?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"共有を停止しますか?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"現在、画面全体を<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>と共有しています"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"現在、画面全体をアプリと共有しています"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"現在、<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>を共有しています"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"現在、アプリを共有しています"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"現在、アプリと共有しています"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"共有を停止"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"画面をキャストしています"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"キャストを停止しますか?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"入力"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"補聴器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ON にしています…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動回転"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"画面を自動回転します"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"プリセットを更新できませんでした"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"プリセット"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"自動字幕起こし"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"デバイスのカメラとマイクのブロックを解除しますか?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ロック画面ウィジェット"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"タブレットがロックされていても、ロック画面のウィジェットは誰でも確認できます。"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ウィジェットの選択を解除する"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"高さを低くする"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"高さを高くする"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ロック画面ウィジェット"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"通知はありません"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"新しい通知はありません"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知のクールダウンが ON になっています"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"通知のクールダウンを ON にしました"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"一度に多くの通知が届いた場合に、最長 2 分間自動的にデバイスの音量が小さくなりアラートも減ります。"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"OFF にする"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ロック解除して以前の通知を表示"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘ⁠ッ⁠ド ト⁠ラ⁠ッ⁠キ⁠ン⁠グ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"着信音のモード"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string>
@@ -872,10 +871,12 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"分割画面の使用時に右側または下部のアプリに切り替える"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"分割画面の使用時に左側または上部のアプリに切り替える"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"分割画面中: アプリを順に置換する"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"入力"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"次の言語に切り替える"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"前の言語に切り替える"</string>
- <string name="input_access_emoji" msgid="8105642858900406351">"絵文字にアクセス"</string>
+ <string name="input_access_emoji" msgid="8105642858900406351">"絵文字にアクセスする"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"音声入力にアクセス"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"アプリ"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"アシスタント"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"現在のアプリ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ユーザー補助"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"キーボード ショートカットをカスタマイズする"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"検索結果がありません"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"カスタマイズ"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完了"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ドラッグ ハンドル"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"キーボードを使用して移動する"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"キーボード ショートカットの詳細"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"タッチパッドを使用して移動する"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"アプリから提供"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ディスプレイ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"タイルのリセット"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"タイルを元の順序とサイズにリセットしますか?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"すべてのタイルをリセットしますか?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"すべてのクイック設定タイルがデバイスの元の設定にリセットされます"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 790445c93c14..683a4e889fbb 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"OFF"</item>
<item msgid="3028994095749238254">"ON"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 471957e044cb..d0ff84296f3e 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"გსურთ თქვენი ეკრანის ჩაწერა?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ერთი აპის ჩაწერა"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"მთლიანი ეკრანის ჩაწერა"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"მთლიანი ეკრანის ჩაწერა: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"მთლიანი ეკრანის ჩაწერისას ჩაიწერება ყველაფერი, რაც თქვენს ეკრანზე გამოჩნდება. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"აპის ჩაწერისას ჩაიწერება ყველაფერი, რაც ამ აპში გამოჩნდება ან დაიკვრება. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ეკრანის ჩაწერა"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"თქვენ ამჟამად იწერთ <xliff:g id="APP_NAME">%1$s</xliff:g>-ს"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ჩაწერის შეწყვეტა"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"მიმდინარეობს ეკრანის გაზიარება"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"კონტენტის გაზიარება"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"გსურთ ეკრანის გაზიარების შეწყვეტა?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"შეწყდეს გაზიარება?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-ს"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს აპს"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"თქვენ ამჟამად აზიარებთ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-ს"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"თქვენ ამჟამად აზიარებთ აპს"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"თქვენ ამჟამად აზიარებთ აპთან"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"გაზიარების შეწყვეტა"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"მიმდინარეობს ეკრანის ტრანსლირება"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"გსურთ ტრანსლირების შეწყვეტა?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"შეყვანა"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"სმენის მოწყობილობები"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ირთვება…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ავტოროტაცია"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ეკრანის ავტომატური შეტრიალება"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ავტოსუბტიტრები"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ჩაკეტილი ეკრანის ვიჯეტები"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ნებისმიერს შეუძლია თქვენს ჩაკეტილ ეკრანზე ვიჯეტების ნახვა, თუნდაც ტაბლეტი ჩაკეტილი იყოს."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ვიჯეტის არჩევის გაუქმება"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"სიმაღლის შემცირება"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"სიმაღლის გაზრდა"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"დაბლოკილი ეკრანის ვიჯეტები"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ყველას გასუფთავება"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"მართვა"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ისტორია"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"შეტყობინების პარამეტრები"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"შეტყობინების ისტორია"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ახალი"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ჩუმი"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"შეტყობინებები"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"შეტყობინებები არ არის."</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ახალი შეტყობინებები არ არის"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"შეტყობინების განტვირთვის პერიოდი ჩართულია"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"შეტყობინების განტვირთვის პერიოდი ჩართულია"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"მოწყობილობის ხმა და გაფრთხილებები მცირდება 2 წუთის განმავლობაში, როდესაც ბევრ შეტყობინებას მიიღებთ ერთდროულად."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"გამორთვა"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"განბლოკეთ ძველი შეტყობინებების სანახავად"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ფიქსირებული"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ხმის მიდევნებით"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"შეეხეთ მრეკავის რეჟიმის შესაცვლელად"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"მრეკავის რეჟიმი"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ეკრანის გაყოფის გამოყენებისას აპზე მარჯვნივ ან ქვემოთ გადართვა"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ეკრანის გაყოფის გამოყენებისას აპზე მარცხნივ ან ზემოთ გადართვა"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ეკრანის გაყოფის დროს: ერთი აპის მეორით ჩანაცვლება"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"შეყვანა"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"შემდეგ ენაზე გადართვა"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"წინა ენაზე გადართვა"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"მიმდინარე აპი"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"მისაწვდომობა"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"კლავიატურის მალსახმობები"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"კლავიატურის მალსახმობების მორგება"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ძიების შედეგები არ არის"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"მორგება"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"მზადაა"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"სახელური ჩავლებისთვის"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"კლავიატურის პარამეტრები"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ნავიგაცია კლავიატურის გამოყენებით"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"კლავიატურის მალსახმობების სწავლა"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ნავიგაცია სენსორული პანელის გამოყენებით"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"მოწოდებულია აპების მიერ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ეკრანი"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"უცნობი"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"მოზაიკის ფილების გადაყენება"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"გსურთ მოზაიკის ფილების გადაყენება მათ ორიგინალ წყობაზე და ზომებზე?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"გსურთ ყველა ფილის გადაყენება?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"სწრაფი პარამეტრების ყველა ფილა გადაყენდება მოწყობილობის ორიგინალ პარამეტრებზე"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 21f8102036c9..7c13eb53d5d8 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"გამორთულია"</item>
<item msgid="3028994095749238254">"ჩართულია"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 3dbfd86b3f38..94f8711cc7a0 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Қолданба экранын жазасыз ба?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бір қолданба экранын жазу"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүкіл экранды жазу"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Бүкіл экранды жазу: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүкіл экранды жазған кезде, онда көрінетін барлық нәрсе жазылады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Қолданбаны жазған кезде, онда көрінетін не ойнатылатын барлық нәрсе жазылады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жазу"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Қазір қолданбадағы (<xliff:g id="APP_NAME">%1$s</xliff:g>) контентті жазып жатырсыз."</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Жазуды тоқтату"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экранды бөлісіп жатыр."</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Контент бөлісіліп жатыр"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлісуді тоқтатасыз ба?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Бөлісу тоқтатылсын ба?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Қазір бүкіл экранды қолданбамен (<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>) бөлісіп жатырсыз."</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Қазір бүкіл экранды қолданбамен бөлісіп жатырсыз."</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Қазір қолданбадағы (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) контентті бөлісіп жатырсыз."</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Қазір қолданбаны бөлісіп жатырсыз."</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Қазір қолданбамен бөлісіп жатырсыз."</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Бөлісуді тоқтату"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Экранды трансляциялап жатырсыз."</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Трансляциялау тоқтасын ба?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Кіріс"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Есту аппараттары"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылып жатыр…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонын блоктан шығару керек пе?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Құлып экранының виджеттері"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Планшет құлыпталып тұрса да, құлып экранындағы виджеттерді кез келген адам көре алады."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетті таңдаудан алу"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Биіктігін төмендету"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Биіктігін арттыру"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Құлып экранының виджеттері"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Барлығын тазарту"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Басқару"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Тарих"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Хабарландыру параметрлері"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Хабарландыру тарихы"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Жаңа"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Үнсіз"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Хабарландырулар"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Қазір бастау"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Хабарландырулар жоқ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңа хабарландырулар жоқ"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Хабарландыру дыбысын азайту параметрі қосулы"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Бір уақытта тым көп хабарландыру келсе, дыбыс деңгейі автоматты түрде азайтылып, хабарландырулар 2 минутқа кідіртіледі."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Өшіру"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ескі хабарландырулар үшін құлыпты ашыңыз"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Бекітілген"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"қоңырау режимі"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлінген экранда оң не төмен жақтағы қолданбаға ауысу"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлу кезінде: бір қолданбаны басқасымен алмастыру"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Енгізу"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Келесі тілге ауысу"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Алдыңғы тілге ауысу"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Қолданыстағы қолданба"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Арнайы мүмкіндіктер"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Пернелер тіркесімін бейімдеу"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Іздеу нәтижелері жоқ."</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Бейімдеу"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Дайын"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Пернетақта параметрлері"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Пернетақтамен жұмыс істеңіз"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Перне тіркесімдерін үйреніңіз."</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Сенсорлық тақтамен жұмыс істеңіз"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Қолданбалар ұсынған"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Дисплей"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгісіз"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Бөлшектерді бастапқы күйге қайтару"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Бөлшектерді бастапқы реті мен өлшеміне қайтару керек пе?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Барлық бөлшекті бастапқы күйге қайтару керек пе?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Барлық \"Жылдам параметрлер\" бөлшегі құрылғының бастапқы параметрлеріне қайтарылады."</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index cf3aa69992f1..2b4c1ac355a2 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Өшірулі"</item>
<item msgid="3028994095749238254">"Қосулы"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index b2c8977ab7ce..2b178181aa3f 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ថត​អេក្រង់​របស់អ្នកឬ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ថត​កម្មវិធី​ទោល"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ថតអេក្រង់ទាំងមូល"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ថតអេក្រង់ទាំងមូល៖ %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"នៅពេល​អ្នកកំពុង​ថតអេក្រង់​ទាំងមូល​របស់អ្នក អ្វីគ្រប់យ៉ាង​ដែលបង្ហាញ​នៅលើ​អេក្រង់​របស់អ្នក​ត្រូវបាន​ថត។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"នៅពេលអ្នក​កំពុង​ថតកម្មវិធី​ណាមួយ អ្វីគ្រប់យ៉ាង​ដែលបង្ហាញ ឬចាក់​នៅក្នុង​កម្មវិធីនោះ​ត្រូវបាន​ថត។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ថត​អេក្រង់"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"បច្ចុប្បន្ន អ្នកកំពុងថត <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ឈប់ថត"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"កំពុងបង្ហាញអេក្រង់"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"កំពុងចែករំលែកខ្លឹមសារ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ឈប់បង្ហាញអេក្រង់ឬ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"ឈប់​ចែករំលែក​ឬ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នកតាមរយៈ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នកតាមរយៈកម្មវិធីមួយ"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញកម្មវិធីមួយ"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"បច្ចុប្បន្ន អ្នកកំពុងចែករំលែក​ជាមួយកម្មវិធីមួយ"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ឈប់​បង្ហាញ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"កំពុង​បញ្ជូន​អេក្រង់"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ឈប់បញ្ជូនឬ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"បញ្ចូល"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"កំពុង​បើក..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិល​ស្វ័យ​ប្រវត្តិ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ទី​តាំង​"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"មិនអាច​ប្ដូរ​ការកំណត់ជាមុន​បានទេ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"កំណត់ជាមុន"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់​មីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់​កាមេរ៉ា​របស់ឧបករណ៍ឬ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ឈប់ទប់ស្កាត់​កាមេរ៉ា និងមីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ធាតុ​ក្រាហ្វិកអេក្រង់ចាក់សោ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"អ្នកគ្រប់គ្នាអាចមើលធាតុក្រាហ្វិកលើអេក្រង់ចាក់សោរបស់អ្នក ទោះបីជាថេប្លេតរបស់អ្នកត្រូវបានចាក់សោក៏ដោយ។"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ដក​ការ​ជ្រើសរើសធាតុ​ក្រាហ្វិក"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"បន្ថយកម្ពស់"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"បង្កើនកម្ពស់"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ធាតុ​ក្រាហ្វិកលើអេក្រង់ចាក់សោ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុ​ក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុ​ក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"សម្អាត​ទាំងអស់"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"គ្រប់គ្រង"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ប្រវត្តិ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"ការ​កំណត់​ការ​ជូនដំណឹង"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"ប្រវត្តិ​នៃការ​ជូន​ដំណឹង"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ថ្មី"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ស្ងាត់"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ការជូនដំណឹង"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើម​ឥឡូវ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"គ្មាន​ការ​ជូនដំណឹង"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"គ្មាន​ការ​ជូន​ដំណឹង​​ថ្មីៗទេ"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ការបន្ថយសំឡេងការជូនដំណឹងត្រូវបានបើក"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ឥឡូវនេះ ការបន្ថយសំឡេងការជូនដំណឹងត្រូវបានបើក"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"កម្រិតសំឡេង និងការជូនដំណឹងនៅលើឧបករណ៍របស់អ្នកត្រូវបានកាត់បន្ថយដោយស្វ័យប្រវត្តិរហូតដល់ 2 នាទី នៅពេលអ្នកទទួលបានការជូនដំណឹងច្រើនពេកក្នុងពេលតែមួយ។"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"បិទ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ដោះសោដើម្បីមើលការជូនដំណឹងចាស់ៗ"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ថេរ"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"រេតាមក្បាល"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ចុច​ដើម្បីប្ដូរ​មុខងារ​រោទ៍"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"មុខងារកម្មវិធី​រោទ៍"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទ​សំឡេង"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើក​សំឡេង"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string>
@@ -814,7 +811,7 @@
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"ដកឃ្លា"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Play/Pause"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ប្ដូរទៅកម្មវិធីនៅខាងស្ដាំ ឬខាងក្រោម ពេលកំពុងប្រើមុខងារ​បំបែកអេក្រង់"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ប្ដូរទៅកម្មវិធីនៅខាងឆ្វេង ឬខាងលើ ពេលកំពុងប្រើមុខងារ​បំបែកអេក្រង់"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ក្នុងអំឡុងពេលប្រើមុខងារបំបែកអេក្រង់៖ ជំនួសកម្មវិធីពីមួយទៅមួយទៀត"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"បញ្ចូល"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ប្ដូរទៅភាសាបន្ទាប់"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ប្ដូរទៅភាសាមុន"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"កម្មវិធីបច្ចុប្បន្ន"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ភាពងាយស្រួល"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់​ក្ដារ​ចុច"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ប្ដូរ​ផ្លូវកាត់​ក្ដារ​ចុចតាម​បំណង"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវ​កាត់ការស្វែងរក"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ស្វែងរកផ្លូវ​កាត់"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"គ្មាន​លទ្ធផល​ស្វែងរក​ទេ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ប្ដូរ​តាម​បំណង"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"រួចរាល់"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដង​អូស"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ការកំណត់​ក្ដារចុច"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ស្វែងយល់អំពីផ្លូវកាត់​ក្ដារ​ចុច"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"រុករកដោយប្រើផ្ទាំងប៉ះរបស់អ្នក"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ផ្ដល់ជូនដោយកម្មវិធី"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ផ្ទាំងបង្ហាញ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"មិនស្គាល់"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"កំណត់ប្រអប់ឡើងវិញ"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"កំណត់ប្រអប់ឡើងវិញទៅទំហំ និងលំដាប់ដើមរបស់ប្រអប់ទាំងនោះឬ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"កំណត់ប្រអប់ទាំងអស់​ឡើងវិញឬ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ប្រអប់​ការកំណត់រហ័សទាំងអស់នឹងកំណត់ឡើងវិញទៅការ​កំណត់ដើមរបស់ឧបករណ៍"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index 54790f6a028e..3c15fd3a35e4 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"បិទ"</item>
<item msgid="3028994095749238254">"បើក"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 20c367696858..b74a0535bdd4 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬೇಕೇ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ನೀವು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಮೇಲೆ ಗೋಚರಿಸುವ ಎಲ್ಲವನ್ನೂ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಎಲ್ಲವನ್ನೂ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ನೀವು ಪ್ರಸ್ತುತ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತಿದ್ದೀರಿ"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಿ"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ಪರದೆಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"ಕಂಟೆಂಟ್‌ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"ಹಂಚಿಕೊಳ್ಳುವುದನ್ನು ನಿಲ್ಲಿಸಬೇಕೇ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ನೀವು ಪ್ರಸ್ತುತ ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ನೀವು ಪ್ರಸ್ತುತ ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆ್ಯಪ್‌ನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ನೀವು ಪ್ರಸ್ತುತ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ನೀವು ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"ನೀವು ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳುತ್ತಿದ್ದೀರಿ"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಿ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ಬಿತ್ತರಿಸುವುದನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ಇನ್‌ಪುಟ್"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್‌"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್‍ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ವಿಜೆಟ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಬೇಡಿ"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ಎತ್ತರವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ಎತ್ತರವನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್‌ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ಯಾವುದೇ ಹೊಸ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ನೋಟಿಫಿಕೇಶನ್ ಕೂಲ್‌ಡೌನ್ ಆನ್ ಆಗಿದೆ"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ನೋಟಿಫಿಕೇಶನ್ ಕೂಲ್‌ಡೌನ್ ಈಗ ಆನ್ ಆಗಿದೆ"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ನೀವು ಏಕಕಾಲದಲ್ಲಿ ತೀರಾ ಹೆಚ್ಚು ನೋಟಿಫಿಕೇಶನ್‌‍‍ಗಳನ್ನು ಪಡೆದಾಗ 2 ನಿಮಿಷಗಳವರೆಗೆ ನಿಮ್ಮ ಸಾಧನದ ವಾಲ್ಯೂಮ್ ಮತ್ತು ಅಲರ್ಟ್‌‍‍ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತದೆ."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ಆಫ್ ಮಾಡಿ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ಹಳೆಯ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ಫಿಕ್ಸಡ್"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ಹೆಡ್ ಟ್ರ್ಯಾಕಿಂಗ್"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ರಿಂಗರ್ ಮೋಡ್ ಬದಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ರಿಂಗರ್ ಮೋಡ್"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್‌"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ಪರದೆ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಬಲಭಾಗ ಅಥವಾ ಕೆಳಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ಪರದೆ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಎಡಭಾಗ ಅಥವಾ ಮೇಲ್ಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸುವ ಸಮಯದಲ್ಲಿ: ಒಂದು ಆ್ಯಪ್‌ನಿಂದ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ಇನ್‌ಪುಟ್"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ಮುಂದಿನ ಭಾಷೆಗೆ ಬದಲಿಸಿ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ಹಿಂದಿನ ಭಾಷೆಗೆ ಬದಲಿಸಿ"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ಪ್ರಸ್ತುತ ಆ್ಯಪ್"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ಮುಗಿದಿದೆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ಡ್ರ್ಯಾಗ್‌ ಹ್ಯಾಂಡಲ್‌"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ಕೀಬೋರ್ಡ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಕಲಿಯಿರಿ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
@@ -1443,7 +1455,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ಭೇಷ್!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ನೀವು ಗೋ ಹೋಮ್ ಜೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ಭೇಷ್‌!"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ಆ್ಯಪ್‌ಗಳಿಂದ ಒದಗಿಸಲಾಗಿದೆ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ಡಿಸ್‌ಪ್ಲೇ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ಅಪರಿಚಿತ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ಟೈಲ್‌ಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡಿ"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ಟೈಲ್‌ಗಳನ್ನು ಅವುಗಳ ಮೂಲ ಆರ್ಡರ್ ಮತ್ತು ಗಾತ್ರಗಳಿಗೆ ರೀಸೆಟ್ ಮಾಡಬೇಕೇ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ಎಲ್ಲಾ ಟೈಲ್‌ಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ಎಲ್ಲಾ ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಟೈಲ್‌ಗಳನ್ನು ಸಾಧನದ ಮೂಲ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index c7cb2b16a2f3..5a188f19a5ac 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ಆಫ್"</item>
<item msgid="3028994095749238254">"ಆನ್"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 35dc245643ae..cc2cb909058a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"화면을 녹화하시겠습니까?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"단일 앱 녹화"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"전체 화면 녹화"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"전체 화면 녹화: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"전체 화면을 녹화하면 화면에 표시되는 모든 항목이 녹화됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"앱을 녹화하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 녹화됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"화면 녹화"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g>의 콘텐츠를 녹화 중입니다."</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"녹화 중지"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"화면 공유 중"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"콘텐츠 공유"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"화면 공유를 중지하시겠습니까?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"공유를 중단하시겠습니까?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"현재 전체 화면을 <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> 앱과 공유 중입니다."</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"현재 전체 화면을 앱과 공유 중입니다"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 공유 중입니다."</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"현재 앱을 공유 중입니다"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"현재 앱과 공유 중입니다"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"공유 중지"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"화면 전송 중"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"전송을 중지할까요?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"입력"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"보청기"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"켜는 중..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"사전 설정을 업데이트할 수 없음"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"미리 설정"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"실시간 자막"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"기기 카메라 및 마이크를 차단 해제하시겠습니까?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"잠금 화면 위젯"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"태블릿이 잠겨 있어도 누구나 잠금 화면에서 위젯을 볼 수 있습니다."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"위젯 선택 해제"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"높이 줄이기"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"높이 늘리기"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"잠금 화면 위젯"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"시작하기"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"알림 없음"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"새로운 알림 없음"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"알림 쿨다운 사용 중"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"한 번에 너무 많은 알림을 받으면 최대 2분간 자동으로 기기 볼륨이 줄어들고 알림이 최소화됩니다."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"사용 중지"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"잠금 해제하여 이전 알림 보기"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"수정됨"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"머리 추적"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"탭하여 벨소리 장치 모드 변경"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"벨소리 장치 모드"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string>
@@ -872,10 +872,12 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"화면 분할 중: 다른 앱으로 바꾸기"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"입력"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"다음 언어로 전환"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"이전 언어로 전환"</string>
- <string name="input_access_emoji" msgid="8105642858900406351">"이모티콘에 액세스"</string>
+ <string name="input_access_emoji" msgid="8105642858900406351">"이모티콘 열기"</string>
<string name="input_access_voice_typing" msgid="7291201476395326141">"음성 입력에 액세스"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"애플리케이션"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"어시스턴트"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"현재 앱"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"접근성"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"단축키 맞춤설정"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"검색 결과 없음"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"맞춤설정"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"완료"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"키보드 설정"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키에 관해 알아보세요."</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string>
@@ -1442,7 +1455,7 @@
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"돌아가기 동작을 완료했습니다."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"홈으로 이동"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"세 손가락을 사용해 터치패드에서 위로 스와이프하세요."</string>
- <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"아주 좋습니다"</string>
+ <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"잘하셨습니다"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"홈으로 이동 동작을 완료했습니다."</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"최근 앱 보기"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"세 손가락을 사용해 터치패드에서 위로 스와이프한 후 잠시 기다리세요."</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"앱에서 제공"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"디스플레이"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"알 수 없음"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"타일 재설정"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"타일을 원래 순서 및 크기로 재설정하시겠습니까?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"모든 타일을 재설정하시겠습니까?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"모든 빠른 설정 타일이 기기의 원래 설정으로 재설정됩니다."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index bc4740dbc23a..bfa11271bbab 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"사용 안함"</item>
<item msgid="3028994095749238254">"사용"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 72e86cc4a703..bcf594db3ded 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Экранды жаздырасызбы?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бир колдонмону жаздыруу"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтүндөй экранды жаздыруу"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Толук экранды жаздыруу: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүтүндөй экранды жаздырганда, андагы нерселердин баары видеого түшүп калат. Андыктан этият болуп, сырсөздөр, төлөм ыкмалары, билдирүүлөр, сүрөттөр, аудио жана видео материалдар сыяктуу купуя нерселерди көрсөтүп албаңыз."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Колдонмону жаздырганда ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер жаздырылат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жаздыруу"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Учурда <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун жаздырып жатасыз"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Жаздырууну токтотуу"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экран бөлүшүлүүдө"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Контент бөлүшүлүүдө"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлүшүүнү токтотосузбу?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Бөлүшүүнү токтотосузбу?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Учурда бүтүндөй экраныңызды <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> менен бөлүшүп жатасыз"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Учурда бүтүндөй экраныңызды колдонмо менен бөлүшүп жатасыз"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Учурда <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> колдонмосун бөлүшүп жатасыз"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Учурда колдонмону бөлүшүп жатасыз"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Учурда колдонмо менен бөлүшүп жатасыз"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Бөлүшүүнү токтотуу"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Тышкы экранга чыгарылууда"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Тышкы экранга чыгарууну токтотосузбу?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Киргизүү"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Угуу аппараттары"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Күйгүзүлүүдө…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ыкчам коштомо жазуулар"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Кулпуланган экрандагы виджеттер"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Кулпуланган планшетте баарына көрүнүп турат."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетти тандоодон чыгаруу"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Бийиктигин азайтуу"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Бийиктигин көбөйтүү"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Кулпуланган экрандагы виджеттер"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Башкаруу"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Таржымал"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Билдирмелердин параметрлери"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Билдирмелердин таржымалы"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Жаңы"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Үнсүз"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Билдирмелер"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Азыр баштоо"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Билдирме жок"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңы билдирмелер жок"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Билдирмелердин үнүн басаңдатуу күйүк"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Өтө көп билдирме келсе, түзмөктүн үнү 2 мүнөткө басаңдап, эскертүүлөрдүн саны азаят."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Өчүрүү"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Билдирмелерди көрүү үчүн кулпуну ачыңыз"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Туруктуу"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Баштын кыймылына көз салуу"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Коңгуроо режимин өзгөртүү үчүн басыңыз"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"коңгуроо режими"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранда сол же төмөн жактагы колдонмого которулуу"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлүнгөн экранды колдонуп жатканда сол же жогору жактагы колдонмого которулуңуз"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлүү режиминде бир колдонмону экинчисине алмаштыруу"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Киргизүү"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Кийинки тилге которулуу"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Мурунку тилге которулуу"</string>
@@ -1411,23 +1411,34 @@
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Акыркы колдонмолор"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Экранды бөлүү"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Киргизүү"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмодогу кыска жолдор"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмонун ыкчам баскычтары"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Учурдагы колдонмо"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Атайын мүмкүнчүлүктөр"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Ыкчам баскычтарды ыңгайлаштыруу"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Эч нерсе табылган жок"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Ыңгайлаштыруу"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Бүттү"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Cүйрөө маркери"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Баскычтоп параметрлери"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Нерселерге баскычтоп аркылуу өтүңүз"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ыкчам баскычтар тууралуу билип алыңыз"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Керектүү жерге сенсордук такта аркылуу өтөсүз"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Колдонмолор сунуштады"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгисиз"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Карталарды баштапкы абалга келтирүү"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Карталар баштапкы иретине жана өлчөмдөрүнө кайтарылсынбы?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Бардык карталарды баштапкы абалга келтиресизби?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Бардык Ыкчам параметрлер карталары түзмөктүн баштапкы параметрлерине кайтарылат"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 694967e3d8d6..e9d9612a4e47 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Өчүк"</item>
<item msgid="3028994095749238254">"Күйүк"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index f6fe3cce059a..4898fa8d823a 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ບັນທຶກໜ້າຈໍຂອງທ່ານບໍ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ບັນທຶກແອັບດຽວ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ບັນທຶກໝົດໜ້າຈໍ"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ບັນທຶກໜ້າຈໍທັງໝົດ: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ເມື່ອທ່ານບັນທຶກໝົດໜ້າຈໍຂອງທ່ານ, ລະບົບຈະບັນທຶກທຸກສິ່ງທີ່ສະແດງຢູ່ໜ້າຈໍຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ເມື່ອທ່ານບັນທຶກແອັບ, ລະບົບຈະບັນທຶກທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ໃນແອັບນັ້ນ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ບັນທຶກໜ້າຈໍ"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ທ່ານກຳລັງບັນທຶກ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ຢຸດການບັນທຶກ"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ກຳລັງແບ່ງປັນໜ້າຈໍ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"ກຳລັງແບ່ງປັນເນື້ອຫາ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ຢຸດການແບ່ງປັນໜ້າຈໍບໍ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"ຢຸດການແບ່ງປັນບໍ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ທ່ານກຳລັງແບ່ງປັນທັງໝົດໜ້າຈໍຂອງທ່ານກັບ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ທ່ານກຳລັງແບ່ງປັນທັງໝົດໜ້າຈໍຂອງທ່ານກັບແອັບ"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ທ່ານກຳລັງແບ່ງປັນ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ທ່ານກຳລັງແບ່ງປັນແອັບ"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"ທ່ານກຳລັງແບ່ງປັນກັບແອັບ"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ຢຸດການແບ່ງປັນ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ກຳລັງສົ່ງສັນຍານໜ້າຈໍ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ຢຸດການສົ່ງສັນຍານບໍ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ການປ້ອນຂໍ້ມູນ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ເຄື່ອງຊ່ວຍຟັງ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ກຳລັງເປີດ..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນ​ອັດ​ຕະ​ໂນ​ມັດ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ຄຳບັນຍາຍສົດ"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸ​ປະ​ກອນບໍ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸ​ປະ​ກອນບໍ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ທຸກຄົນສາມາດເບິ່ງວິດເຈັດຢູ່ໜ້າຈໍລັອກຂອງທ່ານໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ຍົກເລີກການເລືອກວິດເຈັດ"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ຫຼຸດຄວາມສູງ"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ເພີ່ມຄວາມສູງ"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ລຶບລ້າງທັງໝົດ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ຈັດການ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ປະຫວັດ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"ການຕັ້ງຄ່າການແຈ້ງເຕືອນ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"ປະຫວັດການແຈ້ງເຕືອນ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ໃໝ່"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ປິດສຽງ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ການແຈ້ງເຕືອນ"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ບໍ່ມີການແຈ້ງເຕືອນໃໝ່"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ຄູດາວການແຈ້ງເຕືອນເປີດຢູ່"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ຕອນນີ້ຄູດາວການແຈ້ງເຕືອນເປີດຢູ່"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ສຽງ ແລະ ແຈ້ງເຕືອນອຸປະກອນຂອງທ່ານຖືກຫຼຸດລົງໂດຍອັດຕະໂນມັດເປັນເວລາເຖິງ 2 ນາທີເມື່ອທ່ານໄດ້ຮັບການແຈ້ງເຕືອນຫຼາຍເກີນໄປໃນຄັ້ງດຽວ."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ປິດ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ປົດລັອກເພື່ອເບິ່ງການແຈ້ງເຕືອນເກົ່າ"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ຄົງທີ່"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ການຕິດຕາມຫົວ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ແຕະເພື່ອປ່ຽນໂໝດຣິງເກີ"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ໂໝດຣິງເກີ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ປິດສຽງ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ເຊົາປິດສຽງ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຂວາ ຫຼື ທາງລຸ່ມໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຊ້າຍ ຫຼື ທາງເທິງໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ໃນລະຫວ່າງແບ່ງໜ້າຈໍ: ໃຫ້ປ່ຽນຈາກແອັບໜຶ່ງເປັນອີກແອັບໜຶ່ງ"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ການປ້ອນຂໍ້ມູນ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ສະຫຼັບເປັນພາສາຖັດໄປ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ສະຫຼັບເປັນພາສາກ່ອນໜ້າ"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ແອັບປັດຈຸບັນ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ປັບແຕ່ງຄີລັດ"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ບໍ່ມີຜົນການຊອກຫາ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ປັບແຕ່ງ"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ແລ້ວໆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ບ່ອນຈັບລາກ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມຂອງທ່ານ"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ສຶກສາຄີລັດ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ນຳທາງໂດຍໃຊ້ແຜ່ນສຳຜັດຂອງທ່ານ"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ສະໜອງໃຫ້ໂດຍແອັບ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ການສະແດງຜົນ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ບໍ່ຮູ້ຈັກ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ຣີເຊັດແຜ່ນ"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ຣີເຊັດແຜ່ນເປັນການຈັດຮຽງ ແລະ ຂະໜາດເດີມບໍ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ຣີເຊັດແຜ່ນທັງໝົດບໍ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ແຜ່ນການຕັ້ງຄ່າດ່ວນທັງໝົດຈະຣີເຊັດເປັນການຕັ້ງຄ່າແບບເກົ່າຂອງອຸປະກອນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index 9386e00af195..34af9aae31e3 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ປິດ"</item>
<item msgid="3028994095749238254">"ເປີດ"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 70a0063d7627..aff0d3085bfa 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Įrašyti ekraną?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Įrašyti vieną programą"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Įrašyti visą ekraną"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Įrašyti visą ekraną: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kai įrašote visą ekraną, įrašomas visas ekrane rodomas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kai įrašote programą, įrašomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Įrašyti ekraną"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Šiuo metu įrašote šią programą: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Sustabdyti įrašymą"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bendrinamas ekranas"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Bendrint turinį"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Nebebendrinti ekrano?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Nebebendrinti?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Šiuo metu bendrinate visą ekraną su šia programa: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Šiuo metu bendrinate visą ekraną su programa"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Šiuo metu bendrinate šią programą: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Šiuo metu bendrinate programą"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Šiuo metu bendrinate su programa"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Nebebendrinti"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Perduodamas ekranas"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Sustabdyti perdavimą?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Įvestis"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Klausos aparatai"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Įjungiama…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrai realiuoju laiku"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Užrakinimo ekrano valdikliai"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Visi gali žr. valdiklius užrakinimo ekrane, net užrakinus planšetinį kompiuterį."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"atšaukti valdiklio pasirinkimą"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Sumažinti aukštį"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Padidinti aukštį"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Užrakinimo ekrano valdikliai"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Viską išvalyti"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Tvarkyti"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Pranešimų nustatymai"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Pranešimų istorija"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nauja"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tylus"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pranešimai"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nėra įspėjimų"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Naujų pranešimų nėra"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Pranešimų neaktyvumo laikotarpis įjungtas"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Pranešimų neaktyvumo laikotarpis dabar įjungtas"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jūsų įrenginio garsumas ir įspėjimai automatiškai sumažinami iki dviejų minučių, kai iš karto gaunate per daug pranešimų."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Išjungti"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atrakinę matykite senesnius pranešimus"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksuotas"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Galvos stebėjimas"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Palieskite, kad pakeistumėte skambučio režimą"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"skambučio režimas"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"nutildyti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"įjungti garsą"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Perjunkite į programą dešinėje arba apačioje išskaidyto ekrano režimu"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Perjunkite į programą kairėje arba viršuje išskaidyto ekrano režimu"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Išskaidyto ekrano režimu: pakeisti iš vienos programos į kitą"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Įvestis"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Perjungti į kitą kalbą"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Perjungti į ankstesnę kalbą"</string>
@@ -1411,23 +1410,34 @@
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Naujausios programos"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Išskaidyto ekrano režimas"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Įvestis"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos šaukiniai"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos spartieji klavišai"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Esama programa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pritaikomumas"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sparčiųjų klavišų tinkinimas"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ieškoti sparčiųjų klavišų"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nėra jokių paieškos rezultatų"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tinkinti"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Atlikta"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkimo rankenėlė"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatūros nustatymai"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naršykite naudodamiesi klaviatūra"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Sužinokite apie sparčiuosius klavišus"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naršykite naudodamiesi jutikline dalimi"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Teikia programos"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekranas"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nežinoma"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Išklotinės nustatymas iš naujo"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Iš naujo nustatyti išklotinės pradinę tvarką ir dydžius?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Iš naujo nustatyti visus išklotines elementus?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visi sparčiųjų nustatymų išklotinės elementai bus iš naujo nustatyti į pradinius įrenginio nustatymus"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index c975e7e3cc80..124f49c7d22e 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Išjungta"</item>
<item msgid="3028994095749238254">"Įjungta"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 360afad1c6fa..b0b417675c1c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vai ierakstīt ekrānu?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ierakstīt vienu lietotni"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ierakstīt visu ekrānu"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Ierakstīt visu ekrānu: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Ierakstot visu ekrānu, viss, kas redzams ekrānā, tiek ierakstīts. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ierakstot lietotni, tiek ierakstīts viss attiecīgajā lietotnē rādītais vai atskaņotais. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ierakstīt ekrānu"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Pašlaik ierakstāt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Apturēt ierakstīšanu"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Notiek ekrāna kopīgošana"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Notiek satura kopīgošana"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vai apturēt ekrāna kopīgošanu?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Vai apturēt kopīgošanu?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Pašlaik kopīgojat visu ekrānu ar lietotni <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Pašlaik kopīgojat visu ekrānu ar lietotni"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Pašlaik kopīgojat lietotni <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Pašlaik kopīgojat lietotni"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Pašlaik kopīgojat saturu ar lietotni."</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Apturēt kopīgošanu"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Notiek ekrāna apraide"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vai pārtraukt apraidi?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ievade"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Dzirdes aparāti"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Notiek ieslēgšana…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitri reāllaikā"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Bloķēšanas ekrāna logrīki"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Jebkurš var skatīt logrīkus bloķēšanas ekrānā, pat ja planšetdators ir bloķēts."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"noņemt logrīka atlasi"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Samazināt augstumu"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Palielināt augstumu"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Bloķēšanas ekrāna logrīki"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Dzēst visu"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Pārvaldīt"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Vēsture"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Paziņojumu iestatījumi"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Paziņojumu vēsture"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Jauni"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Klusums"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Paziņojumi"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Sākt tūlīt"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nav paziņojumu"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nav jaunu paziņojumu"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Nogaidīšanas periods paziņojumiem ir ieslēgts"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Saņemot par daudz paziņojumu uzreiz, skaļums un brīdinājumi tiek automātiski samazināti līdz 2 min."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Izslēgt"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atbloķējiet vecāku paziņojumu skatīšanai"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksēts"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"zvanītāja režīms"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string>
@@ -815,8 +813,8 @@
<string name="keyboard_key_back" msgid="4185420465469481999">"Atpakaļ"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Atstarpe"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"Ievadīšanas taustiņš"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Atpakaļatkāpes taustiņš"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Atskaņot/apturēt"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Apturēt"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Nākamais"</string>
@@ -826,7 +824,7 @@
<string name="keyboard_key_page_up" msgid="173914303254199845">"Lapa uz augšu"</string>
<string name="keyboard_key_page_down" msgid="9035902490071829731">"Lapa uz leju"</string>
<string name="keyboard_key_forward_del" msgid="5325501825762733459">"Dzēšanas taustiņš"</string>
- <string name="keyboard_key_esc" msgid="6230365950511411322">"Atsoļa taustiņš"</string>
+ <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
<string name="keyboard_key_move_home" msgid="3496502501803911971">"Sākumvietas taustiņš"</string>
<string name="keyboard_key_move_end" msgid="99190401463834854">"Beigvietas taustiņš"</string>
<string name="keyboard_key_insert" msgid="4621692715704410493">"Ievietošanas taustiņš"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pāriet uz lietotni pa labi/lejā, kamēr izmantojat sadalīto ekrānu."</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pāriet uz lietotni pa kreisi/augšā, kamēr izmantojat sadalīto ekrānu."</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ekrāna sadalīšanas režīmā: pārvietot lietotni no viena ekrāna uz otru"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ievade"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Pārslēgt uz nākamo valodu"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pārslēgt uz iepriekšējo valodu"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Pašreizējā lietotne"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Īsinājumtaustiņu pielāgošana"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēt saīsnes"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nav meklēšanas rezultātu"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pielāgot"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gatavs"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatūras iestatījumi"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pārvietošanās, izmantojot tastatūru"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Uzziniet par īsinājumtaustiņiem."</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pārvietošanās, izmantojot skārienpaliktni"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Nodrošina lietotnes"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displejs"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nezināma"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Elementu atiestatīšana"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vai atiestatīt elementus, atjaunojot to sākotnējo secību un izmērus?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vai atiestatīt visus elementus?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visiem ātro iestatījumu elementiem tiks atiestatīti sākotnējie iestatījumi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index c65a1d429492..e5cb1758b783 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Izslēgts"</item>
<item msgid="3028994095749238254">"Ieslēgts"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index ebd62a9568e0..e30cf21fceec 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се снима екранот?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимање на една апликација"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Снимање на целиот екран"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Снимање на целиот екран: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Додека го снимате целиот екран, сѐ што е прикажано на екранот се снима. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Додека снимате апликација, може да се сними сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Снимај го екранот"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Во моментов ја снимате <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Сопри го снимањето"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Се споделува екранот"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Се споделуваат содржини"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Да се сопре споделувањето на екранот?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Да се сопре споделувањето?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Во моментов го споделувате целиот екран со <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Во моментов го споделувате целиот екран со апликација"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Во моментов ја споделувате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Во моментов споделувате апликација"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Во моментов споделувате со апликација"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Сопри го споделувањето"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Се емитува екранот"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Да се сопре емитувањето?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Влез"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни помагала"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Се вклучува…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматски титлови"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Виџети на заклучен екран"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Секој може да гледа виџети на заклучениот екран, дури и ако таблетот е заклучен."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"поништи го изборот на виџетот"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Намали ја висината"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Зголеми ја висината"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети на заклучен екран"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни сега"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Нема известувања"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нови известувања"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"„Подискретни известувања“ е вклучена"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Јачината на звукот и известувањата на уредот се намалуваат автоматски до 2 минути кога добивате премногу известувања одеднаш."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Исклучи"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отклучете за да ги видите старите известувања"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Фиксно"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Следење на главата"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Допрете за да го промените режимот на ѕвончето"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"режим на ѕвонче"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string>
@@ -814,7 +814,7 @@
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Бришење наназад"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Пушти/Паузирај"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Сопри"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Следно"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Префрлете се на апликацијата десно или долу при користењето поделен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете се на апликацијата лево или горе при користењето поделен екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"При поделен екран: префрлете ги аплик. од едната на другата страна"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Внесување"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете на следниот јазик"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Префрлете на претходниот јазик"</string>
@@ -1413,28 +1415,39 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Тековна апликација"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Пристапност"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Приспособете ги кратенките од тастатурата"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пребарувајте кратенки"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултати од пребарување"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Приспособете"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Движете се со користење на тастатурата"</string>
- <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете кратенки од тастатурата"</string>
+ <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете ги кратенките од тастатурата"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Движете се со користење на допирната подлога"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете движења за допирната подлога"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Движете се со користење на тастатурата и допирната подлога"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете движења за допирната подлога, кратенки од тастатурата и друго"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Врати се назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Оди на почетниот екран"</string>
- <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прегледајте ги неодамнешните апликации"</string>
+ <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи ги неодамнешните апликации"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Повлечете налево или надесно со три прста на допирната подлога"</string>
@@ -1444,7 +1457,7 @@
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Повлечете нагоре со три прсти на допирната подлога"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Го завршивте движењето за враќање на почетниот екран"</string>
- <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прегледајте ги неодамнешните апликации"</string>
+ <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прикажи ги неодамнешните апликации"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Повлечете нагоре и задржете со три прста на допирната подлога"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Обезбедено од апликации"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Ресетирајте ги плочките"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Да се ресетираат плочките на нивниот првичен редослед и големини?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Да се ресетираат сите плочки?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Сите плочки на „Брзи поставки“ ќе се ресетираат на првичните поставки на уредот"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index a8d96950b6f8..61539d62baf7 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Исклучено"</item>
<item msgid="3028994095749238254">"Вклучено"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 6152fce2e02b..42f67c6558c2 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"നിങ്ങളുടെ സ്ക്രീൻ റെക്കോർഡ് ചെയ്യണോ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ഒരു ആപ്പ് റെക്കോർഡ് ചെയ്യുക"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുക"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"മുഴുവൻ സ്ക്രീനും റെക്കോർഡ് ചെയ്യുക: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"നിങ്ങളുടെ സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുമ്പോൾ, സ്ക്രീനിൽ ദൃശ്യമാകുന്ന എല്ലാം റെക്കോർഡ് ചെയ്യപ്പെടും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"നിങ്ങളുടെ ആപ്പ് റെക്കോർഡ് ചെയ്യുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും റെക്കോർഡ് ചെയ്യപ്പെടും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"സ്ക്രീൻ റെക്കോർഡ് ചെയ്യുക"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"നിങ്ങൾ ഇപ്പോൾ <xliff:g id="APP_NAME">%1$s</xliff:g> റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"റെക്കോർഡിംഗ് നിർത്തുക"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"സ്‌ക്രീൻ പങ്കിടുന്നു"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"ഉള്ളടക്കം പങ്കിടൽ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"സ്‌ക്രീൻ പങ്കിടുന്നത് നിർത്തണോ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"പങ്കിടൽ നിർത്തണോ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"നിങ്ങൾ ഇപ്പോൾ മുഴുവൻ സ്ക്രീനും <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> എന്നതുമായി പങ്കിടുകയാണ്"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"നിങ്ങൾ ഇപ്പോൾ മുഴുവൻ സ്ക്രീനും ഒരു ആപ്പുമായി പങ്കിടുകയാണ്"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"നിങ്ങൾ ഇപ്പോൾ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> പങ്കിടുകയാണ്"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"നിങ്ങൾ നിലവിൽ ഒരു ആപ്പ് പങ്കിടുകയാണ്"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"നിങ്ങൾ നിലവിൽ ഒരു ആപ്പുമായി പങ്കിടുകയാണ്"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"പങ്കിടൽ നിർത്തുക"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"സ്‌ക്രീൻ കാസ്‌റ്റ് ചെയ്യുന്നു"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ഇൻപുട്ട്"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ശ്രവണ സഹായികൾ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ഓണാക്കുന്നു…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"സ്‌ക്രീൻ സ്വയമേവ തിരിയൽ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"സ്‌ക്രീൻ സ്വയമേവ തിരിക്കുക"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"തത്സമയ ക്യാപ്ഷൻ"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ടാബ്‌ലെറ്റ് ലോക്കാണെങ്കിൽ പോലും ലോക്ക് സ്ക്രീനിൽ ആർക്കും വിജറ്റുകൾ കാണാം."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"വിജറ്റ് തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ഉയരം കുറയ്‌ക്കുക"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ഉയരം കൂട്ടുക"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്‌ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്‌ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"പുതിയ അറിയിപ്പുകളൊന്നുമില്ല"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"അറിയിപ്പിന്റെ ശബ്ദം കുറയ്ക്കൽ ഓണാണ്"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"നിരവധി അറിയിപ്പ് ഒരുമിച്ച് ലഭിക്കുമ്പോൾ, ഉപകരണത്തിന്റെ ശബ്‌ദവും മുന്നറിയിപ്പും 2 മിനിറ്റ് വരെ സ്വയമേവ കുറയ്ക്കും."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ഓഫാക്കുക"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"പഴയ അറിയിപ്പുകൾ കാണാൻ അൺലോക്ക് ചെയ്യുക"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ഓൺ ചെയ്തിരിക്കുന്നു"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ഹെഡ് ട്രാക്കിംഗ്"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"റിംഗർ മോഡ് മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"റിംഗർ മോഡ്"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
@@ -869,9 +869,11 @@
<string name="system_multitasking_rhs" msgid="8714224917276297810">"വലതുവശത്തുള്ള നിലവിലെ ആപ്പിനൊപ്പം സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
<string name="system_multitasking_lhs" msgid="8402954791206308783">"ഇടതുവശത്തുള്ള നിലവിലെ ആപ്പിനൊപ്പം സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"സ്‌ക്രീൻ വിഭജന മോഡിൽ നിന്ന് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറുക"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ വലതുവശത്തെ/താഴത്തെ ആപ്പിലേക്ക് മാറൂ"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ വലതുവശത്തെ/താഴത്തെ ആപ്പിലേക്ക് മാറുക"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ ഇടതുവശത്തെ/മുകളിലെ ആപ്പിലേക്ക് മാറൂ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"സ്‌ക്രീൻ വിഭജന മോഡിൽ: ഒരു ആപ്പിൽ നിന്ന് മറ്റൊന്നിലേക്ക് മാറുക"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ഇൻപുട്ട്"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"അടുത്ത ഭാഷയിലേക്ക് മാറുക"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"മുമ്പത്തെ ഭാഷയിലേക്ക് മാറുക"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"നിലവിലെ ആപ്പ്"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ഉപയോഗസഹായി"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"കീബോർഡ് കുറുക്കുവഴികൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"പൂർത്തിയായി"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"കീബോർഡ് ക്രമീകരണം"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ആപ്പുകൾ നൽകുന്നത്"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ഡിസ്‌പ്ലേ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"അജ്ഞാതം"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ടൈലുകൾ റീസെറ്റ് ചെയ്യുക"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ടൈലുകൾ അവയുടെ ഒറിജിനൽ ക്രമത്തിലേക്കും വലുപ്പങ്ങളിലേക്കും റീസെറ്റ് ചെയ്യണോ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"എല്ലാ ടൈലുകളും റീസെറ്റ് ചെയ്യണോ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"എല്ലാ ദ്രുത ക്രമീകരണ ടൈലുകളും ഉപകരണത്തിന്റെ ഒറിജിനൽ ക്രമീകരണത്തിലേക്ക് റീസെറ്റ് ചെയ്യും"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 609fdde3f5de..c1278d46440f 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ഓഫാണ്"</item>
<item msgid="3028994095749238254">"ഓണാണ്"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index aafc8c610c55..c87f98acd3e9 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Дэлгэцээ бичих үү?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Нэг аппыг бичих"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтэн дэлгэцийг бичих"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Дэлгэцийг бүхэлд нь бичих: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Таныг бүтэн дэлгэцээ бичиж байхад дэлгэц дээр тань харуулж буй аливаа зүйлийг бичдэг. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Таныг апп бичиж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйлийг бичдэг. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Дэлгэцийг бичих"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Та одоогоор <xliff:g id="APP_NAME">%1$s</xliff:g>-г бичиж байна"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Бичихийг зогсоох"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Дэлгэцийг хуваалцаж байна"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Контент хуваалцаж байна"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Дэлгэц хуваалцахыг зогсоох уу?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Хуваалцахыг зогсоох уу?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Та одоогоор дэлгэцээ бүтнээр нь <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-тай хуваалцаж байна"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Та одоогоор дэлгэцээ бүтнээр нь нэг апптай хуваалцаж байна"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Та одоогоор <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-г хуваалцаж байна"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Та одоогоор нэг аппыг хуваалцаж байна"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Та одоогоор нэг апптай хуваалцаж байна"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Хуваалцахыг зогсоох"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Дэлгэцийг дамжуулж байна"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Дамжуулахaa болих уу?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Оролт"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Сонсголын төхөөрөмж"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Асааж байна…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Шууд тайлбар"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Түгжээтэй дэлгэцийн виджет"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Таны таблет түгжээтэй байсан ч түгжээтэй дэлгэцийн виджетийг тань дурын хүн үзнэ"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетийн сонголтыг болиулах"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Намсгах"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Өндөрсгөх"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Түгжээтэй дэлгэцийн виджет"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Одоо эхлүүлэх"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Мэдэгдэл байхгүй"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Шинэ мэдэгдэл алга"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Мэдэгдлийн хөргөлт асаалттай байна"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Таныг хэт олон мэдэгдэл нэг дор авахад таны төхөөрөмжийн дууны түвшин болон дохиог 2 хүртэлх минутын турш автоматаар багасгадаг."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Унтраах"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Хуучин мэдэгдлийг харах бол түгжээг тайл"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Зассан"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Толгой хянах"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Хонхны горимыг өөрчлөхийн тулд товшино уу"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"хонхны горим"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Дэлгэц хуваахыг ашиглаж байхдаа баруун талд эсвэл доор байх апп руу сэлгэ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн талд эсвэл дээр байх апп руу сэлгэ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Дэлгэц хуваах үеэр: аппыг нэгээс нөгөөгөөр солих"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Оролт"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Дараагийн хэл рүү сэлгэх"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Өмнөх хэл рүү сэлгэх"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Одоогийн апп"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Хандалт"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Товчлуурын шууд холбоосыг өөрчлөх"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ямар ч хайлтын илэрц байхгүй"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Өөрчлөх"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Болсон"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Гараа ашиглан шилжих"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Мэдрэгч самбараа ашиглан шилжээрэй"</string>
@@ -1449,7 +1462,7 @@
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Сайн байна!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Бүх аппыг харах"</string>
- <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээр тань байх тусгай товчийг дарна уу"</string>
+ <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string>
<string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Аппуудаас өгсөн"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Дэлгэц"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Тодорхойгүй"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Хавтангуудыг шинэчлэх"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Хавтангуудыг эх дараалал, хэмжээ рүү нь шинэчлэх үү?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Бүх хавтанг шинэчлэх үү?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Шуурхай тохиргооны бүх хавтан төхөөрөмжийн эх тохиргоо руу шинэчлэгдэнэ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index a3f54541880d..da890cc5a157 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Унтраалттай"</item>
<item msgid="3028994095749238254">"Асаалттай"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index fbbb1670b286..204399c77b96 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तुमची स्क्रीन रेकॉर्ड करायची आहे का?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अ‍ॅप रेकॉर्ड करा"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूर्ण स्क्रीन रेकॉर्ड करा"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"संपूर्ण स्क्रीन रेकॉर्ड करा: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्टी रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तुम्ही अ‍ॅप रेकॉर्ड करता, तेव्हा त्या अ‍ॅपमध्ये दाखवलेली किंवा प्ले केलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रेकॉर्ड करा"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"तुम्ही सध्या <xliff:g id="APP_NAME">%1$s</xliff:g> रेकॉर्ड करत आहात"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रेकॉर्ड करणे थांबवा"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रीन शेअर करत आहे"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"आशय शेअर करत आहे"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रीन शेअर करणे थांबवायचे आहे का?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"शेअर करणे थांबवायचे आहे का?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"तुम्ही सध्या तुमची संपूर्ण स्क्रीन <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> सह शेअर करत आहात"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"तुम्ही सध्या तुमची संपूर्ण स्क्रीन एका ॲपसह शेअर करत आहात"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"तुम्ही सध्या <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> शेअर करत आहात"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"तुम्ही सध्या एक ॲप शेअर करत आहात"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"तुम्ही सध्या एक ॲप शेअर करत आहात"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"शेअर करणे थांबवा"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्‍क्रीन कास्‍ट करत आहे"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट करणे थांबवायचे आहे का?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवणयंत्रे"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सुरू करत आहे…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट करता आले नाही"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव्ह कॅप्शन"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिव्हाइसचा कॅमेरा आणि मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"लॉक स्‍क्रीन विजेट"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"तुमचा टॅबलेट लॉक केला, तरी कोणीही तुमच्या लॉक स्क्रीनवरील विजेट पाहू शकतो."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेटची निवड रद्द करा"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"उंची कमी करा"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"उंची वाढवा"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्‍क्रीन विजेट"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अ‍ॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्‍क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"सूचना नाहीत"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"नवीन सूचना नाहीत"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"नोटिफिकेशन कूलडाउन सुरू आहे"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"आता नोटिफिकेशन कूलडाउन सुरू आहे"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"एकाच वेळी अनेक नोटिफिकेशन मिळाल्यास, डिव्हाइसचा आवाज आणि सूचना आपोआप कमाल २ मिनिटांपर्यंत कमी होतात."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करा"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"जुन्या सूचना पाहण्यासाठी अनलॉक करा"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"निश्चित केला आहे"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्रॅकिंग"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिंगर मोड बदलण्यासाठी टॅप करा"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन वापरताना उजवीकडील किंवा खालील अ‍ॅपवर स्विच करा"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन वापरताना डावीकडील किंवा वरील अ‍ॅपवर स्विच करा"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीनदरम्यान: एक अ‍ॅप दुसऱ्या अ‍ॅपने बदला"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"पुढील भाषेवर स्विच करा"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"मागील भाषेवर स्विच करा"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"सध्याचे अ‍ॅप"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"अ‍ॅक्सेसिबिलिटी"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"कीबोर्ड शॉर्टकट कस्टमाइझ करा"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कोणतेही शोध परिणाम नाहीत"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइझ करा"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूर्ण झाले"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्रॅग हॅंडल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग्ज"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"तुमचा कीबोर्ड वापरून नेव्हिगेट करा"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट जाणून घ्या"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"तुमचा टचपॅड वापरून नेव्हिगेट करा"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"अ‍ॅप्सद्वारे पुरवलेले"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिस्प्ले"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"टाइल रीसेट करा"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"टाइल त्यांच्या मूळ क्रमानुसार आणि मूळ आकारांमध्ये रीसेट करायच्या आहेत का?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"सर्व टाइल रीसेट करायच्या?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"सर्व क्विक सेटिंग्ज टाइल डिव्हाइसच्या मूळ सेटिंग्जवर रीसेट केल्या जातील"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index 54c320c953d5..3ea25a68922b 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"बंद आहे"</item>
<item msgid="3028994095749238254">"सुरू आहे"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 087c8a664076..ce2b3f07c547 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rakam skrin anda?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rakam satu apl"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rakam seluruh skrin"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Rakam keseluruhan skrin: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Apabila anda merakam seluruh skrin anda, apa-apa sahaja yang dipaparkan pada skrin anda akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Apabila anda merakam apl, apa-apa sahaja yang dipaparkan atau dimainkan dalam apl tersebut akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rakam skrin"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Anda sedang merakam <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Hentikan rakaman"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Berkongsi skrin"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Berkongsi kandungan"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hentikan perkongsian skrin?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Hentikan perkongsian?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Anda sedang berkongsi seluruh skrin anda dengan <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Anda sedang berkongsi seluruh skrin anda dengan apl"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Anda sedang berkongsi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Anda sedang berkongsi apl"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Anda sedang berkongsi dengan apl"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hentikan perkongsian"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Menghantar skrin"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hentikan penghantaran?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu pendengaran"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Menghidupkan…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoputar"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoputar skrin"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pratetapan"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sari Kata Langsung"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget skrin kunci"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Sesiapa sahaja boleh melihat widget pada skrin kunci, walaupun tablet dikunci."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"nyahpilih widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Kurangkan ketinggian"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Tambahkan ketinggian"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget skrin kunci"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Kosongkan semua"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Urus"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Sejarah"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Tetapan pemberitahuan"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Sejarah pemberitahuan"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Baharu"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Senyap"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pemberitahuan"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Tiada pemberitahuan"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Tiada pemberitahuan baharu"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Tempoh bertenang pemberitahuan dihidupkan"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Tempoh bertenang pemberitahuan dihidupkan"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Kelantangan, makluman peranti dikurangkan secara automatik hingga 2 minit apabila menerima banyak pemberitahuan serentak."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Matikan"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat pemberitahuan lama"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Tetap"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Penjejakan Kepala"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Ketik untuk menukar mod pendering"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"mod pendering"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Tukar kepada apl di sebelah kanan/bawah semasa menggunakan skrin pisah"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Tukar kepada apl di sebelah kiri/atas semasa menggunakan skrin pisah"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Semasa skrin pisah: gantikan apl daripada satu apl kepada apl lain"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih kepada bahasa seterusnya"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih kepada bahasa sebelumnya"</string>
@@ -1415,20 +1414,31 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Apl Semasa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sesuaikan pintasan papan kekunci"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tiada hasil carian"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tetapan Papan Kekunci"</string>
- <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci anda"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
+ <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ketahui pintasan papan kekunci"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigasi menggunakan pad sentuh anda"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Ketahui gerak isyarat pad sentuh"</string>
@@ -1462,7 +1472,7 @@
<string name="back_edu_toast_content" msgid="4530314597378982956">"Untuk kembali, leret ke kiri atau ke kanan dengan tiga jari pada pad sentuh"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Untuk mengakses laman utama, leret ke atas dengan tiga jari pada pad sentuh"</string>
<string name="overview_edu_toast_content" msgid="5797030644017804518">"Untuk melihat apl terbaharu, leret ke atas dan tahan dengan tiga jari pada pad sentuh"</string>
- <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Untuk melihat semua apl anda, tekan kekunci tindakan pada papan kekunci anda"</string>
+ <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Untuk melihat semua apl, tekan kekunci tindakan pada papan kekunci"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"Disunting"</string>
<string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Buka kunci untuk melihat"</string>
<string name="contextual_education_dialog_title" msgid="4630392552837487324">"Pendidikan kontekstual"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disediakan oleh apl"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Paparan"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Tetapkan semula jubin"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Tetapkan semula jubin kepada urutan dan saiz yang asal?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tetapkan semula semua jubin?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua jubin Tetapan Pantas akan ditetapkan semula kepada tetapan asal peranti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index 174e416e3508..51f215b31931 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Mati"</item>
<item msgid="3028994095749238254">"Hidup"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d456bcae7085..a3568f2b56bb 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ဖန်သားပြင်ကို ရိုက်ကူးမလား။"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"အက်ပ်တစ်ခုကို ရိုက်ကူးရန်"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ဖန်သားပြင်တစ်ခုလုံးကို ရိုက်ကူးရန်"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ဖန်သားပြင်တစ်ခုလုံးကို ရိုက်ကူးရန်- %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"သင့်ဖန်သားပြင်တစ်ခုလုံး ရိုက်ကူးနေချိန်တွင် ဖန်သားပြင်တွင် ပြထားသည့် အရာအားလုံးကို ရိုက်ကူးသည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"အက်ပ်ကို ရိုက်ကူးနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို ရိုက်ကူးသည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ဖန်သားပြင်ကို ရိုက်ကူးရန်"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို ရိုက်ကူးနေသည်"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ရိုက်ကူးမှု ရပ်ရန်"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ဖန်သားပြင်ကို မျှဝေနေသည်"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"အကြောင်းအရာကို မျှဝေနေသည်"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ဖန်သားပြင်မျှဝေခြင်း ရပ်မလား။"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"မျှဝေခြင်းကို ရပ်မလား။"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"သင်သည် လက်ရှိတွင် <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ဖြင့် ဖန်သားပြင်တစ်ခုလုံးကို မျှဝေနေသည်"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"သင်သည် လက်ရှိတွင် အက်ပ်တစ်ခုဖြင့် ဖန်သားပြင်တစ်ခုလုံးကို မျှဝေနေသည်"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ကို မျှဝေနေသည်"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"သင်သည် လက်ရှိတွင် အက်ပ်ကို မျှဝေနေသည်"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"သင်သည် လက်ရှိတွင် အက်ပ်ဖြင့် မျှဝေနေသည်"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"မျှဝေခြင်း ရပ်ရန်"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ဖန်သားပြင်ကို ကာစ်လုပ်နေသည်"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ကာစ်လုပ်ခြင်းကို ရပ်လိုသလား။"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"အဝင်"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"နားကြားကိရိယာ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ဖွင့်နေသည်…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"အသင့်သုံးကို အပ်ဒိတ်လုပ်၍မရပါ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ကြိုတင်သတ်မှတ်ချက်"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"တိုက်ရိုက်စာတန်း"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"စက်၏ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"တက်ဘလက်လော့ခ်ချထားသော်လည်း မည်သူမဆို လော့ခ်မျက်နှာပြင်ဝိဂျက်ကို ကြည့်နိုင်သည်။"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ဝိဂျက် ပြန်ဖြုတ်ရန်"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"အမြင့်ကို လျှော့ရန်"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"အမြင့်ကို တိုးရန်"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"အားလုံးရှင်းရန်"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"စီမံရန်"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"မှတ်တမ်း"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"အကြောင်းကြားချက် ဆက်တင်များ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"အကြောင်းကြားချက် မှတ်တမ်း"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"အသစ်"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"အသံတိတ်ခြင်း"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"အကြောင်းကြားချက်များ"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"အကြောင်းကြားချက် မရှိပါ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"အကြောင်းကြားချက်သစ် မရှိပါ"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"အကြောင်းကြားချက် သတိပေးမှု လျှော့ချခြင်း ဖွင့်ထားသည်"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"အကြောင်းကြားချက်များစွာ တစ်ပြိုင်နက်ရပါက သင့်စက်၏ အသံနှင့် သတိပေးချက်ကို ၂ မိနစ်ကြာသည်အထိ အလိုအလျောက်လျှော့ချသည်။"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ပိတ်ရန်"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"အကြောင်းကြားချက်ဟောင်းကြည့်ရန် လော့ခ်ဖွင့်ပါ"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ပုံသေ"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ခေါင်းလှုပ်ရှားမှု"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ဖုန်းခေါ်သံမုဒ်သို့ ပြောင်းရန် တို့ပါ"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"အသံမြည်မုဒ်"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string>
@@ -860,9 +858,9 @@
<string name="group_system_go_back" msgid="2730322046244918816">"ပြန်သွားရန်"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"ပင်မစာမျက်နှာသို့သွားရန်"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"မကြာသေးမီကအက်ပ်များ ကြည့်ရန်"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"မကြာသေးမီက အက်ပ်များကို အဝိုင်းပုံရှေ့သို့လှည့်ရန်"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"မကြာသေးမီက အက်ပ်များကို အဝိုင်းပုံနောက်ပြန်လှည့်ရန်"</string>
- <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"အက်ပ်ပြသမှု ဖွင့်ရန်"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"လတ်တလော အက်ပ်များတလျှောက် ရှေ့သို့လှည့်ရန်"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"လတ်တလော အက်ပ်များတလျှောက် နောက်ပြန်လှည့်ရန်"</string>
+ <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"အက်ပ်စာရင်း ဖွင့်ရန်"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ဆက်တင်များ ဖွင့်ရန်"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ဖွင့်ရန်"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"လော့ခ်မျက်နှာပြင်"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"မျက်နှာပြင်ခွဲ၍ပြသခြင်း သုံးစဉ် ညာ (သို့) အောက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးစဉ် ဘယ် (သို့) အထက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"မျက်နှာပြင် ခွဲ၍ပြသစဉ်- အက်ပ်တစ်ခုကို နောက်တစ်ခုနှင့် အစားထိုးရန်"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"စာရိုက်ခြင်း"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"နောက်ဘာသာစကားသို့ ပြောင်းရန်"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ယခင်ဘာသာစကားသို့ ပြောင်းရန်"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"လက်ရှိအက်ပ်"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"အများသုံးနိုင်မှု"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"လက်ကွက်ဖြတ်လမ်းများကို စိတ်ကြိုက်လုပ်ခြင်း"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ဖြတ်လမ်းများ ရှာရန်"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ရှာဖွေမှုရလဒ် မရှိပါ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"စိတ်ကြိုက်လုပ်ရန်"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ပြီးပြီ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"သင့်ကီးဘုတ်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"သင့်တာ့ချ်ပက်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"အက်ပ်များက ပံ့ပိုးထားသည်"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ဖန်သားပြင်"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"အမျိုးအမည်မသိ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"အကွက်ငယ်များ ပြင်ဆင်သတ်မှတ်ခြင်း"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"အကွက်ငယ်များကို ၎င်းတို့၏ မူလအစီအစဉ်နှင့် အရွယ်အစားများသို့ ပြင်ဆင်သတ်မှတ်မလား။"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"အကွက်ငယ်အားလုံးကို ပြင်ဆင်သတ်မှတ်မလား။"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"အမြန်ဆက်တင်များ အကွက်ငယ်အားလုံးကို စက်ပစ္စည်း၏ မူရင်းဆက်တင်များသို့ ပြင်ဆင်သတ်မှတ်ပါမည်"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index f665a00a5214..7af75166de11 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ပိတ်"</item>
<item msgid="3028994095749238254">"ဖွင့်"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 1fbb35b0905e..38b267332f47 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du ta opp skjermen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ta opp én app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ta opp hele skjermen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Ta opp hele skjermen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du tar opp hele skjermen, blir alt som vises på skjermen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du tar opp en app, blir alt som vises eller spilles av i appen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ta opp skjermen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du tar nå opp <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stopp opptaket"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deler skjermen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Deler innhold"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vil du slutte å dele skjermen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Vil du slutte å dele?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du deler nå hele skjermen med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du deler nå hele skjermen med en app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du deler nå <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du deler nå en app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Du deler med en app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Slutt å dele"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Caster skjermen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vil du stoppe castingen?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Innenhet"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Slår på …"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forhåndsinnstilling"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Direkteteksting"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du oppheve blokkeringen av enhetsmikrofonen?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du oppheve blokkeringen av enhetskameraet?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du oppheve blokkeringen av enhetskameraet og -mikrofonen?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Moduler på låseskjermen"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Hvem som helst kan se moduler på låseskjermen – selv om nettbrettet er låst."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"velg bort modul"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Reduser høyden"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Øk høyden"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Låseskjermmoduler"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Fjern alt"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Logg"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Varslingsinnstillinger"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Varsellogg"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Ny"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Lydløs"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Varsler"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nå"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ingen varsler"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ingen nye varsler"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Varseldemping er slått på"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enhetsvolumet og varsler reduseres automatisk i opptil 2 min når du får for mange varsler samtidig."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Slå av"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås opp for å se eldre varsler"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fast"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hodesporing"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trykk for å endre ringemodus"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modus for ringeprogrammet"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bytt til appen til høyre eller under mens du bruker delt skjerm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bytt til appen til venstre eller over mens du bruker delt skjerm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"I delt skjerm: Bytt ut en app"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Skrivespråk"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Bytt til neste språk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Bytt til forrige språk"</string>
@@ -964,7 +964,7 @@
<string name="data_connection_no_internet" msgid="691058178914184544">"Ingen internettilkobling"</string>
<string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string>
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Endre rekkefølgen på innstillingene."</string>
- <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"På/av-meny"</string>
+ <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Av/på-meny"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskjerm"</string>
<string name="finder_active" msgid="7907846989716941952">"Du kan finne denne telefonen med Finn enheten min, selv når den er slått av"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktiv app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tilpass hurtigtastene"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ingen søkeresultater"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpass"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Ferdig"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger med tastaturet"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lær deg hurtigtaster"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger med styreflaten"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Levert av apper"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjerm"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukjent"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Tilbakestill brikkene"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vil du tilbakestille brikkene til den opprinnelige rekkefølgen og størrelsen?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vil du tilbakestille alle brikkene?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle brikker for hurtiginnstillinger tilbakestilles til enhetens opprinnelige innstillinger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index a9efd1d95a7d..3ed1a4e4aa7a 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Av"</item>
<item msgid="3028994095749238254">"På"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 381118ac4d6c..82f9aca68198 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तपाईंको स्क्रिन रेकर्ड गर्ने हो?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एउटा एप रेकर्ड गर्नुहोस्"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरै स्क्रिन रेकर्ड गर्नुहोस्"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"सम्पूर्ण स्क्रिन रेकर्ड गर्नुहोस्: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तपाईंले आफ्नो पूरै स्क्रिन रेकर्ड गरिरहेका बेला तपाईंको स्क्रिनमा देखाइने सबै सामग्री रेकर्ड गरिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तपाईंले यो एप रेकर्ड गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री रेकर्ड गरिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रिन रेकर्ड गर्नुहोस्"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"तपाईं अहिले <xliff:g id="APP_NAME">%1$s</xliff:g> रेकर्ड गरिरहनुभएको छ"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रेकर्ड गर्न छाड्नुहोस्"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रिन सेयर गरिँदै छ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"सामग्री सेयर गरिँदै छ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रिन सेयर गर्न छाड्ने हो?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"सेयर गर्न छाड्ने हो?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"तपाईं अहिले <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> सँग आफ्नो डिभाइसको पूरै स्क्रिन सेयर गरिरहनुभएको छ"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"तपाईं अहिले कुनै एपसँग आफ्नो डिभाइसको पूरै स्क्रिन सेयर गरिरहनुभएको छ"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"तपाईं अहिले <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> सेयर गरिरहनुभएको छ"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"तपाईं अहिले कुनै एप सेयर गरिरहनुभएको छ"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"तपाईं अहिले एपसँग सेयर गरिरहनुभएको छ"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"सेयर गर्न छाड्नुहोस्"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्क्रिन कास्ट गरिँदै छ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट गर्न छाड्ने हो?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"हियरिङ डिभाइसहरू"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सक्रिय गर्दै…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइभ क्याप्सन"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"लक स्क्रिन विजेटहरू"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"तपाईंको ट्याब्लेट लक भएका बेला पनि सबैले लक स्क्रिनमा भएका विजेट हेर्न सक्छन्।"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेटको चयन रद्द गर्नुहोस्"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"उचाइ घटाउनुहोस्"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"उचाइ बढाउनुहोस्"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लक स्क्रिन विजेटहरू"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"सबै हटाउनुहोस्"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थित गर्नुहोस्"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"हिस्ट्री"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"नोटिफिकेसन सेटिङ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"नोटिफिकेसनसम्बन्धी हिस्ट्री"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"नयाँ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"साइलेन्ट"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाहरू"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"कुनै सूचनाहरू छैनन्"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"कुनै पनि नयाँ सूचना छैन"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"नोटिफिकेसन कुलडाउन अन छ"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"नोटिफिकेसन कुलडाउन अहिले अन छ"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"तपाईंले एकै पटक धेरै नोटिफिकेसन प्राप्त गर्दा बढीमा २ मिनेटसम्म तपाईंको डिभाइसको भोल्युम र अलर्टहरूको सङ्ख्या स्वतः घटाइन्छ।"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"अफ गर्नुहोस्"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुराना सूचनाहरू हेर्न अनलक गर्नुहोस्"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"निश्चित"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"हेड ट्र्याकिङ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"रिङ्गर मोड बदल्न ट्याप गर्नुहोस्"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"घण्टी बजाउने मोड"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा दायाँ वा तलको एप चलाउनुहोस्"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा बायाँ वा माथिको एप चलाउनुहोस्"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रिन प्रयोग गरिएका बेला: एउटा स्क्रिनमा भएको एप अर्कोमा लैजानुहोस्"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"अर्को भाषा प्रयोग गर्नुहोस्"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"अघिल्लो भाषा प्रयोग गर्नुहोस्"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"हालको एप"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सर्वसुलभता"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"किबोर्डका सर्टकटहरू कस्टमाइज गर्नुहोस्"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कुनै पनि खोज परिणाम भेटिएन"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइज गर्नुहोस्"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूरा भयो"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"किबोर्ड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"किबोर्डका सर्टकटहरू प्रयोग गर्न सिक्नुहोस्"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
@@ -1441,7 +1451,7 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"तीन वटा औँला प्रयोग गरी टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"राम्रो!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले जेस्चर प्रयोग गरी पछाडि जाने तरिका सिक्नुभएको छ।"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमपेजमा जानुहोस्"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"टचप्याडमा तीन वटा औँलाले माथितिर स्वाइप गर्नुहोस्"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"अद्भुत!"</string>
@@ -1449,11 +1459,11 @@
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"तीन वटा औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"अद्भुत!"</string>
- <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले हालसालै चलाइएका एपहरू हेर्ने जेस्चर पूरा गर्नुभएको छ।"</string>
+ <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले जेस्चर प्रयोग गरी हालसालै चलाइएका एपहरू हेर्ने तरिका सिक्नुभएको छ।"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"सबै एपहरू हेर्नुहोस्"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"स्याबास!"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले \"सबै एपहरू हेर्नुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले जेस्चर प्रयोग गरी सबै एपहरू हेर्ने तरिका सिक्नुभएको छ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"एपले उपलब्ध गराएका"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिस्प्ले"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"टाइलहरू रिसेट गर्नुहोस्"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"टाइलहरूको डिफल्ट क्रम र आकार रिसेट गर्ने हो?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"सबै टाइलहरू रिसेट गर्ने हो?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"द्रुत सेटिङका सबै टाइलहरू रिसेट गरी डिभाइसका मूल सेटिङ लागू गरिने छन्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index c1b2f3420a40..2350c67d2bcf 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"अफ छ"</item>
<item msgid="3028994095749238254">"अन छ"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 8ff59ee93c98..3e0b5d9d144d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Je scherm opnemen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Eén app opnemen"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Hele scherm opnemen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Hele scherm opnemen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Als je je hele scherm opneemt, wordt alles opgenomen wat op je scherm wordt getoond. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Als je een app opneemt, wordt alles opgenomen wat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Scherm opnemen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Je neemt op dit moment <xliff:g id="APP_NAME">%1$s</xliff:g> op"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Opname stoppen"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Scherm delen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Content delen"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Scherm delen stoppen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Delen stoppen?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Je deelt op dit moment je hele scherm met <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Je deelt op dit moment je hele scherm met een app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Je deelt op dit moment <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Je deelt op dit moment een app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Je deelt op dit moment met een app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Delen stoppen"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Scherm casten"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stoppen met casten?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hoortoestellen"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aanzetten…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorinstelling"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live ondertiteling"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Microfoon van apparaat niet meer blokkeren?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Apparaatcamera niet meer blokkeren?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blokkeren van apparaatcamera en -microfoon opheffen?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets op het vergrendelscherm"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Iedereen kan widgets op je vergrendelscherm bekijken, ook als je tablet vergrendeld is."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget deselecteren"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Hoogte verkleinen"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Hoogte vergroten"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets op het vergrendelscherm"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Geen meldingen"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nieuwe meldingen"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Afkoelperiode van meldingen staat aan"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Afkoelperiode van meldingen staat nu aan"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Als je te veel meldingen tegelijk krijgt, worden het volume op je apparaat en meldingen automatisch maximaal 2 minuten beperkt."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Uitzetten"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontgrendel om oudere meldingen te zien"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vast"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdtracking"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"beltoonmodus"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Naar de app rechts of onderaan gaan als je een gesplitst scherm gebruikt"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Naar de app links of bovenaan gaan als je een gesplitst scherm gebruikt"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Tijdens gesplitst scherm: een app vervangen door een andere"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Overschakelen naar volgende taal"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Overschakelen naar vorige taal"</string>
@@ -1409,23 +1410,34 @@
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Recente apps"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gesplitst scherm"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snelkoppelingen"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-sneltoetsen"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sneltoetsen aanpassen"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sneltoetsen zoeken"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen zoekresultaten"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Aanpassen"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Toetsenbordinstellingen"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeren met je toetsenbord"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer sneltoetsen die je kunt gebruiken"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeren met je touchpad"</string>
@@ -1442,11 +1454,11 @@
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Je weet nu hoe je het gebaar voor terug maakt."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Naar startscherm"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe met 3 vingers omhoog op de touchpad"</string>
- <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Goed werk!"</string>
+ <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Goed gedaan!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Je weet nu hoe je het gebaar Naar startscherm maakt"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Recente apps bekijken"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe met 3 vingers omhoog en houd vast op de touchpad"</string>
- <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed werk!"</string>
+ <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed gedaan!"</string>
<string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle apps bekijken"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Geleverd door apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Scherm"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Tegels resetten"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Tegels resetten naar de oorspronkelijke volgorde en grootte?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Alle tegels resetten?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle tegels voor Snelle instellingen worden teruggezet naar de oorspronkelijke instellingen van het apparaat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index c5d93610fb87..4193463cae8e 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Uit"</item>
<item msgid="3028994095749238254">"Aan"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 60864409ee7f..980c47d13883 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରିବେ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ଗୋଟିଏ ଆପ ରେକର୍ଡ କରନ୍ତୁ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରନ୍ତୁ: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ଆପଣ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା ସବୁକିଛି ରେକର୍ଡ ହୋଇଥାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ଆପଣ ଏକ ଆପ ରେକର୍ଡ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି ରେକର୍ଡ ହୋଇଥାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME">%1$s</xliff:g>ର ବିଷୟବସ୍ତୁକୁ ରେକର୍ଡ କରୁଛନ୍ତି"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ରେକର୍ଡିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ସ୍କ୍ରିନ ସେୟାର କରାଯାଉଛି"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"ବିଷୟବସ୍ତୁ ସେୟାର କରାଯାଉଛି"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ସ୍କ୍ରିନ ସେୟାର କରିବା ବନ୍ଦ କରିବେ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"ସେୟାର କରିବା ବନ୍ଦ କରିବେ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ସହ ସେୟାର କରୁଛନ୍ତି"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଏକ ଆପ ସହ ସେୟାର କରୁଛନ୍ତି"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>କୁ ସେୟାର କରୁଛନ୍ତି"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ଆପଣ ବର୍ତ୍ତମାନ ଏକ ଆପକୁ ସେୟାର କରୁଛନ୍ତି"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"ଆପଣ ବର୍ତ୍ତମାନ ଏକ ଆପ ସହ ସେୟାର କରୁଛନ୍ତି"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ସେୟାର କରିବା ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ସ୍କ୍ରିନ କାଷ୍ଟ କରାଯାଉଛି"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ଇନପୁଟ୍"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ଅନ୍ ହେଉଛି…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ ସ୍କ୍ରିନ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ଲାଇଭ କେପ୍ସନ"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଲକ ସ୍କ୍ରିନରେ ୱିଜେଟକୁ ଭ୍ୟୁ କରିପାରିବେ।"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ୱିଜେଟକୁ ଅଚୟନ କରନ୍ତୁ"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ଉଚ୍ଚତାକୁ କମ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ଉଚ୍ଚତାକୁ ବଢ଼ାନ୍ତୁ"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ଇତିହାସ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"ବିଜ୍ଞପ୍ତି ସେଟିଂସ"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"ବିଜ୍ଞପ୍ତି ଇତିହାସ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ନୂଆ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ନୀରବ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"କୌଣସି ନୂଆ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ନାହିଁ"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ବିଜ୍ଞପ୍ତି କୁଲଡାଉନ ଚାଲୁ ଅଛି"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ଆପଣ ଥରକେ ଏକାଧିକ ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ କଲେ ଆପଣଙ୍କ ଡିଭାଇସର ଭଲ୍ୟୁମ ଓ ଆଲର୍ଟ ସ୍ୱତଃ 2 ମିନିଟ ପର୍ଯ୍ୟନ୍ତ କମ ହୁଏ।"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ପୁରୁଣା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଦେଖିବାକୁ ଅନଲକ କରନ୍ତୁ"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ନିଶ୍ଚିତ ହୋଇଛି"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ହେଡ ଟ୍ରାକିଂ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ରିଙ୍ଗର୍ ମୋଡ୍ ବଦଳାଇବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ରିଂଗର ମୋଡ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍‍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string>
@@ -814,9 +812,9 @@
<string name="keyboard_key_home" msgid="3734400625170020657">"ହୋମ"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"ଫେରନ୍ତୁ"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"ସ୍ପେସ୍‍"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"ଏଣ୍ଟର୍"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"ବ୍ୟାକସ୍ପେସ୍‍"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"ସ୍ପେସ"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"ଏଣ୍ଟର"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"ବେକସ୍ପେସ"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"ପ୍ଲେ କରନ୍ତୁ/ପଜ୍‍ କରନ୍ତୁ"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"ପରବର୍ତ୍ତୀ"</string>
@@ -826,7 +824,7 @@
<string name="keyboard_key_page_up" msgid="173914303254199845">"ଉପର ପୃଷ୍ଠା"</string>
<string name="keyboard_key_page_down" msgid="9035902490071829731">"ତଳ ପୃଷ୍ଠା"</string>
<string name="keyboard_key_forward_del" msgid="5325501825762733459">"ଡିଲିଟ କରନ୍ତୁ"</string>
- <string name="keyboard_key_esc" msgid="6230365950511411322">"ଏସକେପ"</string>
+ <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
<string name="keyboard_key_move_home" msgid="3496502501803911971">"ହୋମ"</string>
<string name="keyboard_key_move_end" msgid="99190401463834854">"ସମାପ୍ତ"</string>
<string name="keyboard_key_insert" msgid="4621692715704410493">"ଇନ୍‌ସର୍ଟ"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ଡାହାଣପଟର ବା ତଳର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ସମୟରେ: କୌଣସି ଆପକୁ ଗୋଟିଏରୁ ଅନ୍ୟ ଏକ ଆପରେ ବଦଳାନ୍ତୁ"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ଇନପୁଟ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ପରବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ପୂର୍ବବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string>
@@ -886,7 +886,7 @@
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ଇମେଲ୍"</string>
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"ମ୍ୟୁଜିକ୍‍"</string>
- <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"କ୍ୟାଲେଣ୍ଡର"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
<string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"କାଲକୁଲେଟର"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ବର୍ତ୍ତମାନର ଆପ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ଆକ୍ସେସିବିଲିଟୀ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"କୌଣସି ସର୍ଚ୍ଚ ଫଳାଫଳ ନାହିଁ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ହୋଇଗଲା"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ଆପଣଙ୍କ କୀବୋର୍ଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ଆପ୍ସ ଦ୍ୱାରା ପ୍ରଦାନ କରାଯାଇଛି"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ଡିସପ୍ଲେ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ଅଜଣା"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ଟାଇଲଗୁଡ଼ିକୁ ରିସେଟ କରନ୍ତୁ"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ଟାଇଲଗୁଡ଼ିକୁ ସେଗୁଡ଼ିକର ମୂଳ କ୍ରମ ଏବଂ ସାଇଜ ଅନୁସାରେ ରିସେଟ କରିବେ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ସମସ୍ତ ଟାଇଲକୁ ରିସେଟ କରିବେ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ସମସ୍ତ କୁଇକ ସେଟିଂସ ଟାଇଲ ଡିଭାଇସର ମୂଳ ସେଟିଂସରେ ରିସେଟ ହୋଇଯିବ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index fe187c2ff082..35afd612e6b6 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ବନ୍ଦ ଅଛି"</item>
<item msgid="3028994095749238254">"ଚାଲୁ ଅଛି"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index b99986a5e955..8cc9f6e57939 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ਕੀ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਹੈ?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ਇੱਕ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ਸਾਰੀ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਕਰੋ: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਨਾਲ ਹੀ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਉਸ ਐਪ ਵਿੱਚ ਦਿਖਾਈ ਜਾਂ ਚਲਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਕਰੋ"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਕਰੋ"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"ਸਮੱਗਰੀ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"ਕੀ ਸਾਂਝਾਕਰਨ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ਨਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨਾਲ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ਸਾਂਝਾਕਰਨ ਬੰਦ ਕਰੋ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ਸਕ੍ਰੀਨ \'ਤੇ ਕਾਸਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ਕੀ ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ਇਨਪੁੱਟ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ਸਵੈ-ਘੁਮਾਓ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ਪ੍ਰੀਸੈੱਟ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ਪ੍ਰੀਸੈੱਟ"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ਕੋਈ ਵੀ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ।"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ਵਿਜੇਟ ਨੂੰ ਅਣਚੁਣਿਆ ਕਰੋ"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ਉਚਾਈ ਘਟਾਓ"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ਉਚਾਈ ਵਧਾਓ"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ਕੋਈ ਨਵੀਂ ਸੂਚਨਾ ਨਹੀਂ ਹੈ"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ਨੋਟੀਫ਼ਿਕੇਸ਼ਨ ਕੂਲਡਾਊਨ ਚਾਲੂ ਹੈ"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ਇੱਕ ਵਾਰ \'ਚ ਕਈ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ, ਡੀਵਾਈਸ ਦੀ ਅਵਾਜ਼ ਅਤੇ ਅਲਰਟ ਵੱਧੋ-ਵੱਧ 2 ਮਿੰਟਾਂ ਲਈ ਆਪਣੇ-ਆਪ ਘੱਟ ਜਾਂਦੇ ਹਨ।"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ਬੰਦ ਕਰੋ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ਪੁਰਾਣੀਆਂ ਸੂਚਨਾਵਾਂ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ਸਥਿਰ"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ਹੈੱਡ ਟਰੈਕਿੰਗ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ਰਿੰਗਰ ਮੋਡ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ਰਿੰਗਰ ਮੋਡ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਸੱਜੇ ਜਾਂ ਹੇਠਾਂ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਖੱਬੇ ਜਾਂ ਉੱਪਰ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੌਰਾਨ: ਇੱਕ ਐਪ ਨਾਲ ਦੂਜੀ ਐਪ ਨੂੰ ਬਦਲੋ"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ਇਨਪੁੱਟ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ਅਗਲੀ ਭਾਸ਼ਾ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ਪਿਛਲੀ ਭਾਸ਼ਾ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ਮੌਜੂਦਾ ਐਪ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ਪਹੁੰਚਯੋਗਤਾ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਸ਼ਾਰਟਕੱਟ ਖੋਜੋ"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ਹੋ ਗਿਆ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਬਾਰੇ ਜਾਣੋ"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ਐਪਾਂ ਵੱਲੋਂ ਮੁਹੱਈਆ ਕੀਤਾ ਗਿਆ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ਡਿਸਪਲੇ"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ਅਗਿਆਤ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ਟਾਇਲਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ਕੀ ਟਾਇਲਾਂ ਨੂੰ ਉਨ੍ਹਾਂ ਦੇ ਮੂਲ ਕ੍ਰਮ ਅਤੇ ਆਕਾਰਾਂ \'ਤੇ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ਕੀ ਸਾਰੀਆਂ ਟਾਇਲਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ਸਾਰੀਆਂ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਟਾਇਲਾਂ ਡੀਵਾਈਸ ਦੀਆਂ ਮੂਲ ਸੈਟਿੰਗਾਂ \'ਤੇ ਰੀਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 62dc05ad67bb..ab2f8abdd739 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ਬੰਦ ਹੈ"</item>
<item msgid="3028994095749238254">"ਚਾਲੂ ਹੈ"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 88177ac56bb7..c183116e3528 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Nagrywać ekran?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nagrywaj jedną aplikację"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nagrywaj cały ekran"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Nagraj cały ekran: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kiedy nagrywasz cały ekran, nagrane zostanie wszystko, co jest na nim widoczne. Dlatego uważaj na hasła, dane do płatności, wiadomości, zdjęcia, nagrania audio czy filmy."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kiedy nagrywasz aplikację, wszystko, co jest w niej wyświetlane lub odtwarzane, zostaje nagrane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nagrywaj ekran"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Obecnie nagrywasz widok aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zatrzymaj nagrywanie"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Udostępniam ekran"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Udostępniasz treści"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Zatrzymać udostępnianie ekranu?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Przestać udostępniać?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Obecnie udostępniasz aplikacji <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> cały widok ekranu"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Obecnie udostępniasz aplikacji cały widok ekranu"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Obecnie udostępniasz aplikację <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Obecnie udostępniasz aplikację"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Obecnie udostępniasz treści aplikacji"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zatrzymaj udostępnianie"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Przesyłam zawartość ekranu"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Zatrzymać przesyłanie?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Wejście"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparaty słuchowe"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Włączam…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Gotowe ustawienie"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Napisy na żywo"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokować mikrofon urządzenia?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokować aparat urządzenia?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokować aparat i mikrofon urządzenia?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widżety na ekranie blokady"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Widżety są widoczne na ekranie blokady, nawet gdy tablet jest zablokowany."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"odznacz widżet"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Zmniejsz wysokość"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Zwiększ wysokość"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widżety na ekranie blokady"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Usuń wszystkie"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Zarządzaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Ustawienia powiadomień"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historia powiadomień"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nowe"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Ciche"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Powiadomienia"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Brak powiadomień"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Brak nowych powiadomień"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Wyciszanie powiadomień jest włączone"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Wyciszanie powiadomień jest teraz włączone"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Gdy w krótkim czasie otrzymasz za dużo powiadomień, dźwięki zostaną automatycznie wyciszone na maks. 2 min."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Wyłącz"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odblokuj i zobacz starsze powiadomienia"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Stały"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Śledzenie głowy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Kliknij, aby zmienić tryb dzwonka"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"tryb dzwonka"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Przełącz się na aplikację po prawej lub poniżej na podzielonym ekranie"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Przełącz się na aplikację po lewej lub powyżej na podzielonym ekranie"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Podczas podzielonego ekranu: zastępowanie aplikacji"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Wprowadzanie"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Przełącz na następny język"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Przełącz na poprzedni język"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Bieżąca aplikacja"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Dostosuj skróty klawiszowe"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Brak wyników wyszukiwania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Dostosuj"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotowe"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ustawienia klawiatury"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Dowiedz się więcej o skrótach klawiszowych"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nawiguj za pomocą touchpada"</string>
@@ -1476,7 +1486,7 @@
<string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Naciśnij klawisz działania w dowolnym momencie. Kliknij, aby poznać więcej gestów."</string>
<string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatkowe przyciemnienie jest teraz częścią suwaka jasności"</string>
<string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Możesz teraz dodatkowo przyciemnić ekran, jeszcze bardziej zmniejszając poziom jasności.\n\nTa funkcja jest teraz częścią suwaka jasności, więc skróty do dodatkowego przyciemniania zostaną usunięte."</string>
- <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Usuń skróty do dodatkowego przyciemnienia"</string>
+ <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Usuń skróty do dodatkowego przyciemniania"</string>
<string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Skróty do dodatkowego przyciemnienia zostały usunięte"</string>
<string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Łączność"</string>
<string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Ułatwienia dostępu"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Z aplikacji"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Wyświetlacz"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nieznane"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Zresetuj kafelki"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Zresetować kafelki do pierwotnej kolejności i rozmiarów?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Zresetować wszystkie kafelki?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Wszystkie kafelki Szybkich ustawień zostaną zresetowane do oryginalnych ustawień urządzenia"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 5aa719f2ea75..d3191b0f6631 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Wyłączono"</item>
<item msgid="3028994095749238254">"Włączone"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e67c16856e58..f1dd9a4b27b1 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Gravar a tela toda: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Você está gravando o app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Parar gravação"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartilhando a tela"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Compartilhando conteúdo"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar o compartilhamento de tela?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Parar de compartilhar?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Você está compartilhando a tela inteira com o app <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Você está compartilhando a tela inteira com um app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Você está compartilhando o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Você está compartilhando um app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Você está compartilhando com um app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interromper compartilhamento"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitindo a tela"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar transmissão?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
@@ -408,7 +406,7 @@
<string name="custom" msgid="3337456985275158299">"Personalizado"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string>
<string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da tela de bloqueio"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Diminuir altura"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar altura"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Remover tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Configurações de notificação"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Histórico de notificações"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciosas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nenhuma notificação nova"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"O recurso de atenuar notificações está ativo"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e alertas são reduzidos por até 2 min quando você recebe muitas notificações juntas."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie p/ acessar notificações antigas"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixo"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -814,7 +812,7 @@
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"Espaço"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Barra de espaço"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Reproduzir/pausar"</string>
@@ -843,7 +841,7 @@
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar a consulta de pesquisa"</string>
<string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Atalhos do teclado"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Atalhos de pesquisa"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pesquisar atalhos"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string>
@@ -859,7 +857,7 @@
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostrar atalhos"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Voltar"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Ir para a tela inicial"</string>
- <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Conferir os apps recentes"</string>
+ <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Ver os apps recentes"</string>
<string name="group_system_cycle_forward" msgid="5478663965957647805">"Avançar pela lista de apps recentes"</string>
<string name="group_system_cycle_back" msgid="8194102916946802902">"Voltar pela lista de apps recentes"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Abrir lista de apps"</string>
@@ -868,12 +866,14 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o aplicativo atual à direita"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app atual à esquerda"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o app à direita"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app à esquerda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
@@ -1461,7 +1472,7 @@
<string name="volume_undo_action" msgid="5815519725211877114">"Desfazer"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Se quiser voltar, deslize para a esquerda ou direita com três dedos no touchpad"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Se quiser acessar a tela inicial, deslize para cima com três dedos no touchpad"</string>
- <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione com três dedos no touchpad"</string>
+ <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione o touchpad com três dedos"</string>
<string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todos os apps, pressione a tecla de ação no teclado"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"Encoberto"</string>
<string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquear para visualizar"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fornecidos por apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Exibição"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Redefinir blocos"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Redefinir os blocos para a ordem e os tamanhos originais?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Redefinir todos os blocos?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index 3526c77d1e24..e315bad6bf80 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desativar"</item>
<item msgid="3028994095749238254">"Ativar"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 30e16f06f00f..03c32a834721 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar o ecrã?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar uma app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar o ecrã inteiro"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Gravar o ecrã inteiro: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando está a gravar o ecrã inteiro, tudo o que é apresentado no ecrã é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando está a gravar uma app, tudo o que é apresentado ou reproduzido nessa app é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar ecrã"</string>
@@ -327,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"A ativar..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
@@ -415,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legendas instantâneas"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmara do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Quer desbloquear a câmara e o microfone?"</string>
@@ -578,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerir"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Definições de notificação"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Histórico de notificações"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Nova"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencioso"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
@@ -592,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Não existem novas notificações"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"O repouso das notificações está ativado"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"O repouso das notificações está agora ativado"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"O volume e os alertas são reduzidos automaticamente durante até 2 minutos quando recebe muitas notificações de uma vez."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie e veja notificações antigas"</string>
@@ -700,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Corrigido"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Posição da cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para alterar o modo de campainha"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de som"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"reativar som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -869,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para a app à direita ou abaixo enquanto usa o ecrã dividido"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para a app à esquerda ou acima enquanto usa o ecrã dividido"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Durante o ecrã dividido: substituir uma app por outra"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para idioma seguinte"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para idioma anterior"</string>
@@ -1410,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalize os atalhos de teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado da pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Indicador para arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Definições do teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue com o teclado"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos de teclado"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue com o touchpad"</string>
@@ -1480,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disponibilizado por apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ecrã"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecido"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reponha os mosaicos"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Repor os mosaicos para a ordem e os tamanhos originais?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Repor todos os mosaicos?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os mosaicos de Definições rápidas vão ser repostos para as definições originais do dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index 34a5ed7b2ca9..deb6783e1b23 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desativados"</item>
<item msgid="3028994095749238254">"Ativados"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e67c16856e58..f1dd9a4b27b1 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Gravar a tela toda: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Você está gravando o app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Parar gravação"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartilhando a tela"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Compartilhando conteúdo"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar o compartilhamento de tela?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Parar de compartilhar?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Você está compartilhando a tela inteira com o app <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Você está compartilhando a tela inteira com um app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Você está compartilhando o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Você está compartilhando um app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Você está compartilhando com um app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interromper compartilhamento"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitindo a tela"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar transmissão?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
@@ -408,7 +406,7 @@
<string name="custom" msgid="3337456985275158299">"Personalizado"</string>
<string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string>
<string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string>
- <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
+ <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da tela de bloqueio"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Diminuir altura"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar altura"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Remover tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Configurações de notificação"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Histórico de notificações"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciosas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nenhuma notificação nova"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"O recurso de atenuar notificações está ativo"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e alertas são reduzidos por até 2 min quando você recebe muitas notificações juntas."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie p/ acessar notificações antigas"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixo"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
@@ -814,7 +812,7 @@
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
- <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
+ <string name="keyboard_key_space" msgid="6980847564173394012">"Espaço"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Barra de espaço"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Reproduzir/pausar"</string>
@@ -843,7 +841,7 @@
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar a consulta de pesquisa"</string>
<string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Atalhos do teclado"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Atalhos de pesquisa"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pesquisar atalhos"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistema"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string>
@@ -859,7 +857,7 @@
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostrar atalhos"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Voltar"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Ir para a tela inicial"</string>
- <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Conferir os apps recentes"</string>
+ <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Ver os apps recentes"</string>
<string name="group_system_cycle_forward" msgid="5478663965957647805">"Avançar pela lista de apps recentes"</string>
<string name="group_system_cycle_back" msgid="8194102916946802902">"Voltar pela lista de apps recentes"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Abrir lista de apps"</string>
@@ -868,12 +866,14 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Criar nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitarefas"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o aplicativo atual à direita"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app atual à esquerda"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Usar a tela dividida com o app à direita"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Usar a tela dividida com o app à esquerda"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Mudar da tela dividida para a tela cheia"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizar atalhos de teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pesquisar atalhos"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configurações do teclado"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
@@ -1461,7 +1472,7 @@
<string name="volume_undo_action" msgid="5815519725211877114">"Desfazer"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"Se quiser voltar, deslize para a esquerda ou direita com três dedos no touchpad"</string>
<string name="home_edu_toast_content" msgid="3381071147871955415">"Se quiser acessar a tela inicial, deslize para cima com três dedos no touchpad"</string>
- <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione com três dedos no touchpad"</string>
+ <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione o touchpad com três dedos"</string>
<string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todos os apps, pressione a tecla de ação no teclado"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"Encoberto"</string>
<string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquear para visualizar"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fornecidos por apps"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Exibição"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Redefinir blocos"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Redefinir os blocos para a ordem e os tamanhos originais?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Redefinir todos os blocos?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index 3526c77d1e24..e315bad6bf80 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Desativar"</item>
<item msgid="3028994095749238254">"Ativar"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index de7f8dac8dd1..5b3190fef48c 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Înregistrezi ecranul?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Înregistrează o aplicație"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Înregistrează tot ecranul"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Înregistrează tot ecranul: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Când înregistrezi întregul ecran, se înregistrează tot ce apare pe ecran. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Când înregistrezi o aplicație, se înregistrează tot ce se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Înregistrează ecranul"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Înregistrezi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Oprește înregistrarea"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Se permite accesul la ecran"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Se permite accesul la conținut"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Oprești accesul la ecran?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Nu mai permiți accesul?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Permiți accesul <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> la întregul ecran"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Permiți accesul unei aplicații la întregul ecran"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Permiți accesul la <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Permiți accesul la o aplicație"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Permiți accesul unei aplicații"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Nu mai permite accesul"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Se proiectează ecranul"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Oprești proiectarea?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Intrare"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparate auditive"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Se activează..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrări live"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeturi pe ecranul de blocare"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Oricine poate vedea widgeturile pe ecranul de blocare, chiar cu tableta blocată"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deselectează widgetul"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Redu înălțimea"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Crește înălțimea"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeturi pe ecranul de blocare"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Șterge toate notificările"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestionează"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istoric"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Setări pentru notificări"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Istoricul notificărilor"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Noi"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silențioase"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nicio notificare"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nicio notificare nouă"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Reducerea sunetului notificărilor este activată"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volumul și alertele dispozitivului sunt reduse automat timp de până la 2 min. când primești prea multe notificări odată"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Dezactivează"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Deblochează ca să vezi notificări vechi"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Urmărirea mișcărilor capului"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modul sonerie"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Treci la aplicația din dreapta sau de mai jos cu ecranul împărțit"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Treci la aplicația din stânga sau de mai sus cu ecranul împărțit"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"În modul ecran împărțit: înlocuiește o aplicație cu alta"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Introducere"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Comută la următoarea limbă"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Comută la limba anterioară"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicația actuală"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizează comenzile rapide de la tastatură"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Niciun rezultat al căutării"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizează"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gata"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navighează folosind tastatura"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Învață comenzile rapide de la tastatură"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navighează folosind touchpadul"</string>
@@ -1445,7 +1456,7 @@
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Înapoi la pagina de pornire"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Glisează în sus cu trei degete oriunde pe touchpad"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelent!"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ai finalizat gestul „accesează ecranul de pornire”"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ai finalizat gestul „înapoi la pagina de pornire”"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Vezi aplicațiile recente"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Glisează în sus și ține apăsat cu trei degete pe touchpad"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Excelent!"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Oferite de aplicații"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ecran"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Necunoscută"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetează cardurile"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Resetezi cardurile la ordinea și dimensiunile inițiale?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Resetezi toate cardurile?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toate cardurile Setări rapide se vor reseta la setările inițiale ale dispozitivului"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index a68f1408dd83..fa950c30850a 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Dezactivat"</item>
<item msgid="3028994095749238254">"Activat"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a007685d4af3..665bd26a386e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Начать запись экрана?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записывать одно приложение"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записывать весь экран"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Записывать весь экран: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Во время записи всего экрана все данные и действия, которые на нем показываются, попадают на видео. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Во время записи приложения все данные и действия, которые показываются в его окне, попадают на видео. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Запись экрана"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Вы записываете экран приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Остановить запись"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Демонстрация экрана"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Вы делитесь контентом"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Прекратить показ экрана?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Закрыть доступ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Вы демонстрируете свой экран в приложении \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\"."</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Вы демонстрируете свой экран в приложении."</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Вы демонстрируете экран приложения \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\"."</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Вы демонстрируете экран приложения."</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Сейчас вы делитесь контентом с приложением."</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Прекратить показ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Трансляция экрана"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Прекратить трансляцию?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Устройство ввода"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слуховые аппараты"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включение…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматические субтитры"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Виджеты на заблокированном экране"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Они видны всем, даже если планшет заблокирован."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"отменить выбор виджета"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Уменьшить высоту"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Увеличить высоту"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виджеты на заблокированном экране"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Нет уведомлений."</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Новых уведомлений нет"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Снижение громкости уведомлений включено"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Если придет слишком много уведомлений, на две минуты громкость и количество оповещений уменьшатся."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Отключить"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблокируйте, чтобы увидеть уведомления"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Без отсле­живания"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"С отсле­живанием"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Нажмите, чтобы изменить режим звонка."</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звонка"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти к приложению справа или внизу на разделенном экране"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Перейти к приложению слева или вверху на разделенном экране"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"В режиме разделения экрана заменить одно приложение другим"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ввод"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Выбрать следующий язык"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Выбрать предыдущий язык"</string>
@@ -1409,23 +1411,34 @@
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Недавние приложения"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Разделение экрана"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ввод"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ярлыки приложений"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Быстрые клавиши для приложений"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Это приложение"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Специальные возможности"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Как настроить быстрые клавиши"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ничего не найдено"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Настроить"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки клавиатуры"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигация с помощью клавиатуры"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Узнайте о сочетаниях клавиш."</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигация с помощью сенсорной панели"</string>
@@ -1433,8 +1446,8 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигация с помощью клавиатуры и сенсорной панели"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Узнайте о жестах на сенсорной панели, сочетаниях клавиш и многом другом."</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главную"</string>
- <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Жест \"Просмотр недавних приложений\""</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главный экран"</string>
+ <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Просмотр недавних приложений"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Проведите тремя пальцами влево или вправо по сенсорной панели."</string>
@@ -1442,7 +1455,7 @@
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы выполнили жест для перехода назад."</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На главный экран"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Проведите тремя пальцами вверх по сенсорной панели."</string>
- <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отличная работа!"</string>
+ <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отлично!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Вы выполнили жест для перехода на главный экран."</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Просмотр недавних приложений"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Проведите вверх по сенсорной панели тремя пальцами и удерживайте."</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Приложения"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Сброс параметров"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Сбросить порядок и размер параметров?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Сбросить все параметры?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Для всех параметров быстрых настроек будут восстановлены значения по умолчанию."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 592937c7b9f6..cd12baeadbb2 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Отключены"</item>
<item msgid="3028994095749238254">"Включены"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index ae9c4d0cf711..0df5d4494e67 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ඔබේ තිරය පටිගත කරන්න ද?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"එක් යෙදුමක් පටිගත කරන්න"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"සම්පූර්ණ තිරය පටිගත කරන්න"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"සම්පූර්ණ තිරය පටිගත කරන්න: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ඔබ ඔබේ සම්පූර්ණ තිරය පටිගත කරන විට, ඔබේ තිරයේ පෙන්වන ඕනෑම දෙයක් වාර්තා වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ඔබ යෙදුමක් පටිගත කරන විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් වාර්තා වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"තිරය පටිගත කරන්න"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ඔබ දැනට <xliff:g id="APP_NAME">%1$s</xliff:g> පටිගත කරමින් සිටී"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"පටිගත කිරීම නවත්වන්න"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"තිරය ​​බෙදා ගැනීම"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"අන්තර්ගතය බෙදා ගැනීම"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"තිරය ​​බෙදා ගැනීම නවත්වන්න ද?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"බෙදා ගැනීම නවත්වන්න ද?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> සමග බෙදා ගනිමින් සිටී"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය යෙදුමක් සමග බෙදා ගනිමින් සිටී"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ඔබ දැනට <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> බෙදා ගනිමින් සිටී"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ඔබ දැනට යෙදුමක් බෙදා ගනිමින් සිටී"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"ඔබ දැනට යෙදුමක් සමග බෙදා ගනිමින් සිටී"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"බෙදා ගැනීම නවත්වන්න"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"විකාශ තිරය"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"විකාශය නවතන්න ද?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ආදානය"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ශ්‍රවණාධාරක"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ක්‍රියාත්මක කරමින්…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්‍රීය කරකැවීම"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්‍රීයව-භ්‍රමණය වන තිරය"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"සජීවී සිරස්තල"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"අගුළු තිර විජට්"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ඔබේ ටැබ්ලටය අගුළු දමා තිබුණත්, ඕනෑම කෙනෙකුට ඔබේ අගුළු තිරයෙහි විජට් බැලිය හැක."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"විජට් නොතෝරන්න"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"උස අඩු කරන්න"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"උස වැඩි කරන්න"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"අගුළු තිර විජට්"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්‍යාපනය කිරීමට අවශ්‍ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"සියල්ල හිස් කරන්න"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"කළමනාකරණය කරන්න"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ඉතිහාසය"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"දැනුම්දීම් සැකසීම්"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"දැනුම්දීම් ඉතිහාසය"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"නව"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"නිහඬ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"දැනුම් දීම්"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"දැන් අරඹන්න"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"දැනුම්දීම් නැත"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"නව දැනුම්දීම් නැත"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"දැනුම්දීම් සිසිල් කිරීම ක්‍රියාත්මකයි"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ඔබට එකවර දැනුම්දීම් වැඩි ප්‍රමාණයක් ලැබෙන විට ඔබේ උපාංග පරිමාව සහ ඇඟවීම් මිනිත්තු 2ක් දක්වා ස්වයංක්‍රීයව අඩු වේ."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ක්‍රියාවිරහිත කරන්න"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"පැරණි දැනුම්දීම් බැලීමට අගුළු හරින්න"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"නියම කළ"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"හිස ලුහුබැඳීම"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"නාදකය වෙනස් කිරීමට තට්ටු කරන්න"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"හඬ නඟන ආකාරය"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"බෙදුම් තිරය භාවිත කරන අතරතුර දකුණේ හෝ පහළින් ඇති යෙදුමට මාරු වන්න"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"බෙදුම් තිරය භාවිත කරන අතරතුර වමේ හෝ ඉහළ ඇති යෙදුමට මාරු වන්න"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"බෙදුම් තිරය අතරතුර: යෙදුමක් එකකින් තවත් එකක් ප්‍රතිස්ථාපනය කරන්න"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ආදානය"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"මීළඟ භාෂාවට මාරු වන්න"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"පෙර භාෂාවට මාරු වන්න"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"වත්මන් යෙදුම"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ප්‍රවේශ්‍යතාව"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"යතුරුපුවරු කෙටිමං අභිරුචිකරණය කරන්න"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"සෙවීම් ප්‍රතිඵල නැත"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"අභිරුචිකරණය කරන්න"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"නිමයි"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ඔබේ යතුරු පුවරුව භාවිතයෙන් සංචාලනය කරන්න"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"යතුරුපුවරු කෙටිමං ඉගෙන ගන්න"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ඔබේ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"යෙදුම් මගින් සපයනු ලැබේ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"සංදර්ශකය"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"නොදනී"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ටයිල් යළි සකසන්න"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ටයිල් ඒවායේ මුල් අනුපිළිවෙලට සහ ප්‍රමාණයට යළි සකසන්න ද?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"සියලු ටයිල් නැවත සකසන්න ද?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"සියලු ඉක්මන් සැකසීම් ටයිල් උපාංගයේ මුල් සැකසීම් වෙත නැවත සකසනු ඇත"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 681f3d52bc09..595575d7eebc 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ක්‍රියාවිරහිතයි"</item>
<item msgid="3028994095749238254">"ක්‍රියාත්මකයි"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index c2235667f08b..7331ba015726 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Chcete nahrávať obrazovku?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrávať jednu aplikáciu"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrávať celú obrazovku"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Nahrať celú obrazovku: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri nahrávaní celej obrazovky sa zaznamená všetko, čo sa na nej zobrazuje. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri nahrávaní aplikácie sa zaznamená všetko, čo sa v nej zobrazuje alebo prehráva. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrávať obrazovku"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Momentálne nahrávate aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zastaviť nahrávanie"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Zdieľa sa obrazovka"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Obsah sa zdieľa"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Chcete prestať zdieľať obrazovku?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Chcete zastaviť zdieľanie?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Momentálne zdieľate celú obrazovku s aplikáciou <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Momentálne zdieľate celú obrazovku s aplikáciou"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Momentálne zdieľate aplikáciu <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Momentálne zdieľate aplikáciu"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Momentálne zdieľate s aplikáciou"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Prestať zdieľať"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Prenáša sa obrazovka"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Chcete zastaviť prenos?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Načúvadlá"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapína sa…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Živý prepis"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať kameru zariadenia?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Miniaplikácie na uzamknutej obrazovke"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Miniaplikácie na uzamknutej obrazovke uvidia všetci, aj keď je tablet uzamknutý."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"zrušiť výber miniaplikácie"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Znížiť výšku"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Zväčšiť výšku"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikácie na uzamknutej obrazovke"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustiť"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Žiadne upozornenia"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Žiadne nové upozornenia"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stlmenie upozornení je zapnuté"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Keď dostanete priveľa upozornení naraz, až na dve minúty sa zníži hlasitosť zariadenia a upozornenia sa obmedzia."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnúť"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odomknutím zobrazíte staršie upozornenia"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Pevné"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sled. polohy hlavy"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvonenia"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
@@ -862,16 +862,18 @@
<string name="group_system_cycle_back" msgid="8194102916946802902">"Cyklické prechádzanie dozadu po nedávnych aplikáciách"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvorenie zoznamu aplikácií"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvorenie nastavení"</string>
- <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvorenie Asistenta"</string>
+ <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvoriť asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknutie obrazovky"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Napísanie poznámky"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Multitasking"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Použite rozdelenú obrazovku s aktuálnou aplikáciou vpravo"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Použite rozdelenú obrazovku s aktuálnou aplikáciou vľavo"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Rozdeliť obrazovku, aktuálna aplikácia vpravo"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Rozdeliť obrazovku, aktuálna aplikácia vľavo"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Prepnutie rozdelenej obrazovky na celú"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prechod na aplikáciu vpravo alebo dole pri rozdelenej obrazovke"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prechod na aplikáciu vľavo alebo hore pri rozdelenej obrazovke"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Počas rozdelenej obrazovky: nahradenie aplikácie inou"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Prepnutie na ďalší jazyk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prepnutie na predchádzajúci jazyk"</string>
@@ -1409,23 +1411,34 @@
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Nedávne aplikácie"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdelená obrazovka"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
- <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Odkazy do aplikácií"</string>
+ <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skratky aplikácií"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuálna aplikácia"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prispôsobenie klávesových skratiek"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prehľadávať skratky"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žiadne výsledky vyhľadávania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prispôsobiť"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavenia klávesnice"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Prechádzajte pomocou klávesnice"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte sa klávesové skratky"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Prechádzajte pomocou touchpadu"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Poskytnuté aplikáciami"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Zobrazovanie"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznáme"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Resetovanie kariet"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Chcete resetovať karty na pôvodné poradie a veľkosti?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Chcete resetovať všetky karty?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všetky karty rýchlych nastavení sa resetujú na pôvodné nastavenia zariadenia"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 6b5af805f15b..607c2215642b 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Vypnuté"</item>
<item msgid="3028994095749238254">"Zapnuté"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 15f1b3ccca7d..d0b0a9ddd0a3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite posneti zaslon?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snemanje ene aplikacije"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snemanje celotnega zaslona"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Snemanje celotnega zaslona: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri snemanju celotnega zaslona se posname vse, kar je prikazano na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri snemanju aplikacije se posname vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snemanje zaslona"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snemate aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ustavi snemanje"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deljenje zaslona"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Deljenje vsebine"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Želite ustaviti deljenje zaslona?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Želite ustaviti deljenje?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno delite celotni zaslon z aplikacijo <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno delite celotni zaslon z eno od aplikacij"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno delite aplikacijo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno delite eno od aplikacij"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Trenutno delite z eno od aplikacij"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ustavi deljenje"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Predvajanje vsebine zaslona"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite ustaviti predvajanje?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vhodna naprava"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Vklapljanje …"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Prednastavljeno"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Samodejni podnapisi"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite odblokirati mikrofon v napravi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite odblokirati fotoaparat v napravi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite odblokirati fotoaparat in mikrofon v napravi?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Pripomočki na zaklenjenem zaslonu"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Pripomočki na zaklenjenem zaslonu so vidni vsem, tudi če je tablica zaklenjena."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"preklic izbire pripomočka"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Zmanjšanje višine"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Povečanje višine"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pripomočki na zaklenjenem zaslonu"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši vse"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Zgodovina"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Nastavitve obvestil"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Zgodovina obvestil"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tiho"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obvestila"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ni obvestil"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ni novih obvestil"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Utišanje obvestil je vklopljeno"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Utišanje obvestil je zdaj vklopljeno"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ko prejmete preveč obvestil naenkrat, se glasnost naprave in opozoril samodejno zmanjša za največ 2 minuti."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Izklopi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odklenite za ogled starejših obvestil"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje premikov glave"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"način zvonjenja"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
@@ -815,7 +812,7 @@
<string name="keyboard_key_back" msgid="4185420465469481999">"Nazaj"</string>
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Preslednica"</string>
- <string name="keyboard_key_enter" msgid="8633362970109751646">"Vnesi"</string>
+ <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Vračalka"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Predvajaj/zaustavi"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Ustavi"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Preklop na aplikacijo desno ali spodaj med uporabo razdeljenega zaslona"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Preklop na aplikacijo levo ali zgoraj med uporabo razdeljenega zaslona"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Pri razdeljenem zaslonu: medsebojna zamenjava aplikacij"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vnos"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Preklop na naslednji jezik"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Preklop na prejšnji jezik"</string>
@@ -1415,27 +1414,38 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostopnost"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagajanje bližnjičnih tipk"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Iskanje po bližnjicah"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ni rezultatov iskanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Končano"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavitve tipkovnice"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krmarjenje s tipkovnico"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Učenje bližnjičnih tipk"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krmarjenje s sledilno ploščico"</string>
<string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite se uporabljati poteze na sledilni ploščici."</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krmarjenje s tipkovnico in sledilno ploščico"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Učenje potez na sledilni ploščici, bližnjičnih tipk in drugega"</string>
- <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazaj"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pojdi na začetni zaslon"</string>
+ <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Pomik nazaj"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pomik na začetni zaslon"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ogled nedavnih aplikacij"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Končano"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazaj"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Zagotavljajo aplikacije"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Zaslon"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznano"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Ponastavitev ploščic"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Želite ponastaviti ploščice na prvotni vrstni red in prvotno velikost?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite ponastaviti vse ploščice?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vse ploščice v hitrih nastavitvah bodo ponastavljene na prvotne nastavitve naprave."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index 5f60ffdaebf7..fddaea615bab 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Izklopljeno"</item>
<item msgid="3028994095749238254">"Vklopljeno"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7f74487ac2b0..428caf08c81d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Të regjistrohet ekrani?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Regjistro një aplikacion"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Regjistro të gjithë ekranin"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Regjistro gjithë ekranin: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kur regjistron të gjithë ekranin, regjistrohet çdo gjë e shfaqur në ekranin tënd. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kur regjistron një aplikacion, regjistrohet çdo gjë që shfaqet ose luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Regjistro ekranin"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Po regjistron aktualisht \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ndalo regjistrimin"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekrani po ndahet"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Po ndan përmbajtjen"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Të ndalohet ndarja e ekranit?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Të ndalohet ndarja?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Po ndan aktualisht të gjithë ekranin me \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Po ndan aktualisht të gjithë ekranin me një aplikacion"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Po ndan aktualisht \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Po ndan aktualisht një aplikacion"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Po ndan aktualisht me një aplikacion"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ndalo ndarjen"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Po transmeton ekranin"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Të ndalohet transmetimi?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Hyrja"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparatet e dëgjimit"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Po aktivizohet…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titrat në çast"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Miniaplikacionet në ekranin e kyçjes"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Çdo person mund të shikojë miniaplikacionet në ekranin tënd të kyçjes, edhe nëse tableti është i kyçur."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anulo zgjedhjen e miniaplikacionit"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Zvogëlo lartësinë"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Rrit lartësinë"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikacionet në ekranin e kyçjes"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Pastroji të gjitha"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Menaxho"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historiku"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Cilësimet e njoftimeve"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historiku i njoftimeve"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Të reja"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Në heshtje"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Njoftimet"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Fillo tani"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Asnjë njoftim"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nuk ka njoftime të reja"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Reduktimi i njoftimeve është aktiv"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volumi i pajisjes dhe sinjalizimet zvogëlohen automatikisht për deri në 2 minuta kur merr shumë njoftime në të njëjtën kohë."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Çaktivizo"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Shkyç për të parë njoftimet e vjetra"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"E fiksuar"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ndjekja e lëvizjeve të kokës"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Trokit për të ndryshuar modalitetin e ziles"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"modaliteti i ziles"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Kalo tek aplikacioni djathtas ose poshtë kur përdor ekranin e ndarë"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Kalo tek aplikacioni në të majtë ose sipër kur përdor ekranin e ndarë"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Gjatë ekranit të ndarë: zëvendëso një aplikacion me një tjetër"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Hyrja"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Kalo te gjuha tjetër"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Kalo te gjuha e mëparshme"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikacioni aktual"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizo shkurtoret e tastierës"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Asnjë rezultat kërkimi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizo"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"U krye"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigo duke përdorur tastierën tënde"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Mëso shkurtoret e tastierës"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigo duke përdorur bllokun me prekje"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Mundësuar nga aplikacionet"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekrani"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nuk njihet"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Rivendos pllakëzat"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Të rivendosen pllakëzat në rendin dhe madhësinë e tyre origjinale?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Të rivendosen të gjitha pllakëzat?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Të gjitha pllakëzat e \"Cilësimeve të shpejta\" do të rivendosen te cilësimet origjinale të pajisjes"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index 9b5032eecbd8..b30c8e7847de 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Joaktive"</item>
<item msgid="3028994095749238254">"Aktive"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index a1952d428f10..63b3e9a4f38d 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Желите да снимите екран?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Сними једну апликацију"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Сними цео екран"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Снимите цео екран: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Када снимате цео екран, снима се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Када снимате апликацију, снима се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Сними екран"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Тренутно снимате: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Заустави снимање"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Екран се дели"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Дељење садржаја"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Желите да зауставите дељење екрана?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Желите да зауставите дељење?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Тренутно делите цео екран са: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Тренутно делите цео екран са апликацијом"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Тренутно делите: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Тренутно делите апликацију"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Тренутно делите са апликацијом"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Заустави дељење"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Пребацује се екран"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Желите да зауставите пребацивање?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Унос"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни апарати"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Укључује се..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аутоматска ротација"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аутоматско ротирање екрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ажурирање задатих подешавања није успело"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Унапред одређена подешавања"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Титл уживо"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Желите да одблокирате камеру и микрофон уређаја?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Виџети за закључани екран"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Сви могу да виде виџете на закључаном екрану, чак и када је таблет закључан."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"поништи избор виџета"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Смањи висину"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Повећај висину"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети за закључани екран"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Нема обавештења"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нових обавештења"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Утишавање обавештења је укључено"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Утишавање обавештења је сада укључено"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Звук и број упозорења на уређају се аутоматски смањују на 2 минута када добијете превише обавештења."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Искључи"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Откључајте за старија обавештења"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Фиксно"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Праћење главе"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Додирните да бисте променили режим звона"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звона"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string>
@@ -814,7 +813,7 @@
<string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
<string name="keyboard_key_space" msgid="6980847564173394012">"Размак"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
- <string name="keyboard_key_backspace" msgid="4095278312039628074">"Тастер за брисање уназад"</string>
+ <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
<string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Тастер за репродукцију/паузирање"</string>
<string name="keyboard_key_media_stop" msgid="1509943745250377699">"Тастер за заустављање"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Тастер Следећа"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређи у апликацију здесна или испод док је подељен екран"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"У режиму подељеног екрана: замена једне апликације другом"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Унос"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Пређи на следећи језик"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Пређи на претходни језик"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Актуелна апликација"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Приступачност"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Тастерске пречице"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Прилагодите тастерске пречице"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Претражите пречице"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултата претраге"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Прилагоди"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Подешавања тастатуре"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Крећите се помоћу тастатуре"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Сазнајте више о тастерским пречицама"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Крећите се помоћу тачпеда"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Обезбеђују апликације"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Ресетујте плочице"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Желите да ресетујете плочице на првобитни редослед и величине?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Желите да ресетујете све плочице?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Све плочице Брзих подешавања ће се ресетовати на првобитна подешавања уређаја"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index 2acf1d264213..2a2e07459243 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Искључено"</item>
<item msgid="3028994095749238254">"Укључено"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b898eafc7f03..bffd40b6c5fe 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vill du spela in det som visas på skärmen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Spela in en app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Spela in hela skärmen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Spela in hela skärmen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"När du spelar in hela skärmen spelas allt som visas på skärmen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"När du spelar in en app spelas allt som visas eller spelas upp i appen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Spela in skärmen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du spelar för närvarande in <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Sluta spela in"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Skärmen delas"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Delar innehåll"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vill du sluta dela skärmen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Vill du sluta dela?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du delar för närvarande hela din skärm med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du delar för närvarande hela din skärm med en app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du delar för närvarande <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du delar för närvarande en app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Du delar för närvarande med en app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Sluta dela"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skärmen castas"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vill du sluta att casta?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingång"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörapparater"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverar …"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Förinställning"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vill du återaktivera enhetens mikrofon?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vill du återaktivera enhetens kamera?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vill du återaktivera enhetens kamera och mikrofon?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgetar för låsskärm"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Vem som helst kan se widgetar på din låsskärm, även om surfplattan är låst."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"avmarkera widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Minska höjden"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Öka höjden"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgetar för låsskärm"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Rensa alla"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Hantera"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Aviseringsinställningar"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Aviseringshistorik"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Ny"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Ljudlöst"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Aviseringar"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Starta nu"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Inga aviseringar"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Det finns inga nya aviseringar"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Dämpning av aviseringar är på"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enheten sänker volymen och minimerar aviseringar i upp till två minuter när du får för många aviseringar samtidigt."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Inaktivera"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås upp för att se äldre aviseringar"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Statiskt"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Huvudspårning"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Tryck för att ändra ringsignalens läge"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringsignalläge"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till höger eller nedanför när du använder delad skärm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Byt till appen till vänster eller ovanför när du använder delad skärm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Med delad skärm: ersätt en app med en annan"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inmatning"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Byt till nästa språk"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Byt till föregående språk"</string>
@@ -1410,24 +1410,35 @@
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string>
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Senaste apparna"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delad skärm"</string>
- <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ingång"</string>
+ <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inmatning"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuell app"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Anpassa kortkommandon"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Inga sökresultat"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassa"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klar"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tangentbordsinställningar"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigera med tangentbordet"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lär dig kortkommandon"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigera med styrplattan"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Tillhandahålls av appar"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skärm"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Okänt"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Återställ rutor"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Vill du återställa rutorna till den ursprungliga ordningen och storleken?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vill du återställa alla rutor?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alla Snabbinställningsrutor återställs till enhetens ursprungliga inställningar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index cf49f8db2606..b72f404c710f 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Av"</item>
<item msgid="3028994095749238254">"På"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8731337c88f0..2140a8ae06bb 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ungependa kurekodi skrini yako?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekodi programu moja"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekodi skrini nzima"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Rekodi skrini nzima: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Unaporekodi skrini yako nzima, chochote kinachoonyeshwa kwenye skrini yako kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Unaporekodi programu, chochote kinachoonyeshwa au kuchezwa kwenye programu hiyo kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekodi skrini"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Kwa sasa unarekodi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Acha kurekodi"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Inaruhusu ufikiaji kwenye skrini"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Unatuma maudhui"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ungependa kuacha kuonyesha skrini?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Ungependa kuacha kutuma maudhui?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye programu"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kwa sasa unaonyesha <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Kwa sasa unatumia programu pamoja na wengine"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Kwa sasa unatuma maudhui kwenye programu"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Acha kuonyesha skrini"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Inatuma skrini"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ungependa kuacha kutuma?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vifaa vya kuingiza sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Visaidizi vya kusikia"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Inawasha..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Mipangilio iliyowekwa mapema"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Manukuu Papo Hapo"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Ungependa kuwacha kuzuia maikrofoni ya kifaa?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Ungependa kuacha kuzuia kamera ya kifaa?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Ungependa kuwacha kuzuia kamera na maikrofoni ya kifaa?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Yeyote anaweza kuona wijeti kwenye skrini yako iliyofungwa, hata ikiwa umefunga kishikwambi chako."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"acha kuchagua wijeti"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Punguza urefu"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Ongeza urefu"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Futa zote"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Dhibiti"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Mipangilio ya arifa"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Historia ya arifa"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Mpya"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Kimya"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Arifa"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Anza sasa"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Hakuna arifa"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Hakuna arifa mpya"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Umewasha mipangilio ya kutuliza arifa"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Arifa na sauti hupunguzwa kiotomatiki kwenye kifaa chako kwa hadi dakika 2 unapopokea arifa nyingi kwa wakati mmoja."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Zima"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Fungua ili uone arifa za zamani"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Imerekebishwa"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ufuatilizi wa Kichwa"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"hali ya programu inayotoa milio ya simu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Badilisha ili uende kwenye programu iliyo kulia au chini unapotumia hali ya kugawa skrini"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Badilisha uende kwenye programu iliyo kushoto au juu unapotumia hali ya kugawa skrini"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ukigawanya skrini: badilisha kutoka programu moja hadi nyingine"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vifaa vya kuingiza data"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Badilisha utumie lugha inayofuata"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Badilisha utumie lugha iliyotangulia"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Programu Inayotumika Sasa"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Weka mapendeleo ya mikato ya kibodi"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hamna matokeo ya utafutaji"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Weka mapendeleo"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Nimemaliza"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Kusogeza kwa kutumia kibodi yako"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Jifunze kuhusu mikato ya kibodi"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Vinavyotolewa na programu"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Maonyesho"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Visivyojulikana"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Badilisha mipangilio ya vigae"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Ungependa kurejesha mipangilio chaguomsingi ya ukubwa na mpangilio wa vigae?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Ungependa kubadilisha vigae vyote?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vigae vyote vya Mipangilio ya Haraka vitabadilishwa kuwa katika mipangilio halisi ya kifaa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index 15de7f88694c..4de75caf05ae 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Vimezimwa"</item>
<item msgid="3028994095749238254">"Vimewashwa"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 2a27b47e54ca..4a53df9c2f29 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -24,7 +24,6 @@
<!-- margin from keyguard status bar to clock. For split shade it should be
keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
<dimen name="keyguard_clock_top_margin">8dp</dimen>
- <dimen name="keyguard_smartspace_top_offset">0dp</dimen>
<!-- QS-->
<dimen name="qs_panel_padding_top">16dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index f556b97eefc2..53d921b5e534 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -33,6 +33,9 @@
<!-- The number of columns in the infinite grid QuickSettings -->
<integer name="quick_settings_infinite_grid_num_columns">6</integer>
+ <!-- The maximum width of large tiles in the infinite grid QuickSettings -->
+ <integer name="quick_settings_infinite_grid_tile_max_width">3</integer>
+
<integer name="power_menu_lite_max_columns">2</integer>
<integer name="power_menu_lite_max_rows">3</integer>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 393631e3364b..26f32ef60851 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -126,6 +126,4 @@
<dimen name="controls_content_padding">24dp</dimen>
<dimen name="control_list_vertical_spacing">8dp</dimen>
<dimen name="control_list_horizontal_spacing">16dp</dimen>
- <!-- For portrait direction in unfold foldable device, we don't need keyguard_smartspace_top_offset-->
- <dimen name="keyguard_smartspace_top_offset">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b75a2186ed64..639bc394972a 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"உங்கள் திரையை ரெக்கார்டு செய்யவா?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ஓர் ஆப்ஸை ரெக்கார்டு செய்தல்"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"முழுத் திரையை ரெக்கார்டு செய்தல்"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"முழுத் திரையை ரெக்கார்டு செய்தல்: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"முழுத் திரையை நீங்கள் ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ஓர் ஆப்ஸை ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அல்லது பிளே செய்யப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"திரையை ரெக்கார்டு செய்"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"இப்போது நீங்கள் <xliff:g id="APP_NAME">%1$s</xliff:g> ஐ ரெக்கார்டு செய்கிறீர்கள்"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ரெக்கார்டிங்கை நிறுத்து"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"திரையைப் பகிர்கிறது"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"உள்ளடக்கம் பகிரப்படுகிறது"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"திரையைப் பகிர்வதை நிறுத்தவா?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"பகிர்வதை நிறுத்தவா?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"இப்போது உங்கள் முழுத்திரையையும் <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> உடன் பகிர்கிறீர்கள்"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"இப்போது உங்கள் முழுத்திரையையும் ஆப்ஸுடன் பகிர்கிறீர்கள்"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"இப்போது நீங்கள் <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ஐப் பகிர்கிறீர்கள்"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"இப்போது நீங்கள் ஓர் ஆப்ஸைப் பகிர்கிறீர்கள்"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"இப்போது நீங்கள் ஓர் ஆப்ஸுடன் பகிர்கிறீர்கள்"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"பகிர்வதை நிறுத்து"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"திரையை அலைபரப்புகிறது"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"அலைபரப்பை நிறுத்தவா?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"உள்ளீடு"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"செவித்துணைக் கருவி"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ஆன் செய்கிறது…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"உடனடி வசனம்"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"டேப்லெட் பூட்டப்பட்டிருந்தாலும் பூட்டுத் திரையில் விட்ஜெட்டை எவரும் பார்க்கலாம்."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"விட்ஜெட்டைத் தேர்வுநீக்கும்"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"உயரத்தைக் குறைக்கும்"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"உயரத்தை அதிகரிக்கும்"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"எல்லாவற்றையும் அழி"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"நிர்வகி"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"இதுவரை வந்த அறிவிப்புகள்"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"அறிவிப்பு அமைப்புகள்"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"இதுவரையான அறிவிப்புகள்"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"புதிது"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"சைலன்ட்"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"அறிவிப்புகள்"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"அறிவிப்புகள் இல்லை"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"புதிய அறிவிப்புகள் இல்லை"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"\'குறைந்த ஒலியளவில் அறிவிப்புகள்\' இயக்கப்பட்டுள்ளது"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"குறைந்த ஒலியளவில் அறிவிப்புகள் இப்போது இயக்கப்பட்டுள்ளது"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ஒரே நேரம் பல அறிவிப்புகள் வரும்போது சாதன ஒலியளவும் விழிப்பூட்டலும் தானாக 2 நிமிடம் குறைக்கப்படும்."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"முடக்கு"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"பழைய அறிவிப்பைப் பார்க்க அன்லாக் செய்க"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"நிலையானது"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ஹெட் டிராக்கிங்"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"ரிங்கர் பயன்முறையை மாற்ற தட்டவும்"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ரிங்கர் பயன்முறை"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது வலது/கீழ் உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"திரைப் பிரிப்பின்போது: ஓர் ஆப்ஸுக்குப் பதிலாக மற்றொன்றை மாற்றுதல்"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"உள்ளீடு"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"அடுத்த மொழிக்கு மாற்றுதல்"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"முந்தைய மொழிக்கு மாற்றுதல்"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"தற்போதைய ஆப்ஸ்"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"மாற்றுத்திறன் வசதி"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"கீபோர்டு ஷார்ட்கட்களைப் பிரத்தியேகப்படுத்துதல்"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ஷார்ட்கட்களைத் தேடுக"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"தேடல் முடிவுகள் இல்லை"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"பிரத்தியேகப்படுத்தும்"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"முடிந்தது"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"கீபோர்டு ஷார்ட்கட்கள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"டச்பேடைப் பயன்படுத்திச் செல்லுதல்"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ஆப்ஸ் வழங்குபவை"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"டிஸ்ப்ளே"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"தெரியவில்லை"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"கட்டங்களை மீட்டமைத்தல்"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"கட்டங்களை அவற்றின் அசல் வரிசைக்கும் அளவுகளுக்கும் மீட்டமைக்கவா?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"அனைத்துக் கட்டங்களையும் மீட்டமைக்கவா?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"விரைவு அமைப்புகளின் கட்டங்கள் அனைத்தும் சாதனத்தின் அசல் அமைப்புகளுக்கு மீட்டமைக்கப்படும்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index a3b9538f891c..66cdeec01991 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="3028994095749238254">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d66c6560fd7c..2834196437d5 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"మీ స్క్రీన్‌ను రికార్డ్ చేయాలా?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ఒక యాప్‌ను రికార్డ్ చేయండి"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ఫుల్ స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"ఫుల్-స్క్రీన్‌ను రికార్డ్ చేయండి: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"మీ ఫుల్ స్క్రీన్‌ను మీరు రికార్డ్ చేసేటప్పుడు, మీ స్క్రీన్‌పై కనిపించేవన్నీ రికార్డ్ అవుతాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"మీరు యాప్‌ను రికార్డ్ చేసేటప్పుడు, సంబంధిత యాప్‌లో కనిపించేవన్నీ లేదా ప్లే అయ్యేవన్నీ రికార్డ్ అవుతాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"మీరు ప్రస్తుతం <xliff:g id="APP_NAME">%1$s</xliff:g>‌ను రికార్డ్ చేస్తున్నారు"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"రికార్డింగ్‌ను ఆపివేయండి"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"స్క్రీన్‌ను షేర్ చేస్తోంది"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"కంటెంట్‌ను షేర్ చేస్తోంది"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"స్క్రీన్‌ను షేర్ చేయడం ఆపివేయాలా?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"షేర్ చేయడాన్ని ఆపివేయాలా?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"మీరు ప్రస్తుతం మీ మొత్తం స్క్రీన్‌ను <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>‌తో షేర్ చేస్తున్నారు"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"మీరు ప్రస్తుతం మీ మొత్తం స్క్రీన్‌ను యాప్‌తో షేర్ చేస్తున్నారు"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"మీరు ప్రస్తుతం <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>‌ను షేర్ చేస్తున్నారు"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"మీరు ప్రస్తుతం యాప్‌ను షేర్ చేస్తున్నారు"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"మీరు ప్రస్తుతం యాప్‌తో షేర్ చేస్తున్నారు"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"షేర్ చేయడాన్ని ఆపివేయండి"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"స్క్రీన్‌ను ప్రసారం చేస్తోంది"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ప్రసారం చేయడం ఆపివేయాలా?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ఇన్‌పుట్"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"వినికిడి పరికరాలు"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ఆన్ చేస్తోంది…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్‌"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్‌"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్‌ను అప్‌డేట్ చేయడం సాధ్యపడలేదు"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"లైవ్ క్యాప్షన్"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్‌లను అన్‌బ్లాక్ చేయమంటారా?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"మీ టాబ్లెట్ లాక్ చేసి ఉన్నా, మీ లాక్ స్క్రీన్‌లో విడ్జెట్‌లను ఎవరైనా చూడవచ్చు."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"విడ్జెట్ ఎంపిక రద్దు చేయండి"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ఎత్తును తగ్గించండి"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"ఎత్తును పెంచండి"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్‌ను ఉపయోగించి యాప్‌ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్‌లు మీ లాక్ స్క్రీన్‌కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string>
@@ -595,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"నోటిఫికేషన్‌లు లేవు"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"కొత్త నోటిఫికేషన్‌లు ఏవీ లేవు"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"నోటిఫికేషన్ కూల్‌డౌన్ ఆన్ అయింది"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"నోటిఫికేషన్ కూల్‌డౌన్ ఇప్పుడు ఆన్ అయ్యింది"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ఒకేసారి పలు నోటిఫికేషన్లు వస్తే, పరికర వాల్యూమ్, అలర్ట్స్ ఆటోమేటిగ్గా 2 నిమిషాలకు తగ్గించబడతాయి."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ఆఫ్ చేయండి"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"పాత నోటిఫికేషన్‌ల కోసం అన్‌లాక్ చేయండి"</string>
@@ -703,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ఫిక్స్‌డ్"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"హెడ్ ట్రాకింగ్"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"రింగర్ మోడ్‌ను మార్చడానికి ట్యాప్ చేయండి"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"రింగర్ మోడ్"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్‌మ్యూట్ చేయి"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
@@ -872,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా కింద యాప్‌నకు మారండి"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్‌నకు మారండి"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"స్ప్లిట్ స్క్రీన్ సమయంలో: ఒక దాన్నుండి మరో దానికి యాప్ రీప్లేస్ చేయండి"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ఇన్‌పుట్"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"తర్వాత భాషకు స్విచ్ అవ్వండి"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"మునుపటి భాషకు స్విచ్ అవ్వండి"</string>
@@ -1413,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"ప్రస్తుత యాప్"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"యాక్సెసిబిలిటీ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్‌కట్‌లు"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"కీబోర్డ్ షార్ట్‌కట్‌లను అనుకూలంగా మార్చండి"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్‌కట్‌లు"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"షార్ట్‌కట్‌లను వెతకండి"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"సెర్చ్ ఫలితాలు ఏవీ లేవు"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"అనుకూలంగా మార్చండి"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"పూర్తయింది"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"కీబోర్డ్ సెట్టింగ్‌లు"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"మీ కీబోర్డ్ ఉపయోగించి నావిగేట్ చేయండి"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"కీబోర్డ్ షార్ట్‌కట్‌ల గురించి తెలుసుకోండి"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"మీ టచ్‌ప్యాడ్‌ని ఉపయోగించి నావిగేట్ చేయండి"</string>
@@ -1458,7 +1470,7 @@
<string name="home_controls_dream_description" msgid="4644150952104035789">"హోమ్ కంట్రోల్స్‌ను స్క్రీన్ సేవర్‌గా చేసి వేగంగా యాక్సెస్ పొందండి"</string>
<string name="volume_undo_action" msgid="5815519725211877114">"చర్య రద్దు చేయండి"</string>
<string name="back_edu_toast_content" msgid="4530314597378982956">"వెనుకకు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి ఎడమ లేదా కుడి వైపునకు స్వైప్ చేయండి"</string>
- <string name="home_edu_toast_content" msgid="3381071147871955415">"హోమ్‌కు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో మీ మూడు వెళ్లతో పైకి స్వైప్ చేయండి"</string>
+ <string name="home_edu_toast_content" msgid="3381071147871955415">"హోమ్‌కు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో మీ మూడు వేళ్లతో పైకి స్వైప్ చేయండి"</string>
<string name="overview_edu_toast_content" msgid="5797030644017804518">"ఇటీవలి యాప్‌లను చూడటానికి, టచ్‌ప్యాడ్‌లో మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
<string name="all_apps_edu_toast_content" msgid="8807496014667211562">"మీ యాప్‌లన్నింటినీ చూడటానికి, మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
<string name="redacted_notification_single_line_title" msgid="212019960919261670">"దాచిపెట్టినది"</string>
@@ -1483,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"యాప్‌ల ద్వారా అందించబడినవి"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"డిస్‌ప్లే"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"తెలియదు"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"టైల్స్‌ను రీసెట్ చేయండి"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"టైల్స్‌ను వాటి ఒరిజినల్ క్రమానికి, సైజ్‌లకు రీసెట్ చేయాలా?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"టైల్స్ అన్ని రీసెట్ చేయాలా?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"అన్ని క్విక్ సెట్టింగ్‌ల టైల్స్, పరికరం తాలూకు ఒరిజినల్ సెట్టింగ్‌లకు రీసెట్ చేయబడతాయి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 6584cdd59282..42ee13d8cc57 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ఆఫ్‌లో ఉంది"</item>
<item msgid="3028994095749238254">"ఆన్‌లో ఉంది"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index b7d1a32fdeba..fcfc4e2364e0 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"บันทึกหน้าจอไหม"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"บันทึกแอปเดียว"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"บันทึกทั้งหน้าจอ"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"บันทึกทั้งหน้าจอ: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ขณะบันทึกทั้งหน้าจอ ระบบจะบันทึกทุกสิ่งที่แสดงอยู่บนหน้าจอ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ขณะบันทึกแอป ระบบจะบันทึกทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"บันทึกหน้าจอ"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"คุณกำลังบันทึก <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"หยุดบันทึก"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"กำลังแชร์หน้าจอ"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"กำลังแชร์เนื้อหา"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"หยุดแชร์หน้าจอไหม"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"หยุดการแชร์ใช่ไหม"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"คุณกำลังแชร์ทั้งหน้าจอกับ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"คุณกำลังแชร์ทั้งหน้าจอกับแอป"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"คุณกำลังแชร์ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"คุณกำลังแชร์แอป"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"คุณกำลังแชร์กับแอป"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"หยุดแชร์"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"กำลังแคสต์หน้าจอ"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"หยุดแคสต์ไหม"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"อินพุต"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"เครื่องช่วยฟัง"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"กำลังเปิด..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"หมุนอัตโนมัติ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"หมุนหน้าจออัตโนมัติ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"คำบรรยายสด"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"วิดเจ็ตในหน้าจอล็อก"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"ทุกคนจะดูวิดเจ็ตที่อยู่ในหน้าจอล็อกของคุณได้ แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ยกเลิกการเลือกวิดเจ็ต"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"ลดความสูง"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"เพิ่มความสูง"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"วิดเจ็ตในหน้าจอล็อก"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ล้างทั้งหมด"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"การตั้งค่าการแจ้งเตือน"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"ประวัติการแจ้งเตือน"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ใหม่"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ปิดเสียง"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ไม่มีการแจ้งเตือน"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ไม่มีการแจ้งเตือนใหม่"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"การพักการแจ้งเตือนเปิดอยู่"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"การพักการแจ้งเตือนเปิดอยู่"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ระบบจะลดระดับเสียงและจำนวนการแจ้งเตือนของอุปกรณ์โดยอัตโนมัติสูงสุด 2 นาทีเมื่อคุณได้รับการแจ้งเตือนพร้อมกันมากเกินไป"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ปิด"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ปลดล็อกเพื่อดูการแจ้งเตือนเก่า"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"คงที่"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"การติดตามการเคลื่อนไหวของศีรษะ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"แตะเพื่อเปลี่ยนโหมดเสียงเรียกเข้า"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"โหมดเสียงเรียกเข้า"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"เปลี่ยนไปใช้แอปทางด้านขวาหรือด้านล่างขณะใช้โหมดแยกหน้าจอ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"เปลี่ยนไปใช้แอปทางด้านซ้ายหรือด้านบนขณะใช้โหมดแยกหน้าจอ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ระหว่างใช้โหมดแยกหน้าจอ: เปลี่ยนแอปหนึ่งเป็นอีกแอปหนึ่ง"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"อินพุต"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"เปลี่ยนเป็นภาษาถัดไป"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"เปลี่ยนเป็นภาษาก่อนหน้า"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"แอปปัจจุบัน"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"การช่วยเหลือพิเศษ"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"ปรับแต่งแป้นพิมพ์ลัด"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ไม่พบผลการค้นหา"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ปรับแต่ง"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"เสร็จสิ้น"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"การตั้งค่าแป้นพิมพ์"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ไปยังส่วนต่างๆ โดยใช้ทัชแพด"</string>
@@ -1441,11 +1451,11 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาบนทัชแพด"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ดีมาก"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับสำเร็จแล้ว"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ไปที่หน้าแรก"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"เก่งมาก"</string>
- <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string>
+ <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกสำเร็จแล้ว"</string>
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ดูแอปล่าสุด"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"เยี่ยมมาก"</string>
@@ -1453,7 +1463,7 @@
<string name="tutorial_action_key_title" msgid="8172535792469008169">"ดูแอปทั้งหมด"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดเสร็จแล้ว"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดสำเร็จแล้ว"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
@@ -1469,9 +1479,9 @@
<string name="back_edu_notification_title" msgid="5624780717751357278">"ใช้ทัชแพดเพื่อย้อนกลับ"</string>
<string name="back_edu_notification_content" msgid="2497557451540954068">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวา แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
<string name="home_edu_notification_title" msgid="6097902076909654045">"ใช้ทัชแพดเพื่อไปยังหน้าแรก"</string>
- <string name="home_edu_notification_content" msgid="6631697734535766588">"ใช้ 3 นิ้วปัดขึ้น แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
+ <string name="home_edu_notification_content" msgid="6631697734535766588">"ใช้ 3 นิ้วปัดขึ้น แตะเพื่อดูท่าทางสัมผัสต่างๆ เพิ่มเติม"</string>
<string name="overview_edu_notification_title" msgid="1265824157319562406">"ใช้ทัชแพดเพื่อดูแอปล่าสุด"</string>
- <string name="overview_edu_notification_content" msgid="3578204677648432500">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
+ <string name="overview_edu_notification_content" msgid="3578204677648432500">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้ แตะเพื่อดูท่าทางสัมผัสต่างๆ เพิ่มเติม"</string>
<string name="all_apps_edu_notification_title" msgid="372262997265569063">"ใช้แป้นพิมพ์เพื่อดูแอปทั้งหมด"</string>
<string name="all_apps_edu_notification_content" msgid="3255070575694025585">"กดปุ่มดำเนินการได้ทุกเมื่อ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
<string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ตอนนี้การหรี่แสงเพิ่มเติมเป็นส่วนหนึ่งของแถบเลื่อนความสว่างแล้ว"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ให้บริการโดยแอป"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"จอแสดงผล"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ไม่ทราบ"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"รีเซ็ตการ์ด"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"รีเซ็ตการ์ดเป็นลำดับและขนาดเดิมไหม"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"รีเซ็ตการ์ดทั้งหมดใช่ไหม"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"การ์ดการตั้งค่าด่วนทั้งหมดจะรีเซ็ตเป็นการตั้งค่าเดิมของอุปกรณ์"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 8b7187b66baa..d249057be4da 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"ปิด"</item>
<item msgid="3028994095749238254">"เปิด"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index a30a2f2f45f6..68cc6d2fb872 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"I-record ang iyong screen?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Mag-record ng isang app"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"I-record ang buong screen"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"I-record ang buong screen: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kapag nire-record mo ang iyong buong screen, nire-record ang anumang ipinapakita sa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kapag nagre-record ka ng app, nire-record ang anumang ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"I-record ang screen"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Kasalukuyan mong nire-record ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Huminto sa pag-record"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ibinabahagi ang screen"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Pagbabahagi ng content"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ihinto ang pagbabahagi ng screen?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Itigil ang pagbabahagi?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Kasalukuyan mong ibinabahagi ang iyong buong screen sa <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Kasalukuyan mong ibinabahagi ang iyong buong screen sa isang app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kasalukuyan kang nagbabahagi ng <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Kasalukuyan kang nagbabahagi ng app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Kasalukuyan kang nagbabahagi sa isang app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ihinto ang pagbabahagi"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Kina-cast ang screen"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ihinto ang pag-cast?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Mga hearing aid"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ino-on…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Instant Caption"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Mga widget ng lock screen"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Makikita ng sinuman ang mga widget sa lock screen, kahit naka-lock ang tablet."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"i-unselect ang widget"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Bawasan ang taas"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Dagdagan ang taas"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Mga widget ng lock screen"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"I-clear lahat"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Pamahalaan"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Mga setting ng notification"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"History ng notification"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Bago"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Naka-silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Mga Notification"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Walang mga notification"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Walang bagong notification"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Naka-on ang cooldown sa notification"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Naka-on na ang cooldown sa notification"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Babawasan ang volume at alerto nang hanggang 2 minuto kapag nakatanggap ng maraming notification."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"I-off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"I-unlock para makita ang mga mas lumang notification"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Nakapirmi"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pag-track ng Ulo"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"I-tap para baguhin ang ringer mode"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Lumipat sa app sa kanan o ibaba habang ginagamit ang split screen"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Lumipat sa app sa kaliwa o itaas habang ginagamit ang split screen"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Habang nasa split screen: magpalit-palit ng app"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Lumipat sa susunod na wika"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Lumipat sa dating wika"</string>
@@ -886,7 +885,7 @@
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Email"</string>
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Music"</string>
- <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendaryo"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
<string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string>
<string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mga mapa"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"Huwag Istorbohin"</string>
@@ -1408,26 +1407,37 @@
<string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Mga kontrol ng system"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Mga system app"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</string>
- <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Kamakailang mga app"</string>
+ <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Mga kamakailang app"</string>
<string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
<string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
<string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Kasalukuyang App"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"I-customize ang mga keyboard shortcut"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Walang resulta ng paghahanap"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"I-customize"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tapos na"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mga Setting ng Keyboard"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Mag-navigate gamit ang iyong keyboard"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Matuto ng mga keyboard shortcut"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Mag-navigate gamit ang iyong touchpad"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Ibinibigay ng mga app"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Hindi Alam"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"I-reset ang mga tile"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"I-reset ang mga tile sa orihinal na pagkakasunod-sunod at mga laki ng mga ito?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"I-reset ang lahat ng tile?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Magre-reset sa mga orihinal na setting ng device ang lahat ng tile ng Mga Mabilisang Setting"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index fe2827f6a4e9..0e43fafd1a04 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Naka-off"</item>
<item msgid="3028994095749238254">"Naka-on"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c79dfcf8f162..fd4bb451f024 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekranınız kaydedilsin mi?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir uygulamayı kaydet"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tüm ekranı kaydedin"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Tüm ekranı kaydet: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Tüm ekranınızı kaydettiğinizde ekranınızda gösterilen her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Bir uygulamayı kaydettiğinizde o uygulamada gösterilen veya oynatılan her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı kaydet"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Şu anda <xliff:g id="APP_NAME">%1$s</xliff:g> içeriğini kaydediyorsunuz"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Kaydı durdur"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran paylaşılıyor"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"İçerik paylaşma"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran paylaşımı durdurulsun mu?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Paylaşım durdurulsun mu?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Şu anda ekranınızın tamamını <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ile paylaşıyorsunuz"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Şu anda ekranınızın tamamını bir uygulamayla paylaşıyorsunuz"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Şu anda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> içeriğini paylaşıyorsunuz"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Şu anda bir uygulamayı paylaşıyorsunuz"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Şu anda bir uygulamayla paylaşıyorsunuz"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Paylaşımı durdur"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran yayınlanıyor"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Yayın durdurulsun mu?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"İşitme cihazları"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Açılıyor…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonunun engellemesi kaldırılsın mı?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerasının engellemesi kaldırılsın mı?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası ile mikrofonunun engellemesi kaldırılsın mı?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Kilit ekranı widget\'ları"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Kilit ekranınızdaki widget\'lar, tabletiniz kilitliyken bile herkes tarafından görüntülenebilir."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget\'ın seçimini kaldırın"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Yüksekliği azalt"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Yüksekliği artır"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilit ekranı widget\'ları"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tümünü temizle"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Yönet"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geçmiş"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Bildirim ayarları"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Bildirim geçmişi"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Yeni"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Sessiz"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirimler"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Şimdi başlat"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Bildirim yok"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Yeni bildirim yok"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildirim şiddetini düşürme etkin"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Aynı anda çok sayıda bildirim aldığınızda 2 dakika boyunca otomatik olarak cihazınızın sesi kısılır ve uyarıları azaltılır."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Kapat"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eski bildirimler için kilidi açın"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Sabit"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş Takibi"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Telefon zili modunu değiştirmek için dokunun"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"telefon zili modu"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi kapat"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string>
@@ -868,12 +866,14 @@
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilit ekranı"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"Not al"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"Çoklu görev"</string>
- <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sağdaki mevcut uygulamayla birlikte bölünmüş ekranı kullanın"</string>
- <string name="system_multitasking_lhs" msgid="8402954791206308783">"Soldaki mevcut uygulamayla birlikte bölünmüş ekranı kullanın"</string>
+ <string name="system_multitasking_rhs" msgid="8714224917276297810">"Sağdaki mevcut uygulamayla birlikte bölünmüş ekranı kullan"</string>
+ <string name="system_multitasking_lhs" msgid="8402954791206308783">"Soldaki mevcut uygulamayla birlikte bölünmüş ekranı kullan"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Bölünmüş ekrandan tam ekrana geç"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran kullanırken sağdaki veya alttaki uygulamaya geçiş yapın"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran kullanırken sağdaki veya alttaki uygulamaya geçiş yap"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran kullanırken soldaki veya üstteki uygulamaya geçiş yapın"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran etkinken: Bir uygulamayı başkasıyla değiştir"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Giriş"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Sonraki dile geç"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Önceki dile geç"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Mevcut Uygulama"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Klavye kısayollarını özelleştirin"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Arama sonucu yok"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Özelleştir"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Bitti"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klavyenizi kullanarak gezinin"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klavye kısayollarını öğrenin"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Dokunmatik alanınızı kullanarak gezinin"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Uygulamalar tarafından sağlanır"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Bilinmiyor"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Kartları sıfırla"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Kartlar orijinal sıralarına ve boyutlarına sıfırlansın mı?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tüm ayar kutuları sıfırlansın mı?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tüm Hızlı Ayarlar kutuları cihazın özgün ayarlarına sıfırlanır"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 1ed106f4efd2..1e30c6d3a5c1 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Kapalı"</item>
<item msgid="3028994095749238254">"Açık"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 2808922e166c..ab5f8a42697a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Записати відео з екрана?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записувати один додаток"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записувати весь екран"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Записувати весь вміст екрана: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Коли ви записуєте вміст усього екрана, на відео потрапляє все, що на ньому відображається. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Коли ви записуєте додаток, на відео потрапляє все, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записувати вміст екрана"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ви зараз записуєте вміст екрана додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Зупинити запис"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Показ екрана"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Ви ділитеся контентом"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Зупинити показ екрана?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Більше не ділитися?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ви зараз показуєте вміст усього екрана в додатку <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ви зараз показуєте вміст усього екрана в додатку."</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Ви зараз показуєте вікно додатка <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Ви зараз показуєте вікно додатка"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Ви зараз ділитеся контентом із додатком"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Зупинити показ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Трансляція екрана"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Зупинити трансляцію?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Джерело сигналу"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухові апарати"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Увімкнення…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автообертання"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Живі субтитри"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Віджети для заблокованого екрана"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Будь-хто бачитиме віджети навіть на заблокованому екрані планшета."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"скасувати вибір віджета"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Зменшити висоту"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Збільшити висоту"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджети для заблокованого екрана"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистити все"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Керувати"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Історія"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Налаштування сповіщень"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Історія сповіщень"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Нові"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Без звуку"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Сповіщення"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Почати зараз"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Сповіщень немає"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Немає нових сповіщень"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Зниження гучності сповіщень увімкнено"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Коли ви отримуєте забагато сповіщень за раз, пристрій автоматично знижує їх гучність і кількість на період до 2 хвилин."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Вимкнути"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Розблокуйте, щоб переглянути старіші"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Завжди ввімкнено"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Відстеження рухів голови"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Торкніться, щоб змінити режим дзвінка"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"режим дзвінка"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string>
@@ -860,8 +858,8 @@
<string name="group_system_go_back" msgid="2730322046244918816">"Назад"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Перейти на головний екран"</string>
<string name="group_system_overview_open_apps" msgid="5659958952937994104">"Переглянути нещодавні додатки"</string>
- <string name="group_system_cycle_forward" msgid="5478663965957647805">"Перемикатися між нещодавніми додатками вперед"</string>
- <string name="group_system_cycle_back" msgid="8194102916946802902">"Перемикатися між нещодавніми додатками назад"</string>
+ <string name="group_system_cycle_forward" msgid="5478663965957647805">"Прокрутити вперед список нещодавніх додатків"</string>
+ <string name="group_system_cycle_back" msgid="8194102916946802902">"Прокрутити назад список нещодавніх додатків"</string>
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Відкрити список додатків"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Відкрити налаштування"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Відкрити додаток Асистент"</string>
@@ -871,9 +869,11 @@
<string name="system_multitasking_rhs" msgid="8714224917276297810">"Розділити екран і показувати поточний додаток праворуч"</string>
<string name="system_multitasking_lhs" msgid="8402954791206308783">"Розділити екран і показувати поточний додаток ліворуч"</string>
<string name="system_multitasking_full_screen" msgid="336048080383640562">"Перейти з розділення екрана на весь екран"</string>
- <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Під час розділення екрана перемикатися на додаток праворуч або внизу"</string>
+ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти до додатка праворуч або внизу на розділеному екрані"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Під час розділення екрана перемикатися на додаток ліворуч або вгорі"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Під час розділення екрана: замінити додаток іншим"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Метод введення"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Вибрати наступну мову"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Вибрати попередню мову"</string>
@@ -1415,25 +1415,36 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Поточний додаток"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Доступність"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Налаштуйте комбінації клавіш"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нічого не знайдено"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Налаштувати"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Дізнайтеся більше про комбінації клавіш"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string>
- <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Жести для сенсорної панелі: докладніше"</string>
+ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Дізнатися про жести на сенсорній панелі"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігація за допомогою клавіатури й сенсорної панелі"</string>
- <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Жести для сенсорної панелі, комбінації клавіш тощо: докладніше"</string>
+ <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Дізнатися про жести на сенсорній панелі, комбінації клавіш і багато іншого"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
<string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Перейти на головний екран"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Переглянути нещодавні додатки"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Надано додатками"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невідомо"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Скинути панелі"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Відновити початковий порядок і розмір панелей?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скинути всі панелі?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усі панелі швидких налаштувань буде скинуто до стандартних налаштувань пристрою"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 61e62e4395ce..6c03aea7b3af 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Вимкнено"</item>
<item msgid="3028994095749238254">"Увімкнено"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b72464cad145..39aa5d1ccfcc 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"آپ کی اسکرین ریکارڈ کریں؟"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ایک ایپ ریکارڈ کریں"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"پوری اسکرین کو ریکارڈ کریں"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"‏پوری اسکرین ریکارڈ کریں: ‎%s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"جب آپ اپنی پوری اسکرین کو ریکارڈ کر رہے ہوتے ہیں تو آپ کی اسکرین پر دکھائی گئی ہر چیز ریکارڈ کی جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"جب آپ کسی ایپ کو ریکارڈ کر رہے ہوتے ہیں تو اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز ریکارڈ کی جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"اسکرین ریکارڈ کریں"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"آپ فی الحال <xliff:g id="APP_NAME">%1$s</xliff:g> ریکارڈ کر رہے ہیں"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ریکارڈنگ روکیں"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"اسکرین کا اشتراک ہو رہا ہے"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"مواد کا اشتراک کیا جا رہا ہے"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"اسکرین کا اشتراک روکیں؟"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"اشتراک کرنا روکیں؟"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"آپ فی الحال <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> کے ساتھ اپنی پوری اسکرین کا اشتراک کر رہے ہیں"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"آپ فی الحال ایک ایپ کے ساتھ اپنی پوری اسکرین کا اشتراک کر رہے ہیں"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"آپ فی الحال <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> کا اشتراک کر رہے ہیں"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"آپ فی الحال ایک ایپ کا اشتراک کر رہے ہیں"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"آپ فی الحال ایک ایپ کے ساتھ اشتراک کر رہے ہیں"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"اشتراک کرنا روکیں"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"اسکرین کاسٹ ہو رہی ہے"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"کاسٹ کرنا بند کریں؟"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ان پٹ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعتی آلات"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"آن ہو رہا ہے…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"لائیو کیپشن"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"مقفل اسکرین کے ویجیٹس"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"کوئی بھی آپ کی مقفل اسکرین پر ویجیٹ دیکھ سکتا ہے اگرچہ آپ کا ٹیبلیٹ مقفل ہو۔"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ویجیٹ غیر منتخب کریں"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"اونچائی کم کریں"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"اونچائی بڑھائیں"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"مقفل اسکرین کے ویجیٹس"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"سبھی کو صاف کریں"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"نظم کریں"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"سرگزشت"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"اطلاع کی ترتیبات"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"اطلاع کی سرگزشت"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"نیا"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"خاموش"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اطلاعات"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ابھی شروع کریں"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"کوئی اطلاعات نہیں ہیں"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"کوئی نئی اطلاعات نہیں"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ںوٹیفیکیشن کول ڈاؤن آن ہے"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"جب آپ کو ایک ساتھ بہت زیادہ اطلاعات موصول ہوتی ہیں تو آپ کے آلے کا والیوم اور الرٹس خودکار طور پر 2 منٹ تک کم ہو جاتے ہیں۔"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"آف کریں"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"پرانی اطلاعات دیکھنے کیلئے غیر مقفل کریں"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"مقرر"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"سر کی ٹریکنگ"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"رنگر وضع تبدیل کرنے کیلئے تھپتھپائیں"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"رنگر موڈ"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string>
@@ -865,7 +863,7 @@
<string name="group_system_access_all_apps_search" msgid="1553588630154197469">"ایپس کی فہرست کھولیں"</string>
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ترتیبات کھولیں"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"اسسٹنٹ کھولیں"</string>
- <string name="group_system_lock_screen" msgid="7391191300363416543">"مقفل اسکرین"</string>
+ <string name="group_system_lock_screen" msgid="7391191300363416543">"اسکرین لاک کریں"</string>
<string name="group_system_quick_memo" msgid="3764560265935722903">"نوٹ لیں"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="6967816258924795558">"ملٹی ٹاسکنگ"</string>
<string name="system_multitasking_rhs" msgid="8714224917276297810">"دائیں جانب موجودہ ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"اسپلٹ اسکرین کا استعمال کرتے ہوئے دائیں یا نیچے ایپ پر سوئچ کریں"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"اسپلٹ اسکرین کا استعمال کرتے ہوئے بائیں یا اوپر ایپ پر سوئچ کریں"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"اسپلٹ اسکرین کے دوران: ایک ایپ کو دوسرے سے تبدیل کریں"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ان پٹ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"اگلی زبان پر سوئچ کریں"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"پچھلی زبان پر سوئچ کریں"</string>
@@ -1415,27 +1415,38 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"موجودہ ایپ"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ایکسیسبیلٹی"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"کی بورڈ شارٹ کٹس کو حسب ضرورت بنائیں"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"تلاش کا کوئی نتیجہ نہیں ہے"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"حسب ضرورت بنائیں"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ہو گیا"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"کی بورڈ کی ترتیبات"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"اپنے کی بورڈ کا استعمال کر کے نیویگیٹ کریں"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"کی بورڈ شارٹ کٹس جانیں"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"اپنے ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
- <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ٹچ پیڈ کے اشارے کو جانیں"</string>
+ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ٹچ پیڈ کے اشاروں کو جانیں"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"اپنے کی بورڈ اور ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ٹچ پیڈ کے اشارے، کی بورڈ شارٹ کٹس اور مزید جانیں"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"واپس جائیں"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"گھر جائیں"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ہوم پر جائیں"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"حالیہ ایپس دیکھیں"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
@@ -1449,11 +1460,11 @@
<string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"حالیہ ایپس دیکھیں"</string>
<string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے اوپر کی طرف سوائپ کریں اور دبائے رکھیں"</string>
<string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"بہترین!"</string>
- <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس کا اشارہ مکمل کر لیا ہے۔"</string>
+ <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس دیکھیں کا اشارہ مکمل کر لیا ہے۔"</string>
<string name="tutorial_action_key_title" msgid="8172535792469008169">"سبھی ایپس دیکھیں"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس کا اشارہ مکمل کر لیا ہے"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس دیکھیں کا اشارہ مکمل کر لیا ہے"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"‏%2$d میں سے ‎%1$d کا لیول"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ایپس کے ذریعہ فراہم کردہ"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ڈسپلے"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامعلوم"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"ٹائلز ری سیٹ کریں"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"ٹائلز کو ان کے اصل آرڈر اور سائزز پر ری سیٹ کریں؟"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"سبھی ٹائلز ری سیٹ کریں؟"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"سبھی فوری ترتیبات کی ٹائلز آلہ کی اصل ترتیبات پر ری سیٹ ہو جائیں گی"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index ebbc30ebca58..a213f00e496d 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"آف ہے"</item>
<item msgid="3028994095749238254">"آن ہے"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 7f6275c5a1d2..67cc8b638a92 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran yozib olinsinmi?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bitta ilovani yozib olish"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Butun ekranni yozib olish"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Butun ekranni yozib olish: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Butun ekranni yozib olishda ekranda koʻrsatilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ilovani yozib olishda ilova koʻrsatilgan yoki ijro etilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranni yozib olish"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Hozir <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi yozib olinmoqda"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Yozuvni toʻxtatish"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran ulashilmoqda"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Kontent ulashilmoqda"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran namoyishi toʻxtatilsinmi?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Ulashuv tugatilsinmi?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Hozir butun ekran <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ilovasiga ulashilmoqda"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Hozir butun ekran ilovaga ulashilmoqda"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Hozir <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ilovasiga kontent ulashilmoqda"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Hozir ilovaga kontent ulashilmoqda"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Hozir ilova bilan ulashilmoqda"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Namoyishni toʻxtatish"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran translatsiya qilinmoqda"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Toʻxtatilsinmi?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Kirish"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eshitish moslamalari"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Yoqilmoqda…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Jonli izoh"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Qurilma mikrofoni blokdan chiqarilsinmi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Qurilma kamerasi blokdan chiqarilsinmi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Qurilma kamerasi va mikrofoni blokdan chiqarilsinmi?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Ekran qulfi vidjetlari"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Ekran quflidagi vidjetlar hammaga koʻrinadi, hatto planshet qulflanganda ham."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"vidjetni bekor qilish"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Balandligini kichraytirish"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Balandligini oshirish"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Ekran qulfi vidjetlari"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Tarix"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Bildirishnoma sozlamalari"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Bildirishnomalar tarixi"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Yangi"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Sokin"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Yangi bildirishoma yoʻq"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildirishnomalar ovozini pasaytirish yoniq"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Bildirishnomalarni sekinlatish yoqildi"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Bir vaqtda juda koʻp bildirishnoma olsangiz, qurilmangiz tovushi va ogohlantirishlar 2 daqiqagacha avtomatik pasaytiriladi."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Faolsizlantirish"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eskilarini koʻrish uchun qulfni yeching"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Statik"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Boshni kuzatish"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Jiringlagich rejimini oʻzgartirish uchun bosing"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"jiringlagich rejimi"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ovozsiz qilish"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ovozni yoqish"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ajratilgan ekranda oʻngdagi yoki pastdagi ilovaga almashish"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ajratilgan ekranda chapdagi yoki yuqoridagi ilovaga almashish"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ajratilgan rejimda ilovalarni oʻzaro almashtirish"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Kiritish"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Keyingi tilga almashtirish"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Avvalgi tilga almashtirish"</string>
@@ -1083,7 +1082,7 @@
<string name="accessibility_magnification_zoom" msgid="4222088982642063979">"Masshtab"</string>
<string name="accessibility_magnification_medium" msgid="6994632616884562625">"Oʻrtacha"</string>
<string name="accessibility_magnification_small" msgid="8144502090651099970">"Kichik"</string>
- <string name="accessibility_magnification_large" msgid="6602944330021308774">"Yirik"</string>
+ <string name="accessibility_magnification_large" msgid="6602944330021308774">"Katta"</string>
<string name="accessibility_magnification_fullscreen" msgid="5043514702759201964">"Butun ekran"</string>
<string name="accessibility_magnification_done" msgid="263349129937348512">"Tayyor"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Tahrirlash"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Joriy ilova"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tezkor tugmalarni moslash"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hech narsa topilmadi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Moslash"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tayyor"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatura sozlamalari"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviatura yordamida kezing"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tezkor tugmalar haqida"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Sensorli panel yordamida kezing"</string>
@@ -1435,14 +1445,14 @@
<string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura va sensorli panel yordamida kezing"</string>
<string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string>
<string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Orqaga"</string>
- <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga qaytish"</string>
<string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string>
<string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string>
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Yaxshi!"</string>
<string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ortga qaytish ishorasi darsini tamomladingiz."</string>
- <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Boshiga"</string>
+ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Boshiga qaytish"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Sensorli panelda uchta barmoq bilan tepaga suring"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Barakalla!"</string>
<string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Bosh ekranni ochish ishorasi darsini tamomladingiz"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Ilovalarga tegishli"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Noaniq"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Katakchalarni asliga qaytarish"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Katakchalar asl tartibi va oʻlchamiga qaytarilsinmi?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Barcha katakchalar asliga qaytarilsinmi?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Barcha Tezkor sozlamalar katakchalari qurilmaning asl sozlamalariga qaytariladi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index 2ae811233176..5e6611c3c1d5 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Oʻchiq"</item>
<item msgid="3028994095749238254">"Yoniq"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 388ebb804828..6752ceb27e97 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ghi màn hình?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ghi một ứng dụng"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ghi toàn màn hình"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Ghi toàn bộ màn hình: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Khi bạn ghi toàn màn hình, mọi nội dung trên màn hình của bạn đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Khi bạn ghi một ứng dụng, mọi nội dung xuất hiện hoặc phát trong ứng dụng đó sẽ đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ghi màn hình"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Bạn đang ghi lại nội dung của <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Dừng ghi"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Đang chia sẻ màn hình"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Chia sẻ nội dung"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Dừng chia sẻ màn hình?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Dừng chia sẻ?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Bạn đang chia sẻ toàn bộ nội dung trên màn hình với <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Bạn đang chia sẻ toàn bộ nội dung trên màn hình với một ứng dụng"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Bạn đang chia sẻ nội dung của <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Bạn đang chia sẻ một ứng dụng"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Bạn đang chia sẻ với một ứng dụng"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dừng chia sẻ"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Đang truyền màn hình"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Dừng truyền?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Thiết bị đầu vào"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Thiết bị trợ thính"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Đang bật…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Chế độ đặt sẵn"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Phụ đề trực tiếp"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Bỏ chặn micrô của thiết bị?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Bỏ chặn camera của thiết bị?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Bỏ chặn máy ảnh và micrô của thiết bị?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Tiện ích trên màn hình khoá"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Ai cũng thấy được tiện ích trên màn hình khoá, kể cả khi khoá máy tính bảng."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"bỏ chọn tiện ích"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Giảm chiều cao"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Tăng chiều cao"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Tiện ích trên màn hình khoá"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string>
@@ -595,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Bắt đầu ngay"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Không có thông báo nào"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Không có thông báo mới"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Chế độ Giảm dần âm lượng thông báo đang bật"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Khi bạn nhận quá nhiều thông báo cùng lúc, âm lượng và cảnh báo tự động giảm trong tối đa 2 phút."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Tắt"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Mở khoá để xem thông báo cũ"</string>
@@ -703,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Cố định"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Theo dõi chuyển động của đầu"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Nhấn để thay đổi chế độ chuông"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"chế độ chuông"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string>
@@ -872,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Chuyển sang ứng dụng bên phải hoặc ở dưới khi đang chia đôi màn hình"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Chuyển sang ứng dụng bên trái hoặc ở trên khi đang chia đôi màn hình"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Trong chế độ chia đôi màn hình: thay một ứng dụng bằng ứng dụng khác"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Đầu vào"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Chuyển sang ngôn ngữ tiếp theo"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Chuyển về ngôn ngữ trước"</string>
@@ -1403,7 +1405,7 @@
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) đang dùng"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) đã dùng gần đây"</string>
<string name="shortcut_helper_category_system" msgid="462110876978937359">"Hệ thống"</string>
- <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Cài đặt hệ thống"</string>
+ <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"Điều khiển hệ thống"</string>
<string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"Ứng dụng hệ thống"</string>
<string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string>
<string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"Ứng dụng gần đây"</string>
@@ -1413,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Ứng dụng hiện tại"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tuỳ chỉnh phím tắt"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
- <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string>
+ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm lối tắt"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Không có kết quả tìm kiếm nào"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tuỳ chỉnh"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Xong"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cài đặt bàn phím"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Di chuyển bằng bàn phím"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tìm hiểu về phím tắt"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Di chuyển bằng bàn di chuột"</string>
@@ -1483,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Do các ứng dụng cung cấp"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Hiển thị"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Không xác định"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Đặt lại các ô"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Đặt lại các ô về thứ tự và kích thước ban đầu?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Đặt lại mọi ô?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Mọi ô Cài đặt nhanh sẽ được đặt lại về chế độ cài đặt ban đầu của thiết bị"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index d9d8af1d644c..8aa360bbeaf7 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Đang tắt"</item>
<item msgid="3028994095749238254">"Đang bật"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index b463900a331d..6e76bb7dbe4e 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"录制整个屏幕"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"全屏录制:%s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"录制单个应用时,该应用中显示或播放的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"录制屏幕"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"您正在录制“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止录制"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在共享屏幕"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"分享内容"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止共享屏幕吗?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"要停止分享吗?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"您正在与“<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>”分享整个屏幕"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"您正在与一个应用分享整个屏幕"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"您正在分享“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”的画面"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"您正在分享一个应用的画面"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"您目前正在与应用进行分享"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止共享"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投屏"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投屏吗?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"输入"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助听器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在开启…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动屏幕旋转"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"实时字幕"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"锁屏微件"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可以查看锁屏上的微件,平板电脑处于锁定状态时也是如此。"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消选中微件"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"减小高度"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"增加高度"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁屏微件"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"历史记录"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"通知设置"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"通知历史记录"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"最新"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"静音"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -597,7 +593,7 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"没有通知"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"没有新通知"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"已触发“通知音量渐降”"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"“通知音量渐降”功能现已开启"</string>
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果您在短时间内收到很多通知,设备音量和提醒次数会自动降低,最长持续 2 分钟。"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"关闭"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解锁即可查看旧通知"</string>
@@ -705,6 +701,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"头部跟踪"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"点按即可更改振铃器模式"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"响铃模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string>
@@ -874,6 +871,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分屏模式时,切换到右侧或下方的应用"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"在分屏期间:将一个应用替换为另一个应用"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"输入"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切换到下一种语言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切换到上一种语言"</string>
@@ -1415,19 +1414,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"当前应用"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"无障碍功能"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自定义键盘快捷键"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"无搜索结果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自定义"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"键盘设置"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用键盘导航"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"了解键盘快捷键"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用触控板导航"</string>
@@ -1441,7 +1451,7 @@
<string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
<string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在触控板上用三根手指向左或向右滑动"</string>
<string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"太棒了!"</string>
- <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您完成了“返回”手势教程。"</string>
+ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您已完成“返回”手势教程。"</string>
<string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"前往主屏幕"</string>
<string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在触控板上用三根手指向上滑动"</string>
<string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string>
@@ -1453,7 +1463,7 @@
<string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string>
<string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
<string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string>
- <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势"</string>
+ <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势教程"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
@@ -1485,6 +1495,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由应用提供"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"显示"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"未知"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"重置功能块"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"要将功能块重置为原始排序和大小吗?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重置所有功能块吗?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有“快捷设置”功能块都将重置为设备的原始设置"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 7748251b9430..2259076341cf 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"已关闭"</item>
<item msgid="3028994095749238254">"已开启"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 91185912af9d..32fe2a100b69 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄影螢幕畫面嗎?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄影一個應用程式"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄影整個螢幕畫面"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"錄製整個螢幕:%s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"當你錄影整個螢幕畫面時,系統會錄影螢幕畫面上顯示的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"當你錄影應用程式時,系統會錄影該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"錄影螢幕畫面"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"你正在錄影「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享螢幕"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"分享內容"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止分享螢幕嗎?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"要停止分享嗎?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"你正與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個螢幕畫面"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"你正與一個應用程式分享整個螢幕畫面"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"你正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"你正在分享一個應用程式"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"你正與一個應用程式分享內容"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放螢幕"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在開啟…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"上鎖畫面小工具"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"無論平板電腦的螢幕是否已上鎖,任何人都可以看到上鎖畫面小工具。"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消揀小工具"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"調低高度"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"調高高度"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"上鎖畫面小工具"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"通知設定"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"通知記錄"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"新"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"靜音"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知緩和已開啟"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"當你在短時間內收到太多通知時,裝置就會調低音量並減少通知數量最多兩分鐘。"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"關閉"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕按即可變更響鈴模式"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"響鈴模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割螢幕時,切換至右邊或下方的應用程式"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割螢幕時,切換至左邊或上方的應用程式"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割螢幕期間:更換應用程式"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"輸入"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切換至下一個語言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換至上一個語言"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"目前的應用程式"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙功能"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"沒有相符的搜尋結果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤導覽"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"瞭解鍵盤快速鍵"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板導覽"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由應用程式提供"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"螢幕"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"重設圖塊"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"要重設圖塊的順序和大小嗎?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重設所有圖塊嗎?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有「快速設定」圖塊將重設為裝置的原始設定"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index cca7ac42906a..c5e05c91e5f1 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"關閉"</item>
<item msgid="3028994095749238254">"開啟"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 2189cbfdf278..9778e7022725 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄製畫面嗎?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄製單一應用程式"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄製整個畫面"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"錄製整個畫面:%s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"錄製整個畫面時,系統會錄下畫面上的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"當你錄製應用程式畫面時,系統會錄下該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"錄製畫面"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"目前正在錄製「<xliff:g id="APP_NAME">%1$s</xliff:g>」的畫面"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享畫面"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"正在分享內容"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"停止分享?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"要停止分享嗎?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"目前正在與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個畫面"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"目前正在與某個應用程式分享整個畫面"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"目前正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」的畫面"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"目前正在分享應用程式畫面"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"目前正在與應用程式分享內容"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放畫面"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投放?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"開啟中…"</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"螢幕鎖定小工具"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"即使平板電腦已鎖定,所有人仍可查看螢幕鎖定畫面上的小工具。"</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消選取小工具"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"調低"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"調高"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"螢幕鎖定小工具"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"通知設定"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"通知記錄"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"最新"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"靜音"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知緩和設定已開啟"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果一次收到過多通知,裝置就會自動降低音量並減少通知數量,持續時間最多 2 分鐘。"</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"關閉"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"頭部追蹤"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"輕觸即可變更鈴聲模式"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"鈴聲模式"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割畫面時,切換到右邊或上方的應用程式"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割畫面時,切換到左邊或上方的應用程式"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割畫面期間:更換應用程式"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"輸入"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切換到下一個語言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換到上一個語言"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"目前的應用程式"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自訂鍵盤快速鍵"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"找不到相符的搜尋結果"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"學習鍵盤快速鍵"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板操作"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由應用程式提供"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"螢幕"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"重設設定方塊"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"要將設定方塊的順序和大小恢復預設值嗎?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重設所有設定方塊嗎?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有快速設定方塊都會恢復裝置的原始設定"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 4cc580434a36..2d34b380af52 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"已關閉"</item>
<item msgid="3028994095749238254">"已開啟"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 05b5040faf85..9f8cf1fe2610 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -112,8 +112,7 @@
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekhoda isikrini sakho?"</string>
<string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekhoda i-app eyodwa"</string>
<string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekhoda sonke isikrini"</string>
- <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
- <skip />
+ <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="3754611651558838691">"Rekhoda sonke isikrini: %s"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Uma urekhoda sonke isikrini sakho, noma yini evela esikrinini iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Uma urekhoda i-app, noma yini evezwa noma edlala kuleyo app iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekhoda isikrini"</string>
@@ -138,17 +137,14 @@
<string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Njengamanje urekhoda i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Misa ukurekhoda"</string>
<string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Yabelana ngesikrini"</string>
- <!-- no translation found for share_to_app_chip_accessibility_label_generic (5517431657924536133) -->
- <skip />
+ <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Ukwabelana ngokuqukethwe"</string>
<string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Misa ukwabelana ngeskrini?"</string>
- <!-- no translation found for share_to_app_stop_dialog_title_generic (9079161538135843648) -->
- <skip />
+ <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Misa ukwabelana?"</string>
<string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Njengamanje wabelana ngaso sonke isikrini sakho ne-<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Njengamanje wabelana ngaso sonke isikrini sakho ne-app"</string>
<string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Njengamanje wabelana nge-<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
<string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Njengamanje wabelana nge-app"</string>
- <!-- no translation found for share_to_app_stop_dialog_message_generic (7622174291691249392) -->
- <skip />
+ <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Njengamanje wabelana ne-app"</string>
<string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Misa ukwabelana"</string>
<string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Isikrini sokusakaza"</string>
<string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Misa ukusakaza?"</string>
@@ -330,6 +326,8 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Okokufaka"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Imishini yendlebe"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Iyavula..."</string>
+ <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) -->
+ <skip />
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string>
@@ -418,6 +416,8 @@
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string>
<string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okushuthwe Bukhoma"</string>
+ <!-- no translation found for quick_settings_notes_label (1028004078001002623) -->
+ <skip />
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string>
@@ -523,10 +523,8 @@
<string name="communal_widget_picker_title" msgid="1953369090475731663">"Amawijethi wesikrini esikhiyiwe"</string>
<string name="communal_widget_picker_description" msgid="490515450110487871">"Noma ubani angabuka amawijethi ngisho noma ithebulethi ikhiyiwe."</string>
<string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"yeka ukukhetha iwijethi"</string>
- <!-- no translation found for accessibility_action_label_shrink_widget (8259511040536438771) -->
- <skip />
- <!-- no translation found for accessibility_action_label_expand_widget (9190524260912211759) -->
- <skip />
+ <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Nciphisa ubude"</string>
+ <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Khuphula ubude"</string>
<string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Amawijethi wesikrini esikhiyiwe"</string>
<string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string>
<string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string>
@@ -583,10 +581,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Sula konke"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Phatha"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Umlando"</string>
- <!-- no translation found for notification_settings_button_description (2441994740884163889) -->
- <skip />
- <!-- no translation found for notification_history_button_description (1578657591405033383) -->
- <skip />
+ <string name="notification_settings_button_description" msgid="2441994740884163889">"Amasethingi esaziso"</string>
+ <string name="notification_history_button_description" msgid="1578657591405033383">"Umlando wesaziso"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Okusha"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Kuthulile"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Izaziso"</string>
@@ -597,7 +593,8 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Qala manje"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Azikho izaziso"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Azikho izaziso ezintsha"</string>
- <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ukwehlisa umsindo wezaziso kuvuliwe"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) -->
+ <skip />
<string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ivolumu yedivayisi yakho kanye nezexwayiso kuncishiswa ngokuzenzakalelayo imizuzu efika kwemi-2 lapho uthola izaziso eziningi kakhulu ngesikhathi esisodwa."</string>
<string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vala"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Vula ukuze ubone izaziso ezindala"</string>
@@ -705,6 +702,7 @@
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Okugxilile"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ukulandelela Ikhanda"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"Thepha ukuze ushintshe imodi yokukhala"</string>
+ <string name="volume_ringer_mode" msgid="6867838048430807128">"imodi yokukhala"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string>
@@ -874,6 +872,8 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Shintshela ku-app ngakwesokudla noma ngezansi ngenkathi usebenzisa uhlukanisa isikrini"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Shintshela ku-app ngakwesokunxele noma ngaphezulu ngenkathi usebenzisa ukuhlukanisa isikrini"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ngesikhathi sokuhlukaniswa kwesikrini: shintsha i-app ngenye"</string>
+ <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) -->
+ <skip />
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Okokufaka"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Shintshela olimini olulandelayo"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Shintshela olimini lwangaphambili"</string>
@@ -1415,19 +1415,30 @@
<string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"I-App yamanje"</string>
<string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string>
<string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
- <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+ <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Hlela izinqamuleli zekhibhodi ngendlela oyifisayo"</string>
+ <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) -->
<skip />
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
<string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ayikho imiphumela yosesho"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
- <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+ <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) -->
<skip />
- <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+ <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) -->
<skip />
+ <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Enza ngendlela oyifisayo"</string>
+ <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kwenziwe"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
<string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string>
<string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Amasethingi Ekhibhodi"</string>
+ <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) -->
+ <skip />
+ <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) -->
+ <skip />
<string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Funa usebenzisa ikhibhodi yakho"</string>
<string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Funda izinqamuleli zamakhibhodi"</string>
<string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Funa usebenzisa iphedi yokuthinta"</string>
@@ -1485,6 +1496,6 @@
<string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Kuhlinzekwe ama-app"</string>
<string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Bonisa"</string>
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Akwaziwa"</string>
- <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Setha amathayela kabusha"</string>
- <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Setha kabusha amathayela ekuhlelekeni nakosayizi bawo bangempela?"</string>
+ <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Qala kabusha onke amathayela?"</string>
+ <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Ithayela Lamasethingi Asheshayo lizosetha kabusha libuyele kumasethingi okuqala edivayisi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index a795ee8b9d75..1a7ce57601e3 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -191,4 +191,7 @@
<item msgid="3079622119444911877">"Kuvaliwe"</item>
<item msgid="3028994095749238254">"Kuvuliwe"</item>
</string-array>
+ <!-- no translation found for tile_states_notes:0 (5894333929299989301) -->
+ <!-- no translation found for tile_states_notes:1 (6419996398343291862) -->
+ <!-- no translation found for tile_states_notes:2 (5908720590832378783) -->
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d4a52c3aeafb..48af82ad7943 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -79,6 +79,9 @@
<!-- The number of columns in the infinite grid QuickSettings -->
<integer name="quick_settings_infinite_grid_num_columns">4</integer>
+ <!-- The maximum width of large tiles in the infinite grid QuickSettings -->
+ <integer name="quick_settings_infinite_grid_tile_max_width">4</integer>
+
<!-- The number of columns in the Dual Shade QuickSettings -->
<integer name="quick_settings_dual_shade_num_columns">4</integer>
@@ -116,7 +119,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes
</string>
<!-- The tiles to display in QuickSettings -->
@@ -383,6 +386,10 @@
<!-- Whether to show activity indicators in the status bar -->
<bool name="config_showActivity">false</bool>
+ <!-- Whether to show the opportunistic satellite icon. When true, an icon will show to indicate
+ satellite capabilities when all other connections are out of service. -->
+ <bool name="config_showOpportunisticSatelliteIcon">true</bool>
+
<!-- Whether or not to show the notification shelf that houses the icons of notifications that
have been scrolled off-screen. -->
<bool name="config_showNotificationShelf">true</bool>
@@ -1082,4 +1089,9 @@
enable the desktop specific features.
-->
<bool name="config_enableDesktopFeatureSet">false</bool>
+
+ <!--
+ Whether the user switching can only happen by logging out and going through the system user (login screen).
+ -->
+ <bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7fa287944956..67eb5b0fdf6b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -815,8 +815,7 @@
<dimen name="keyguard_clock_top_margin">18dp</dimen>
<!-- The amount to shift the clocks during a small/large transition -->
<dimen name="keyguard_clock_switch_y_shift">14dp</dimen>
- <!-- When large clock is showing, offset the smartspace by this amount -->
- <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
+
<!-- The amount to translate lockscreen elements on the GONE->AOD transition -->
<dimen name="keyguard_enter_from_top_translation_y">-100dp</dimen>
<!-- The amount to translate lockscreen elements on the GONE->AOD transition, on device fold -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1766cdf8c804..53ab686ff0d7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -794,6 +794,7 @@
<!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_secondary_label_transient">Turning on&#8230;</string>
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_brightness_unable_adjust_msg">Can\'t adjust brightness because it\'s being\n controlled by the top app</string>
<!-- QuickSettings: Rotation Unlocked [CHAR LIMIT=NONE] -->
<string name="quick_settings_rotation_unlocked_label">Auto-rotate</string>
<!-- Accessibility label for Auto-ratate QuickSettings tile [CHAR LIMIT=NONE] -->
@@ -1004,6 +1005,9 @@
<!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40] [BACKUP_MESSAGE_ID=8916875614623730005]-->
<string name="quick_settings_hearing_devices_live_caption_title">Live Caption</string>
+ <!-- QuickSettings: Notes tile. The label of a quick settings tile for launching the default notes taking app. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_notes_label">Note</string>
+
<!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
<string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
<!--- Title of dialog triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=150] -->
@@ -1518,7 +1522,7 @@
<string name="no_unseen_notif_text">No new notifications</string>
<!-- Title of heads up notification for adaptive notifications user education. [CHAR LIMIT=60] -->
- <string name="adaptive_notification_edu_hun_title">Notification cooldown is on</string>
+ <string name="adaptive_notification_edu_hun_title">Notification cooldown is now on</string>
<!-- Text of heads up notification for adaptive notifications user education. [CHAR LIMIT=100] -->
<string name="adaptive_notification_edu_hun_text">Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once.</string>
@@ -2270,6 +2274,8 @@
<string name="system_multitasking_splitscreen_focus_lhs">Switch to app on left or above while using split screen</string>
<!-- User visible title for the keyboard shortcut that replaces an app from one to another during split screen [CHAR LIMIT=70] -->
<string name="system_multitasking_replace">During split screen: replace an app from one to another</string>
+ <!-- User visible title for the keyboard shortcut that moves a focused task to a next display [CHAR LIMIT=70] -->
+ <string name="system_multitasking_move_to_next_display">Move active window between displays</string>
<!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_input">Input</string>
@@ -3738,6 +3744,11 @@
is a component that shows the user which keyboard shortcuts they can use.
[CHAR LIMIT=NONE] -->
<string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string>
+ <!-- Sub title at the top of the keyboard shortcut helper customization dialog. Explains to the
+ user what action they need to take in the customization dialog to assign a new custom shortcut.
+ The helper is a component that shows the user which keyboard shortcuts they can use.
+ [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_customize_mode_sub_title">Press key to assign shortcut</string>
<!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user
hasn't typed in anything in the search box yet. The helper is a component that shows the
user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
@@ -3749,6 +3760,16 @@
use. The helper shows shortcuts in categories, which can be collapsed or expanded.
[CHAR LIMIT=NONE] -->
<string name="shortcut_helper_content_description_collapse_icon">Collapse icon</string>
+ <!-- Content description of the Meta key (also called Action Key) icon that prompts users to
+ press some key combination starting with meta key to assign new key combination to shortcut
+ in shortcut helper customization dialog. The helper is a component that shows the user
+ which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_content_description_meta_key">Action or Meta key icon</string>
+ <!-- Content description of the plus icon after the meta key icon prompts users to
+ press some key combination starting with meta key to assign new key combination to shortcut
+ in shortcut helper customization dialog. The helper is a component that shows the user
+ which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_content_description_plus_icon">Plus icon</string>
<!-- Description text of the button that allows user to customize shortcuts in keyboard
shortcut helper The helper is a component that shows the user which keyboard shortcuts
they can use. [CHAR LIMIT=NONE] -->
@@ -3779,6 +3800,24 @@
open keyboard settings while in shortcut helper. The helper is a component that shows the
user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_keyboard_settings_buttons_label">Keyboard Settings</string>
+ <!-- Label on the set shortcut button in keyboard shortcut helper customize dialog, that allows user to
+ confirm and assign key combination to selected shortcut. The helper is a component that
+ shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_customize_dialog_set_shortcut_button_label">Set shortcut</string>
+ <!-- Label on the cancel button in keyboard shortcut helper customize dialog, that allows user to
+ cancel and exit shortcut customization dialog, returning to the main shortcut helper page.
+ The helper is a component that shows the user which keyboard shortcuts they can use.
+ [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_customize_dialog_cancel_button_label">Cancel</string>
+ <!-- Placeholder text, prompting user to Press key combination assign to shortcut. This is shown
+ in shortcut helper's "Add Custom Shortcut" Dialog text field when user hasn't pressed
+ any key yet. The helper is a component that shows the user which keyboard shortcuts
+ they can use. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_add_shortcut_dialog_placeholder">Press key</string>
+ <!-- Error message displayed when the user select a key combination that is already in use while
+ assigning a new custom key combination to a shortcut in shortcut helper. The helper is a
+ component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_customize_dialog_error_message">Key combination already in use. Try another key.</string>
<!-- Keyboard touchpad tutorial scheduler-->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7d071cd4a04c..c69b98c8c37f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -557,6 +557,16 @@
<item name="android:showWhenLocked">true</item>
</style>
+ <style name="Theme.SystemUI.Dialog.Volume">
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:showWhenLocked">true</item>
+ <item name="android:windowBackground">@color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
<style name="SystemUI.Material3.Slider.Volume">
<item name="trackHeight">40dp</item>
<item name="thumbHeight">52dp</item>
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index ad09b466dd3e..d885e00fbe82 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -348,4 +348,14 @@
<item>Off</item>
<item>On</item>
</string-array>
+
+ <!-- State names for notes tile: unavailable, off, on.
+ This subtitle is shown when the tile is in that particular state but does not set its own
+ subtitle, so some of these may never appear on screen. They should still be translated as
+ if they could appear. [CHAR LIMIT=32] -->
+ <string-array name="tile_states_notes">
+ <item>Unavailable</item>
+ <item>Off</item>
+ <item>On</item>
+ </string-array>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 283e4556d05c..83ca496dbef2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -115,23 +115,23 @@ oneway interface IOverviewProxy {
/**
* Sent when {@link TaskbarDelegate#checkNavBarModes} is called.
*/
- void checkNavBarModes() = 30;
+ void checkNavBarModes(int displayId) = 30;
/**
* Sent when {@link TaskbarDelegate#finishBarAnimations} is called.
*/
- void finishBarAnimations() = 31;
+ void finishBarAnimations(int displayId) = 31;
/**
* Sent when {@link TaskbarDelegate#touchAutoDim} is called. {@param reset} is true, when auto
* dim is reset after a timeout.
*/
- void touchAutoDim(boolean reset) = 32;
+ void touchAutoDim(int displayid, boolean reset) = 32;
/**
* Sent when {@link TaskbarDelegate#transitionTo} is called.
*/
- void transitionTo(int barMode, boolean animate) = 33;
+ void transitionTo(int displayId, int barMode, boolean animate) = 33;
/**
* Sent when {@link TaskbarDelegate#appTransitionPending} is called.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 6209ed82a0ad..e332280bc31a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -21,6 +21,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import com.android.internal.util.ScreenshotRequest;
@@ -102,9 +103,9 @@ interface ISystemUiProxy {
oneway void expandNotificationPanel() = 29;
/**
- * Notifies SystemUI to invoke Back.
+ * Notifies SystemUI of a back KeyEvent.
*/
- oneway void onBackPressed() = 44;
+ oneway void onBackEvent(in KeyEvent keyEvent) = 44;
/** Sets home rotation enabled. */
oneway void setHomeRotationEnabled(boolean enabled) = 45;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 7ec977a8d6aa..9e8cabf141ed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -243,10 +243,6 @@ public class Task {
public Rect appBounds;
- // Last snapshot data, only used for recent tasks
- public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
- new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
-
@ViewDebug.ExportedProperty(category="recents")
public boolean isVisible;
@@ -283,7 +279,6 @@ public class Task {
public Task(Task other) {
this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
other.isLocked, other.taskDescription, other.topActivity);
- lastSnapshotData.set(other.lastSnapshotData);
positionInParent = other.positionInParent;
appBounds = other.appBounds;
isVisible = other.isVisible;
@@ -315,33 +310,10 @@ public class Task {
: key.baseIntent.getComponent();
}
- public void setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask) {
- lastSnapshotData.set(rawTask.lastSnapshotData);
- }
-
public TaskKey getKey() {
return key;
}
- /**
- * Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
- */
- public float getVisibleThumbnailRatio(boolean clipInsets) {
- if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
- return 0f;
- }
-
- float availableWidth = lastSnapshotData.taskSize.x;
- float availableHeight = lastSnapshotData.taskSize.y;
- if (clipInsets) {
- availableWidth -=
- (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right);
- availableHeight -=
- (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom);
- }
- return availableWidth / availableHeight;
- }
-
@Override
public boolean equals(Object o) {
if (o == this) {
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 24d619119983..df9f7053c3f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -35,6 +35,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.customization.R
import com.android.systemui.dagger.qualifiers.Background
@@ -62,6 +63,7 @@ import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.plugins.clocks.ZenData.ZenMode
import com.android.systemui.res.R as SysuiR
+import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
@@ -80,7 +82,6 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -103,6 +104,7 @@ constructor(
private val featureFlags: FeatureFlagsClassic,
private val zenModeController: ZenModeController,
private val zenModeInteractor: ZenModeInteractor,
+ private val userTracker: UserTracker,
) {
var loggers =
listOf(
@@ -120,6 +122,10 @@ constructor(
connectClock(value)
}
+ private fun is24HourFormat(userId: Int? = null): Boolean {
+ return DateFormat.is24HourFormat(context, userId ?: userTracker.userId)
+ }
+
private fun disconnectClock(clock: ClockController?) {
if (clock == null) {
return
@@ -186,7 +192,7 @@ constructor(
var pastVisibility: Int? = null
override fun onViewAttachedToWindow(view: View) {
- clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ clock.events.onTimeFormatChanged(is24HourFormat())
// Match the asing for view.parent's layout classes.
smallClockFrame =
(view.parent as ViewGroup)?.also { frame ->
@@ -218,7 +224,7 @@ constructor(
largeClockOnAttachStateChangeListener =
object : OnAttachStateChangeListener {
override fun onViewAttachedToWindow(p0: View) {
- clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ clock.events.onTimeFormatChanged(is24HourFormat())
}
override fun onViewDetachedFromWindow(p0: View) {}
@@ -358,7 +364,7 @@ constructor(
}
override fun onTimeFormatChanged(timeFormat: String?) {
- clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
+ clock?.run { events.onTimeFormatChanged(is24HourFormat()) }
}
override fun onTimeZoneChanged(timeZone: TimeZone) {
@@ -366,7 +372,7 @@ constructor(
}
override fun onUserSwitchComplete(userId: Int) {
- clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
+ clock?.run { events.onTimeFormatChanged(is24HourFormat(userId)) }
zenModeCallback.onNextAlarmChanged()
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 11dde6aa0dfb..71d4e9af6f55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -147,7 +147,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_clock_switch_y_shift);
mSmartspaceTopOffset = (int) (mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_smartspace_top_offset)
+ com.android.systemui.customization.R.dimen.keyguard_smartspace_top_offset)
* mContext.getResources().getConfiguration().fontScale
/ mContext.getResources().getDisplayMetrics().density
* SMARTSPACE_TOP_PADDING_MULTIPLIER);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1342dd05d7f2..95830b5f4ed7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,6 +15,8 @@
*/
package com.android.keyguard;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
import android.annotation.NonNull;
import android.app.Presentation;
import android.content.Context;
@@ -36,18 +38,24 @@ import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.data.repository.ShadePositionRepository;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import dagger.Lazy;
+import kotlinx.coroutines.CoroutineScope;
+
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Provider;
@SysUISingleton
public class KeyguardDisplayManager {
@@ -58,6 +66,7 @@ public class KeyguardDisplayManager {
private final DisplayManager mDisplayService;
private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
+ private final Provider<ShadePositionRepository> mShadePositionRepositoryProvider;
private final ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
private final Context mContext;
@@ -102,9 +111,12 @@ public class KeyguardDisplayManager {
DeviceStateHelper deviceStateHelper,
KeyguardStateController keyguardStateController,
ConnectedDisplayKeyguardPresentation.Factory
- connectedDisplayKeyguardPresentationFactory) {
+ connectedDisplayKeyguardPresentationFactory,
+ Provider<ShadePositionRepository> shadePositionRepositoryProvider,
+ @Application CoroutineScope appScope) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
+ mShadePositionRepositoryProvider = shadePositionRepositoryProvider;
uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
mDisplayService = mContext.getSystemService(DisplayManager.class);
mDisplayTracker = displayTracker;
@@ -112,6 +124,17 @@ public class KeyguardDisplayManager {
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
+ if (ShadeWindowGoesAround.isEnabled()) {
+ collectFlow(appScope, shadePositionRepositoryProvider.get().getDisplayId(),
+ (id) -> onShadeWindowMovedToDisplayId(id));
+ }
+ }
+
+ private void onShadeWindowMovedToDisplayId(int shadeDisplayId) {
+ if (mShowing) {
+ hidePresentation(shadeDisplayId);
+ updateDisplays(/* showing= */ true);
+ }
}
private boolean isKeyguardShowable(Display display) {
@@ -119,9 +142,20 @@ public class KeyguardDisplayManager {
if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
return false;
}
- if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
- if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
- return false;
+ if (ShadeWindowGoesAround.isEnabled()) {
+ int shadeDisplayId = mShadePositionRepositoryProvider.get().getDisplayId().getValue();
+ if (display.getDisplayId() == shadeDisplayId) {
+ if (DEBUG) {
+ Log.i(TAG,
+ "Do not show KeyguardPresentation on the shade window display");
+ }
+ return false;
+ }
+ } else {
+ if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
+ if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
+ return false;
+ }
}
display.getDisplayInfo(mTmpDisplayInfo);
if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8b593701540b..8ca0e807b31c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -121,6 +121,7 @@ import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -218,7 +219,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int MSG_USER_UNLOCKED = 334;
private static final int MSG_ASSISTANT_STACK_CHANGED = 335;
private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
- private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
private static final int MSG_TELEPHONY_CAPABLE = 338;
private static final int MSG_TIMEZONE_UPDATE = 339;
private static final int MSG_USER_STOPPED = 340;
@@ -401,7 +401,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mFingerprintDetectRunning;
private boolean mIsDreaming;
- private boolean mLogoutEnabled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
@@ -473,6 +472,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ @Deprecated
private final SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray();
private final SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private final SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
@@ -1737,9 +1737,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
} else if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
- } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
- action)) {
- mHandler.sendEmptyMessage(MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED);
}
}
};
@@ -2326,9 +2323,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
break;
- case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
- updateLogoutEnabled();
- break;
case MSG_TELEPHONY_CAPABLE:
updateTelephonyCapable((boolean) msg.obj);
break;
@@ -2494,7 +2488,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean isUserUnlocked = mUserManager.isUserUnlocked(user);
mLogger.logUserUnlockedInitialState(user, isUserUnlocked);
mUserIsUnlocked.put(user, isUserUnlocked);
- mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
updateSecondaryLockscreenRequirement(user);
List<UserInfo> allUsers = mUserManager.getUsers();
for (UserInfo userInfo : allUsers) {
@@ -2688,7 +2681,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* @see Intent#ACTION_USER_UNLOCKED
*/
public boolean isUserUnlocked(int userId) {
- return mUserIsUnlocked.get(userId);
+ if (Flags.userEncryptedSource()) {
+ return mUserManager.isUserUnlocked(userId);
+ } else {
+ return mUserIsUnlocked.get(userId);
+ }
}
/**
@@ -4054,28 +4051,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return null; // not found
}
- /**
- * @return a cached version of DevicePolicyManager.isLogoutEnabled()
- */
- public boolean isLogoutEnabled() {
- return mLogoutEnabled;
- }
-
- private void updateLogoutEnabled() {
- Assert.isMainThread();
- boolean logoutEnabled = mDevicePolicyManager.isLogoutEnabled();
- if (mLogoutEnabled != logoutEnabled) {
- mLogoutEnabled = logoutEnabled;
-
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onLogoutEnabledChanged();
- }
- }
- }
- }
-
protected int getBiometricLockoutDelay() {
return BIOMETRIC_LOCKOUT_RESET_DELAY_MS;
}
@@ -4213,7 +4188,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println("ActiveUnlockRunning="
+ mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId()));
- pw.println("userUnlockedCache[userid=" + userId + "]=" + isUserUnlocked(userId));
+ pw.println("userUnlockedCache[userid=" + userId + "]=" + mUserIsUnlocked.get(userId));
pw.println("actualUserUnlocked[userid=" + userId + "]="
+ mUserManager.isUserUnlocked(userId));
new DumpsysTableLogger(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 7ac5ac229793..fdee21bcc479 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -286,11 +286,6 @@ public class KeyguardUpdateMonitorCallback {
public void onTrustAgentErrorMessage(CharSequence message) { }
/**
- * Called when a value of logout enabled is change.
- */
- public void onLogoutEnabledChanged() { }
-
- /**
* Called when authenticated fingerprint biometrics are cleared.
*/
public void onFingerprintsCleared() { }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index ef172a1b24f6..fc42045c02c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -62,13 +62,13 @@ public abstract class ClockRegistryModule {
scope,
mainDispatcher,
bgDispatcher,
- featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
+ com.android.systemui.Flags.lockscreenCustomClocks()
+ || featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
/* handleAllUsers= */ true,
new DefaultClockProvider(
context,
layoutInflater,
resources,
- featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION),
MigrateClocksToBlueprint.isEnabled(),
com.android.systemui.Flags.clockReactiveVariants()
),
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index bebfd859f9ed..cd19aaac6831 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -116,7 +116,7 @@ constructor(
fun logUpdateLockScreenUserLockedMsg(
userId: Int,
- userUnlocked: Boolean,
+ userStorageUnlocked: Boolean,
encryptedOrLockdown: Boolean,
) {
buffer.log(
@@ -124,12 +124,12 @@ constructor(
LogLevel.DEBUG,
{
int1 = userId
- bool1 = userUnlocked
+ bool1 = userStorageUnlocked
bool2 = encryptedOrLockdown
},
{
"updateLockScreenUserLockedMsg userId=$int1 " +
- "userUnlocked:$bool1 encryptedOrLockdown:$bool2"
+ "userStorageUnlocked:$bool1 encryptedOrLockdown:$bool2"
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 46e45aaf8a8a..66b3e189b6c9 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -905,7 +905,18 @@ public class ScreenDecorations implements
return lp;
}
- private WindowManager.LayoutParams getWindowLayoutBaseParams() {
+ public static WindowManager.LayoutParams getWindowLayoutBaseParams() {
+ return getWindowLayoutBaseParams(/* excludeFromScreenshots= */ true);
+ }
+
+ /**
+ * Creates the base {@link WindowManager.LayoutParams} that are used for all decoration windows.
+ *
+ * @param excludeFromScreenshots whether to set the {@link
+ * WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY} flag.
+ */
+ public static WindowManager.LayoutParams getWindowLayoutBaseParams(
+ boolean excludeFromScreenshots) {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -921,7 +932,7 @@ public class ScreenDecorations implements
// FLAG_SLIPPERY can only be set by trusted overlays
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
- if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
+ if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS && excludeFromScreenshots) {
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 811b47d57c1d..a46b236d46fb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.animation.Animator;
import android.annotation.SuppressLint;
import android.app.ActivityThread;
import android.app.Application;
@@ -135,6 +136,9 @@ public class SystemUIApplication extends Application implements
if (Flags.enableLayoutTracing()) {
View.setTraceLayoutSteps(true);
}
+ if (com.android.window.flags.Flags.systemUiPostAnimationEnd()) {
+ Animator.setPostNotifyEndListenerEnabled(true);
+ }
if (mProcessWrapper.isSystemUser()) {
IntentFilter bootCompletedFilter = new
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index f8b445ba069e..3cf400aa5c16 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -38,7 +38,6 @@ import android.view.IWindowManager;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -138,10 +137,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- scvhSupplier,
- new SfVsyncFrameCallbackProvider(),
- WindowManagerGlobal::getWindowSession,
- mViewCaptureAwareWindowManager);
+ scvhSupplier);
}
}
@@ -407,7 +403,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
}
}
- boolean isMagnificationSettingsPanelShowing(int displayId) {
+ @MainThread
+ private boolean isMagnificationSettingsPanelShowing(int displayId) {
final MagnificationSettingsController magnificationSettingsController =
mMagnificationSettingsSupplier.get(displayId);
if (magnificationSettingsController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0883a0611d15..7d5cf232bcb9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -54,11 +54,8 @@ import android.util.Range;
import android.util.Size;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
-import android.view.IWindow;
-import android.view.IWindowSession;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
@@ -80,10 +77,8 @@ import android.widget.ImageView;
import androidx.annotation.UiThread;
import androidx.core.math.MathUtils;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.res.R;
@@ -127,7 +122,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private final SurfaceControl.Transaction mTransaction;
private final WindowManager mWm;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private float mScale;
private int mSettingsButtonIndex = MagnificationSize.DEFAULT;
@@ -219,11 +213,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private int mMinWindowSize;
private final WindowMagnificationAnimationController mAnimationController;
- private final Supplier<IWindowSession> mGlobalWindowSessionSupplier;
- private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private final MagnificationGestureDetector mGestureDetector;
private int mBounceEffectDuration;
- private final Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
private Locale mLocale;
private NumberFormat mPercentFormat;
private float mBounceEffectAnimationScale;
@@ -258,18 +249,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@NonNull WindowMagnifierCallback callback,
SysUiState sysUiState,
SecureSettings secureSettings,
- Supplier<SurfaceControlViewHost> scvhSupplier,
- SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
- Supplier<IWindowSession> globalWindowSessionSupplier,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ Supplier<SurfaceControlViewHost> scvhSupplier) {
mContext = context;
mHandler = handler;
mAnimationController = animationController;
- mAnimationController.setOnAnimationEndRunnable(() -> {
- if (Flags.createWindowlessWindowMagnifier()) {
- notifySourceBoundsChanged();
- }
- });
+ mAnimationController.setOnAnimationEndRunnable(this::notifySourceBoundsChanged);
mAnimationController.setWindowMagnificationController(this);
mWindowMagnifierCallback = callback;
mSysUiState = sysUiState;
@@ -283,7 +267,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mWm = context.getSystemService(WindowManager.class);
mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mResources = mContext.getResources();
mScale = secureSettings.getFloatForUser(
@@ -313,76 +296,31 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mGestureDetector =
new MagnificationGestureDetector(mContext, handler, this);
mWindowInsetChangeRunnable = this::onWindowInsetChanged;
- mGlobalWindowSessionSupplier = globalWindowSessionSupplier;
- mSfVsyncFrameProvider = sfVsyncFrameProvider;
// Initialize listeners.
- if (Flags.createWindowlessWindowMagnifier()) {
- mMirrorViewRunnable = new Runnable() {
- final Rect mPreviousBounds = new Rect();
-
- @Override
- public void run() {
- if (mMirrorView != null) {
- if (mPreviousBounds.width() != mMirrorViewBounds.width()
- || mPreviousBounds.height() != mMirrorViewBounds.height()) {
- mMirrorView.setSystemGestureExclusionRects(Collections.singletonList(
- new Rect(0, 0, mMirrorViewBounds.width(),
- mMirrorViewBounds.height())));
- mPreviousBounds.set(mMirrorViewBounds);
- }
- updateSystemUIStateIfNeeded();
- mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
- mDisplayId, mMirrorViewBounds);
- }
- }
- };
-
- mMirrorSurfaceViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- mMirrorView.post(this::applyTapExcludeRegion);
+ mMirrorViewRunnable = new Runnable() {
+ final Rect mPreviousBounds = new Rect();
- mMirrorViewGeometryVsyncCallback = null;
- } else {
- mMirrorViewRunnable = () -> {
+ @Override
+ public void run() {
if (mMirrorView != null) {
- final Rect oldViewBounds = new Rect(mMirrorViewBounds);
- mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
- if (oldViewBounds.width() != mMirrorViewBounds.width()
- || oldViewBounds.height() != mMirrorViewBounds.height()) {
+ if (mPreviousBounds.width() != mMirrorViewBounds.width()
+ || mPreviousBounds.height() != mMirrorViewBounds.height()) {
mMirrorView.setSystemGestureExclusionRects(Collections.singletonList(
- new Rect(0, 0,
- mMirrorViewBounds.width(), mMirrorViewBounds.height())));
+ new Rect(0, 0, mMirrorViewBounds.width(),
+ mMirrorViewBounds.height())));
+ mPreviousBounds.set(mMirrorViewBounds);
}
updateSystemUIStateIfNeeded();
mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
mDisplayId, mMirrorViewBounds);
}
- };
-
- mMirrorSurfaceViewLayoutChangeListener =
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- mMirrorView.post(this::applyTapExcludeRegion);
-
- mMirrorViewGeometryVsyncCallback =
- l -> {
- if (isActivated() && mMirrorSurface != null && calculateSourceBounds(
- mMagnificationFrame, mScale)) {
- // The final destination for the magnification surface should be at 0,0
- // since the ViewRootImpl's position will change
- mTmpRect.set(0, 0, mMagnificationFrame.width(),
- mMagnificationFrame.height());
- mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect,
- Surface.ROTATION_0).apply();
-
- // Notify source bounds change when the magnifier is not animating.
- if (!mAnimationController.isAnimating()) {
- mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId,
- mSourceBounds);
- }
- }
- };
- }
+ }
+ };
+
+ mMirrorSurfaceViewLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ mMirrorView.post(this::applyTouchableRegion);
mMirrorViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -463,7 +401,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (isActivated()) {
updateDimensions();
- applyTapExcludeRegion();
+ applyTouchableRegion();
}
if (!enable) {
@@ -513,9 +451,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (mMirrorView != null) {
mHandler.removeCallbacks(mMirrorViewRunnable);
mMirrorView.removeOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
- if (!Flags.createWindowlessWindowMagnifier()) {
- mViewCaptureAwareWindowManager.removeView(mMirrorView);
- }
mMirrorView = null;
}
@@ -624,11 +559,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (!isActivated()) return;
LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
params.accessibilityTitle = getAccessibilityWindowTitle();
- if (Flags.createWindowlessWindowMagnifier()) {
- mSurfaceControlViewHost.relayout(params);
- } else {
- mWm.updateViewLayout(mMirrorView, params);
- }
+ mSurfaceControlViewHost.relayout(params);
}
/**
@@ -678,62 +609,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
return (oldRotation - newRotation + 4) % 4 * 90;
}
- private void createMirrorWindow() {
- if (Flags.createWindowlessWindowMagnifier()) {
- createWindowlessMirrorWindow();
- return;
- }
-
- // The window should be the size the mirrored surface will be but also add room for the
- // border and the drag handle.
- int windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
- int windowHeight = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
-
- LayoutParams params = new LayoutParams(
- windowWidth, windowHeight,
- LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- LayoutParams.FLAG_NOT_TOUCH_MODAL
- | LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSPARENT);
- params.gravity = Gravity.TOP | Gravity.LEFT;
- params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
- params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- params.receiveInsetsIgnoringZOrder = true;
- params.setTitle(mContext.getString(R.string.magnification_window_title));
- params.accessibilityTitle = getAccessibilityWindowTitle();
-
- mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
- mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view);
-
- mMirrorBorderView = mMirrorView.findViewById(R.id.magnification_inner_border);
-
- // Allow taps to go through to the mirror SurfaceView below.
- mMirrorSurfaceView.addOnLayoutChangeListener(mMirrorSurfaceViewLayoutChangeListener);
-
- mMirrorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
- mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
- mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> {
- if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) {
- mHandler.post(mWindowInsetChangeRunnable);
- }
- return v.onApplyWindowInsets(insets);
- });
-
- mViewCaptureAwareWindowManager.addView(mMirrorView, params);
-
- SurfaceHolder holder = mMirrorSurfaceView.getHolder();
- holder.addCallback(this);
- holder.setFormat(PixelFormat.RGBA_8888);
- addDragTouchListeners();
- }
-
private void createWindowlessMirrorWindow() {
// The window should be the size the mirrored surface will be but also add room for the
// border and the drag handle.
@@ -802,62 +677,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
- private void applyTapExcludeRegion() {
- if (Flags.createWindowlessWindowMagnifier()) {
- applyTouchableRegion();
- return;
- }
-
- // Sometimes this can get posted and run after deleteWindowMagnification() is called.
- if (mMirrorView == null) return;
-
- final Region tapExcludeRegion = calculateTapExclude();
- final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken());
- try {
- IWindowSession session = mGlobalWindowSessionSupplier.get();
- session.updateTapExcludeRegion(window, tapExcludeRegion);
- } catch (RemoteException e) {
- }
- }
-
- private Region calculateTapExclude() {
- Region regionInsideDragBorder = new Region(mBorderDragSize, mBorderDragSize,
- mMirrorView.getWidth() - mBorderDragSize,
- mMirrorView.getHeight() - mBorderDragSize);
-
- Region tapExcludeRegion = new Region();
-
- Rect dragArea = new Rect();
- mDragView.getHitRect(dragArea);
-
- Rect topLeftArea = new Rect();
- mTopLeftCornerView.getHitRect(topLeftArea);
-
- Rect topRightArea = new Rect();
- mTopRightCornerView.getHitRect(topRightArea);
-
- Rect bottomLeftArea = new Rect();
- mBottomLeftCornerView.getHitRect(bottomLeftArea);
-
- Rect bottomRightArea = new Rect();
- mBottomRightCornerView.getHitRect(bottomRightArea);
-
- Rect closeArea = new Rect();
- mCloseView.getHitRect(closeArea);
-
- // add tapExcludeRegion for Drag or close
- tapExcludeRegion.op(dragArea, Region.Op.UNION);
- tapExcludeRegion.op(topLeftArea, Region.Op.UNION);
- tapExcludeRegion.op(topRightArea, Region.Op.UNION);
- tapExcludeRegion.op(bottomLeftArea, Region.Op.UNION);
- tapExcludeRegion.op(bottomRightArea, Region.Op.UNION);
- tapExcludeRegion.op(closeArea, Region.Op.UNION);
-
- regionInsideDragBorder.op(tapExcludeRegion, Region.Op.DIFFERENCE);
-
- return regionInsideDragBorder;
- }
-
private void applyTouchableRegion() {
// Sometimes this can get posted and run after deleteWindowMagnification() is called.
if (mMirrorView == null) return;
@@ -1085,13 +904,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* {@link #mMagnificationFrame}.
*/
private void modifyWindowMagnification(boolean computeWindowSize) {
- if (Flags.createWindowlessWindowMagnifier()) {
- updateMirrorSurfaceGeometry();
- updateWindowlessMirrorViewLayout(computeWindowSize);
- } else {
- mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
- updateMirrorViewLayout(computeWindowSize);
- }
+ updateMirrorSurfaceGeometry();
+ updateWindowlessMirrorViewLayout(computeWindowSize);
}
/**
@@ -1169,58 +983,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mMirrorViewRunnable.run();
}
- /**
- * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame}
- * and translates MirrorView position when the view is moved close to the screen edges;
- *
- * @param computeWindowSize set to {@code true} to compute window size with
- * {@link #mMagnificationFrame}.
- */
- private void updateMirrorViewLayout(boolean computeWindowSize) {
- if (!isActivated()) {
- return;
- }
- final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth();
- final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight();
-
- LayoutParams params =
- (LayoutParams) mMirrorView.getLayoutParams();
- params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
- params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
- if (computeWindowSize) {
- params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
- params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
- }
-
- // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
- // able to move close to the screen edges.
- final float translationX;
- final float translationY;
- if (params.x < 0) {
- translationX = Math.max(params.x, -mOuterBorderSize);
- } else if (params.x > maxMirrorViewX) {
- translationX = Math.min(params.x - maxMirrorViewX, mOuterBorderSize);
- } else {
- translationX = 0;
- }
- if (params.y < 0) {
- translationY = Math.max(params.y, -mOuterBorderSize);
- } else if (params.y > maxMirrorViewY) {
- translationY = Math.min(params.y - maxMirrorViewY, mOuterBorderSize);
- } else {
- translationY = 0;
- }
- mMirrorView.setTranslationX(translationX);
- mMirrorView.setTranslationY(translationY);
- mWm.updateViewLayout(mMirrorView, params);
-
- // If they are not dragging the handle, we can move the drag handle immediately without
- // disruption. But if they are dragging it, we avoid moving until the end of the drag.
- if (!mIsDragging) {
- mMirrorView.post(this::maybeRepositionButton);
- }
- }
-
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v == mDragView
@@ -1474,7 +1236,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
calculateMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
if (!isActivated()) {
- createMirrorWindow();
+ createWindowlessMirrorWindow();
showControls();
applyResourcesValues();
} else {
@@ -1766,7 +1528,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (newGravity != layoutParams.gravity) {
layoutParams.gravity = newGravity;
mDragView.setLayoutParams(layoutParams);
- mDragView.post(this::applyTapExcludeRegion);
+ mDragView.post(this::applyTouchableRegion);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
index 5414b623ff97..39fd44a83c58 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
@@ -63,6 +63,7 @@ constructor(
private val captioningManager: StateFlow<CaptioningManager?> =
userRepository.selectedUser
.map { userScopedCaptioningManagerProvider.forUser(it.userInfo.userHandle) }
+ .flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
override val captioningModel: StateFlow<CaptioningModel?> =
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index c9c4fd594adc..6635d8b06a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -22,6 +22,7 @@ import android.annotation.UserIdInt
import android.app.admin.DevicePolicyManager
import android.content.IntentFilter
import android.os.UserHandle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel
@@ -57,7 +58,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/** Defines interface for classes that can access authentication-related application state. */
@@ -178,6 +178,16 @@ interface AuthenticationRepository {
* profile of an organization-owned device.
*/
@UserIdInt suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int
+
+ /**
+ * Returns the device policy enforced maximum time to lock the device, in milliseconds. When the
+ * device goes to sleep, this is the maximum time the device policy allows to wait before
+ * locking the device, despite what the user setting might be set to.
+ */
+ suspend fun getMaximumTimeToLock(): Long
+
+ /** Returns `true` if the power button should instantly lock the device, `false` otherwise. */
+ suspend fun getPowerButtonInstantlyLocks(): Boolean
}
@SysUISingleton
@@ -324,6 +334,19 @@ constructor(
}
}
+ override suspend fun getMaximumTimeToLock(): Long {
+ return withContext(backgroundDispatcher) {
+ devicePolicyManager.getMaximumTimeToLock(/* admin= */ null, selectedUserId)
+ }
+ }
+
+ /** Returns `true` if the power button should instantly lock the device, `false` otherwise. */
+ override suspend fun getPowerButtonInstantlyLocks(): Boolean {
+ return withContext(backgroundDispatcher) {
+ lockPatternUtils.getPowerButtonInstantlyLocks(selectedUserId)
+ }
+ }
+
private val selectedUserId: Int
@UserIdInt get() = userRepository.getSelectedUserInfo().id
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 67579fd7f696..1c994731c393 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.authentication.domain.interactor
import android.os.UserHandle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
@@ -49,7 +50,6 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Hosts application business logic related to user authentication.
@@ -215,7 +215,7 @@ constructor(
*/
suspend fun authenticate(
input: List<Any>,
- tryAutoConfirm: Boolean = false
+ tryAutoConfirm: Boolean = false,
): AuthenticationResult {
if (input.isEmpty()) {
throw IllegalArgumentException("Input was empty!")
@@ -254,6 +254,20 @@ constructor(
return AuthenticationResult.FAILED
}
+ /**
+ * Returns the device policy enforced maximum time to lock the device, in milliseconds. When the
+ * device goes to sleep, this is the maximum time the device policy allows to wait before
+ * locking the device, despite what the user setting might be set to.
+ */
+ suspend fun getMaximumTimeToLock(): Long {
+ return repository.getMaximumTimeToLock()
+ }
+
+ /** Returns `true` if the power button should instantly lock the device, `false` otherwise. */
+ suspend fun getPowerButtonInstantlyLocks(): Boolean {
+ return !getAuthenticationMethod().isSecure || repository.getPowerButtonInstantlyLocks()
+ }
+
private suspend fun shouldSkipAuthenticationAttempt(
authenticationMethod: AuthenticationMethodModel,
isAutoConfirmAttempt: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index f8254592819b..a107322423bb 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -36,6 +36,7 @@ import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
import com.android.systemui.people.widget.PeopleBackupHelper
+import com.android.systemui.qs.panels.domain.backup.QSPreferencesBackupHelper
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManagerImpl
@@ -58,9 +59,9 @@ open class BackupHelper : BackupAgentHelper() {
private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY =
"systemui.keyguard.quickaffordance.shared_preferences"
- private const val COMMUNAL_PREFS_BACKUP_KEY =
- "systemui.communal.shared_preferences"
+ private const val COMMUNAL_PREFS_BACKUP_KEY = "systemui.communal.shared_preferences"
private const val COMMUNAL_STATE_BACKUP_KEY = "systemui.communal_state"
+ private const val QS_PREFERENCES_BACKUP_KEY = "systemui.qs.shared_preferences"
val controlsDataLock = Any()
const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
@@ -74,22 +75,20 @@ open class BackupHelper : BackupAgentHelper() {
val keys = PeopleBackupHelper.getFilesToBackup()
addHelper(
PEOPLE_TILES_BACKUP_KEY,
- PeopleBackupHelper(this, userHandle, keys.toTypedArray())
+ PeopleBackupHelper(this, userHandle, keys.toTypedArray()),
)
addHelper(
KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY,
- KeyguardQuickAffordanceBackupHelper(
- context = this,
- userId = userHandle.identifier,
- ),
+ KeyguardQuickAffordanceBackupHelper(context = this, userId = userHandle.identifier),
+ )
+ addHelper(
+ QS_PREFERENCES_BACKUP_KEY,
+ QSPreferencesBackupHelper(context = this, userId = userHandle.identifier),
)
if (communalEnabled()) {
addHelper(
COMMUNAL_PREFS_BACKUP_KEY,
- CommunalPrefsBackupHelper(
- context = this,
- userId = userHandle.identifier,
- )
+ CommunalPrefsBackupHelper(context = this, userId = userHandle.identifier),
)
addHelper(
COMMUNAL_STATE_BACKUP_KEY,
@@ -110,10 +109,7 @@ open class BackupHelper : BackupAgentHelper() {
}
private fun addControlsHelper(userId: Int) {
- val file = UserFileManagerImpl.createFile(
- userId = userId,
- fileName = CONTROLS,
- )
+ val file = UserFileManagerImpl.createFile(userId = userId, fileName = CONTROLS)
// The map in mapOf is guaranteed to be order preserving
val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId))
NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also {
@@ -134,6 +130,7 @@ open class BackupHelper : BackupAgentHelper() {
* @property lock a lock to hold while backing up and restoring the files.
* @property context the context of the [BackupAgent]
* @property fileNamesAndPostProcess a map from the filenames to back up and the post processing
+ *
* ```
* actions to take
* ```
@@ -141,7 +138,7 @@ open class BackupHelper : BackupAgentHelper() {
private class NoOverwriteFileBackupHelper(
val lock: Any,
val context: Context,
- val fileNamesAndPostProcess: Map<String, () -> Unit>
+ val fileNamesAndPostProcess: Map<String, () -> Unit>,
) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) {
override fun restoreEntity(data: BackupDataInputStream) {
@@ -152,11 +149,12 @@ open class BackupHelper : BackupAgentHelper() {
return
}
synchronized(lock) {
- traceSection("File restore: ${data.key}") {
- super.restoreEntity(data)
- }
- Log.d(TAG, "Finishing restore for ${data.key} for user ${context.userId}. " +
- "Starting postProcess.")
+ traceSection("File restore: ${data.key}") { super.restoreEntity(data) }
+ Log.d(
+ TAG,
+ "Finishing restore for ${data.key} for user ${context.userId}. " +
+ "Starting postProcess.",
+ )
traceSection("Postprocess: ${data.key}") {
fileNamesAndPostProcess.get(data.key)?.invoke()
}
@@ -167,7 +165,7 @@ open class BackupHelper : BackupAgentHelper() {
override fun performBackup(
oldState: ParcelFileDescriptor?,
data: BackupDataOutput?,
- newState: ParcelFileDescriptor?
+ newState: ParcelFileDescriptor?,
) {
synchronized(lock) { super.performBackup(oldState, data, newState) }
}
@@ -176,15 +174,13 @@ open class BackupHelper : BackupAgentHelper() {
private fun getPPControlsFile(context: Context, userId: Int): () -> Unit {
return {
- val file = UserFileManagerImpl.createFile(
- userId = userId,
- fileName = BackupHelper.CONTROLS,
- )
+ val file = UserFileManagerImpl.createFile(userId = userId, fileName = BackupHelper.CONTROLS)
if (file.exists()) {
- val dest = UserFileManagerImpl.createFile(
- userId = userId,
- fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
- )
+ val dest =
+ UserFileManagerImpl.createFile(
+ userId = userId,
+ fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
+ )
file.copyTo(dest)
val jobScheduler = context.getSystemService(JobScheduler::class.java)
jobScheduler?.schedule(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 12b5fc01845c..b491c94db151 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -27,6 +27,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlertDialog;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -315,6 +316,16 @@ public class AuthContainerView extends LinearLayout
mBiometricCallback = new BiometricCallback();
mMSDLPlayer = msdlPlayer;
+ // Listener for when device locks from adaptive auth, dismiss prompt
+ getContext().getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
+ getContext().getMainExecutor(),
+ isKeyguardLocked -> {
+ if (isKeyguardLocked) {
+ onStartedGoingToSleep();
+ }
+ }
+ );
+
final BiometricModalities biometricModalities = new BiometricModalities(
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
index ba51d02fd288..68ec0f2d57ef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -25,6 +25,7 @@ import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import android.util.Log
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.biometrics.shared.model.toSensorStrength
@@ -91,13 +92,14 @@ constructor(
trySendWithFailureLogging(
DEFAULT_PROPS,
TAG,
- "no registered sensors, use default props"
+ "no registered sensors, use default props",
)
} else {
+ Log.d(TAG, "onAllAuthenticatorsRegistered $sensors")
trySendWithFailureLogging(
sensors[0],
TAG,
- "update properties on authenticators registered"
+ "update properties on authenticators registered",
)
}
}
@@ -160,7 +162,7 @@ constructor(
FingerprintSensorProperties.TYPE_UNKNOWN,
false /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
- listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT)
+ listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
private val DEFAULT_PROPS =
FingerprintSensorPropertiesInternal(
@@ -171,7 +173,7 @@ constructor(
FingerprintSensorProperties.TYPE_UNKNOWN,
false /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
- listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT)
+ listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
index b07006887011..08b3e99fadd0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
@@ -72,7 +72,7 @@ constructor(
// Request LockSettingsService to return the Gatekeeper Password in the
// VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
// Gatekeeper Password and operationId.
- var effectiveUserId = request.userInfo.userIdForPasswordEntry
+ var effectiveUserId = request.userInfo.deviceCredentialOwnerId
val response =
if (Flags.privateSpaceBp() && effectiveUserId != request.userInfo.userId) {
effectiveUserId = request.userInfo.userId
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index b8ff3bb43203..178e1112fa6f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.biometrics.shared.model.SensorLocation
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -42,7 +43,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val context: Context,
repository: FingerprintPropertyRepository,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
displayStateInteractor: DisplayStateInteractor,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
@@ -73,15 +74,12 @@ constructor(
* - device's natural orientation
*/
private val unscaledSensorLocation: Flow<SensorLocationInternal> =
- combine(
- repository.sensorLocations,
- uniqueDisplayId,
- ) { locations, displayId ->
+ combine(repository.sensorLocations, uniqueDisplayId) { locations, displayId ->
// Devices without multiple physical displays do not use the display id as the key;
// instead, the key is an empty string.
locations.getOrDefault(
displayId,
- locations.getOrDefault("", SensorLocationInternal.DEFAULT)
+ locations.getOrDefault("", SensorLocationInternal.DEFAULT),
)
}
@@ -92,16 +90,15 @@ constructor(
* - device's natural orientation
*/
val sensorLocation: Flow<SensorLocation> =
- combine(
+ combine(unscaledSensorLocation, configurationInteractor.scaleForResolution) {
unscaledSensorLocation,
- configurationInteractor.scaleForResolution,
- ) { unscaledSensorLocation, scale ->
+ scale ->
val sensorLocation =
SensorLocation(
naturalCenterX = unscaledSensorLocation.sensorLocationX,
naturalCenterY = unscaledSensorLocation.sensorLocationY,
naturalRadius = unscaledSensorLocation.sensorRadius,
- scale = scale
+ scale = scale,
)
sensorLocation
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index 18a7739f12ab..abbbd730c47e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -48,14 +48,14 @@ constructor(
private val authController: AuthController,
private val selectedUserInteractor: SelectedUserInteractor,
private val fingerprintManager: FingerprintManager?,
- @Application scope: CoroutineScope
+ @Application scope: CoroutineScope,
) {
private fun calculateIconSize(): Int {
val pixelPitch = context.resources.getFloat(R.dimen.pixel_pitch)
if (pixelPitch <= 0) {
Log.e(
"UdfpsOverlayInteractor",
- "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device."
+ "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device.",
)
}
return (context.resources.getFloat(R.dimen.udfps_icon_size) / pixelPitch).toInt()
@@ -83,12 +83,11 @@ constructor(
/** Sets whether Udfps overlay should handle touches */
fun setHandleTouches(shouldHandle: Boolean = true) {
- if (authController.isUdfpsSupported
- && shouldHandle != _shouldHandleTouches.value) {
+ if (authController.isUdfpsSupported && shouldHandle != _shouldHandleTouches.value) {
fingerprintManager?.setIgnoreDisplayTouches(
requestId.value,
authController.udfpsProps!!.get(0).sensorId,
- !shouldHandle
+ !shouldHandle,
)
}
_shouldHandleTouches.value = shouldHandle
@@ -107,10 +106,11 @@ constructor(
override fun onUdfpsLocationChanged(
udfpsOverlayParams: UdfpsOverlayParams
) {
+ Log.d(TAG, "udfpsOverlayParams updated $udfpsOverlayParams")
trySendWithFailureLogging(
udfpsOverlayParams,
TAG,
- "update udfpsOverlayParams"
+ "update udfpsOverlayParams",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index cbf783d66d42..df34952f4f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -34,6 +34,7 @@ import android.util.Log
import android.util.RotationUtils
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.AuthInteractionProperties
import com.android.launcher3.icons.IconProvider
import com.android.systemui.Flags.msdlFeedback
@@ -71,7 +72,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
-import com.android.app.tracing.coroutines.launchTraced as launch
/** ViewModel for BiometricPrompt. */
class PromptViewModel
@@ -973,7 +973,7 @@ constructor(
/**
* The order of getting logo icon/description is:
* 1. If the app sets customized icon/description, use the passed-in value
- * 2. If shouldShowLogoWithOverrides(), use activityInfo to get icon/description
+ * 2. If shouldUseActivityLogo(), use activityInfo to get icon/description
* 3. Otherwise, use applicationInfo to get icon/description
*/
private fun Context.getUserBadgedLogoInfo(
@@ -981,6 +981,7 @@ private fun Context.getUserBadgedLogoInfo(
iconProvider: IconProvider,
activityTaskManager: ActivityTaskManager,
): Pair<Drawable?, String> {
+ // If the app sets customized icon/description, use the passed-in value directly
var icon: Drawable? =
if (prompt.logoBitmap != null) BitmapDrawable(resources, prompt.logoBitmap) else null
var label = prompt.logoDescription ?: ""
@@ -993,36 +994,28 @@ private fun Context.getUserBadgedLogoInfo(
if (componentName != null && shouldUseActivityLogo(componentName)) {
val activityInfo = getActivityInfo(componentName)
if (activityInfo != null) {
- if (icon == null) {
- icon = iconProvider.getIcon(activityInfo)
- }
- if (label.isEmpty()) {
- label = activityInfo.loadLabel(packageManager).toString()
- }
+ icon = icon ?: iconProvider.getIcon(activityInfo)
+ label = label.ifEmpty { activityInfo.loadLabel(packageManager).toString() }
}
}
- if (icon != null && label.isNotEmpty()) {
- return Pair(icon, label)
- }
-
// Use applicationInfo for other cases
- val appInfo = prompt.getApplicationInfo(this, componentName)
- if (appInfo == null) {
- Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
- } else {
- if (icon == null) {
- icon = packageManager.getApplicationIcon(appInfo)
- }
- if (label.isEmpty()) {
- label =
- packageManager
- .getUserBadgedLabel(
- packageManager.getApplicationLabel(appInfo),
- UserHandle.of(userId),
- )
- .toString()
+ if (icon == null || label.isEmpty()) {
+ val appInfo = prompt.getApplicationInfo(this, componentName)
+ if (appInfo != null) {
+ icon = icon ?: packageManager.getApplicationIcon(appInfo)
+ label = label.ifEmpty { packageManager.getApplicationLabel(appInfo).toString() }
+ } else {
+ Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
}
}
+
+ // Add user badge
+ val userHandle = UserHandle.of(prompt.userInfo.userId)
+ if (label.isNotEmpty()) {
+ label = packageManager.getUserBadgedLabel(label, userHandle).toString()
+ }
+ icon = icon?.let { packageManager.getUserBadgedIcon(it, userHandle) }
+
return Pair(icon, label)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index e178c093b746..7039d5e7d9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.bouncer.domain.interactor
import android.app.StatusBarManager.SESSION_KEYGUARD
+import com.android.app.tracing.coroutines.asyncTraced as async
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.UiEventLogger
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
@@ -39,9 +40,9 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.asyncTraced as async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
@@ -65,7 +66,7 @@ constructor(
private val uiEventLogger: UiEventLogger,
private val sessionTracker: SessionTracker,
sceneBackInteractor: SceneBackInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
) {
private val _onIncorrectBouncerInput = MutableSharedFlow<Unit>()
val onIncorrectBouncerInput: SharedFlow<Unit> = _onIncorrectBouncerInput
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
index 4fe6fc69e8be..f50a2ab1d803 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.bouncer.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -33,16 +32,14 @@ import kotlinx.coroutines.flow.map
*/
class BouncerUserActionsViewModel
@AssistedInject
-constructor(
- private val bouncerInteractor: BouncerInteractor,
-) : UserActionsViewModel() {
+constructor(private val bouncerInteractor: BouncerInteractor) : UserActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
bouncerInteractor.dismissDestination
.map { prevScene ->
mapOf(
Back to UserActionResult(prevScene),
- Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
+ Swipe.Down to UserActionResult(prevScene),
)
}
.collect { actions -> setActions(actions) }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt
index 06d391704870..6c78b8b0e58a 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt
@@ -19,6 +19,7 @@ package com.android.systemui.brightness.data.repository
import android.annotation.SuppressLint
import android.hardware.display.BrightnessInfo
import android.hardware.display.DisplayManager
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.brightness.shared.model.BrightnessLog
import com.android.systemui.brightness.shared.model.LinearBrightness
import com.android.systemui.brightness.shared.model.formatBrightness
@@ -46,7 +47,6 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/**
@@ -64,6 +64,9 @@ interface ScreenBrightnessRepository {
/** Current maximum value for the brightness */
val maxLinearBrightness: Flow<LinearBrightness>
+ /** Whether the current brightness value is overridden by the application window */
+ val isBrightnessOverriddenByWindow: StateFlow<Boolean>
+
/** Gets the current values for min and max brightness */
suspend fun getMinMaxLinearBrightness(): Pair<LinearBrightness, LinearBrightness>
@@ -90,10 +93,7 @@ constructor(
@Background private val backgroundContext: CoroutineContext,
) : ScreenBrightnessRepository {
- private val apiQueue =
- Channel<SetBrightnessMethod>(
- capacity = UNLIMITED,
- )
+ private val apiQueue = Channel<SetBrightnessMethod>(capacity = UNLIMITED)
init {
applicationScope.launch(context = backgroundContext) {
@@ -132,7 +132,8 @@ constructor(
displayManager.registerDisplayListener(
listener,
null,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS,
+ /* eventFlags */ 0,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS,
)
awaitClose { displayManager.unregisterDisplayListener(listener) }
@@ -180,6 +181,11 @@ constructor(
.logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_BRIGHTNESS, null)
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(0f))
+ override val isBrightnessOverriddenByWindow = brightnessInfo
+ .filterNotNull()
+ .map { it.isBrightnessOverrideByWindow }
+ .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false)
+
override fun setTemporaryBrightness(value: LinearBrightness) {
apiQueue.trySend(SetBrightnessMethod.Temporary(value))
}
@@ -190,8 +196,10 @@ constructor(
private sealed interface SetBrightnessMethod {
val value: LinearBrightness
+
@JvmInline
value class Temporary(override val value: LinearBrightness) : SetBrightnessMethod
+
@JvmInline
value class Permanent(override val value: LinearBrightness) : SetBrightnessMethod
}
@@ -201,7 +209,7 @@ constructor(
LOG_BUFFER_BRIGHTNESS_CHANGE_TAG,
if (permanent) LogLevel.DEBUG else LogLevel.VERBOSE,
{ str1 = value.formatBrightness() },
- { "Change requested: $str1" }
+ { "Change requested: $str1" },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt b/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt
index 5647f521762f..b794c5c85645 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt
@@ -25,12 +25,12 @@ import com.android.systemui.brightness.shared.model.logDiffForTable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
/**
* Converts between [GammaBrightness] and [LinearBrightness].
@@ -68,6 +68,8 @@ constructor(
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), GammaBrightness(0))
}
+ val brightnessOverriddenByWindow = screenBrightnessRepository.isBrightnessOverriddenByWindow
+
/** Sets the brightness temporarily, while the user is changing it. */
suspend fun setTemporaryBrightness(gammaBrightness: GammaBrightness) {
screenBrightnessRepository.setTemporaryBrightness(
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index 02161d255abc..917a4ff9036b 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -19,6 +19,7 @@ package com.android.systemui.brightness.ui.compose
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.DragInteraction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
@@ -27,8 +28,10 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
@@ -38,6 +41,7 @@ import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -62,6 +66,7 @@ import com.android.systemui.utils.PolicyRestriction
@Composable
private fun BrightnessSlider(
+ viewModel: BrightnessSliderViewModel,
gammaValue: Int,
valueRange: IntRange,
label: Text.Resource,
@@ -97,21 +102,31 @@ private fun BrightnessSlider(
null
}
+ val overriddenByAppState by if (Flags.showToastWhenAppControlBrightness()) {
+ viewModel.brightnessOverriddenByWindow.collectAsStateWithLifecycle()
+ } else {
+ mutableStateOf(false)
+ }
+
PlatformSlider(
value = animatedValue,
valueRange = floatValueRange,
enabled = !isRestricted,
onValueChange = {
if (!isRestricted) {
- hapticsViewModel?.onValueChange(it)
- value = it.toInt()
- onDrag(value)
+ if (!overriddenByAppState) {
+ hapticsViewModel?.onValueChange(it)
+ value = it.toInt()
+ onDrag(value)
+ }
}
},
onValueChangeFinished = {
if (!isRestricted) {
- hapticsViewModel?.onValueChangeEnded()
- onStop(value)
+ if (!overriddenByAppState) {
+ hapticsViewModel?.onValueChangeEnded()
+ onStop(value)
+ }
}
},
modifier =
@@ -136,6 +151,21 @@ private fun BrightnessSlider(
},
interactionSource = interactionSource,
)
+ // Showing the warning toast if the current running app window has controlled the
+ // brightness value.
+ if (Flags.showToastWhenAppControlBrightness()) {
+ val context = LocalContext.current
+ LaunchedEffect(interactionSource) {
+ interactionSource.interactions.collect { interaction ->
+ if (interaction is DragInteraction.Start && overriddenByAppState) {
+ viewModel.showToast(
+ context,
+ R.string.quick_settings_brightness_unable_adjust_msg
+ )
+ }
+ }
+ }
+ }
}
private val sliderBackgroundFrameSize = 8.dp
@@ -167,6 +197,7 @@ fun BrightnessSliderContainer(
Box(modifier = modifier.fillMaxWidth().sysuiResTag("brightness_slider")) {
BrightnessSlider(
+ viewModel = viewModel,
gammaValue = gamma,
valueRange = viewModel.minBrightness.value..viewModel.maxBrightness.value,
label = viewModel.label,
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
index a61ce8f62522..1630ee58449f 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
@@ -17,7 +17,8 @@
package com.android.systemui.brightness.ui.viewmodel
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
+import android.content.Context
+import androidx.annotation.StringRes
import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor
import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor
import com.android.systemui.brightness.shared.model.GammaBrightness
@@ -29,6 +30,7 @@ import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.res.R
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
+import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
import com.android.systemui.utils.PolicyRestriction
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -51,6 +53,7 @@ constructor(
val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor,
@Assisted private val supportsMirroring: Boolean,
+ private val brightnessWarningToast: BrightnessWarningToast,
) : ExclusiveActivatable() {
private val hydrator = Hydrator("BrightnessSliderViewModel.hydrator")
@@ -75,6 +78,15 @@ constructor(
brightnessPolicyEnforcementInteractor.startAdminSupportDetailsDialog(restriction)
}
+ val brightnessOverriddenByWindow = screenBrightnessInteractor.brightnessOverriddenByWindow
+
+ fun showToast(viewContext: Context, @StringRes resId: Int) {
+ if (brightnessWarningToast.isToastActive()) {
+ return
+ }
+ brightnessWarningToast.show(viewContext, resId)
+ }
+
/**
* As a brightness slider is dragged, the corresponding events should be sent using this method.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt
new file mode 100644
index 000000000000..ddd6bc9ef16b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay
+
+/** Interface for listening to indication text changed from [ClipboardIndicationProvider]. */
+interface ClipboardIndicationCallback {
+
+ /** Notifies the indication text changed. */
+ fun onIndicationTextChanged(text: CharSequence)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt
new file mode 100644
index 000000000000..be3272369d46
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay
+
+/** Interface to provide the clipboard indication to be shown under the overlay. */
+interface ClipboardIndicationProvider {
+
+ /**
+ * Gets the indication text async.
+ *
+ * @param callback callback for getting the indication text.
+ */
+ fun getIndicationText(callback: ClipboardIndicationCallback)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt
index 03bc9350674b..da94d5b518b7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer
+package com.android.systemui.clipboardoverlay
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import javax.inject.Inject
-/** Flags related to media tap-to-transfer. */
@SysUISingleton
-class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags) {
- /** */
- fun isMediaTttEnabled(): Boolean = featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER)
+open class ClipboardIndicationProviderImpl @Inject constructor() : ClipboardIndicationProvider {
+
+ override fun getIndicationText(callback: ClipboardIndicationCallback) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 65c01ed9eecd..ac747845267c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
import static com.android.systemui.Flags.clipboardImageTimeout;
import static com.android.systemui.Flags.clipboardSharedTransitions;
+import static com.android.systemui.Flags.showClipboardIndication;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
@@ -99,6 +100,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private final ClipboardTransitionExecutor mTransitionExecutor;
private final ClipboardOverlayView mView;
+ private final ClipboardIndicationProvider mClipboardIndicationProvider;
private Runnable mOnSessionCompleteListener;
private Runnable mOnRemoteCopyTapped;
@@ -173,6 +175,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
}
};
+ private ClipboardIndicationCallback mIndicationCallback = new ClipboardIndicationCallback() {
+ @Override
+ public void onIndicationTextChanged(@NonNull CharSequence text) {
+ mView.setIndicationText(text);
+ }
+ };
+
@Inject
public ClipboardOverlayController(@OverlayWindowContext Context context,
ClipboardOverlayView clipboardOverlayView,
@@ -185,11 +194,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
@Background Executor bgExecutor,
ClipboardImageLoader clipboardImageLoader,
ClipboardTransitionExecutor transitionExecutor,
+ ClipboardIndicationProvider clipboardIndicationProvider,
UiEventLogger uiEventLogger) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mClipboardImageLoader = clipboardImageLoader;
mTransitionExecutor = transitionExecutor;
+ mClipboardIndicationProvider = clipboardIndicationProvider;
mClipboardLogger = new ClipboardLogger(uiEventLogger);
@@ -288,6 +299,9 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
mClipboardModel = model;
mClipboardLogger.setClipSource(mClipboardModel.getSource());
+ if (showClipboardIndication()) {
+ mClipboardIndicationProvider.getIndicationText(mIndicationCallback);
+ }
if (clipboardImageTimeout()) {
if (shouldAnimate) {
reset();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 1762d82b3237..7e4d76280909 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -18,6 +18,8 @@ package com.android.systemui.clipboardoverlay;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.systemui.Flags.showClipboardIndication;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -53,6 +55,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -103,6 +106,8 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
private View mShareChip;
private View mRemoteCopyChip;
private View mActionContainerBackground;
+ private View mIndicationContainer;
+ private TextView mIndicationText;
private View mDismissButton;
private LinearLayout mActionContainer;
private ClipboardOverlayCallbacks mClipboardCallbacks;
@@ -136,6 +141,8 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
mShareChip = requireViewById(R.id.share_chip);
mRemoteCopyChip = requireViewById(R.id.remote_copy_chip);
mDismissButton = requireViewById(R.id.dismiss_button);
+ mIndicationContainer = requireViewById(R.id.indication_container);
+ mIndicationText = mIndicationContainer.findViewById(R.id.indication_text);
bindDefaultActionChips();
@@ -208,6 +215,14 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
}
}
+ void setIndicationText(CharSequence text) {
+ mIndicationText.setText(text);
+
+ // Set the visibility of clipboard indication based on the text is empty or not.
+ int visibility = text.isEmpty() ? View.GONE : View.VISIBLE;
+ mIndicationContainer.setVisibility(visibility);
+ }
+
void setMinimized(boolean minimized) {
if (minimized) {
mMinimizedPreview.setVisibility(View.VISIBLE);
@@ -221,6 +236,18 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
mPreviewBorder.setVisibility(View.VISIBLE);
mActionContainer.setVisibility(View.VISIBLE);
}
+
+ if (showClipboardIndication()) {
+ // Adjust the margin of clipboard indication based on the minimized state.
+ int marginStart = minimized ? getResources().getDimensionPixelSize(
+ R.dimen.overlay_action_container_margin_horizontal)
+ : getResources().getDimensionPixelSize(
+ R.dimen.overlay_action_container_minimum_edge_spacing);
+ ConstraintLayout.LayoutParams params =
+ (ConstraintLayout.LayoutParams) mIndicationContainer.getLayoutParams();
+ params.setMarginStart(marginStart);
+ mIndicationContainer.setLayoutParams(params);
+ }
}
void setInsets(WindowInsets insets, int orientation) {
@@ -313,6 +340,7 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
setTranslationX(0);
setAlpha(0);
mActionContainerBackground.setVisibility(View.GONE);
+ mIndicationContainer.setVisibility(View.GONE);
mDismissButton.setVisibility(View.GONE);
mShareChip.setVisibility(View.GONE);
mRemoteCopyChip.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
index 527819c73e2f..c81f0d98f3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
@@ -15,18 +15,26 @@
*/
package com.android.systemui.clipboardoverlay.dagger
+import com.android.systemui.clipboardoverlay.ClipboardIndicationProvider
+import com.android.systemui.clipboardoverlay.ClipboardIndicationProviderImpl
import com.android.systemui.clipboardoverlay.ClipboardOverlaySuppressionController
import com.android.systemui.clipboardoverlay.ClipboardOverlaySuppressionControllerImpl
import dagger.Binds
import dagger.Module
-/** Dagger Module for code in the clipboard overlay package. */
+/** Dagger Module to provide default implementations which could be overridden. */
@Module
-interface ClipboardOverlaySuppressionModule {
+interface ClipboardOverlayOverrideModule {
/** Provides implementation for [ClipboardOverlaySuppressionController]. */
@Binds
fun provideClipboardOverlaySuppressionController(
clipboardOverlaySuppressionControllerImpl: ClipboardOverlaySuppressionControllerImpl
): ClipboardOverlaySuppressionController
+
+ /** Provides implementation for [ClipboardIndicationProvider]. */
+ @Binds
+ fun provideClipboardIndicationProvider(
+ clipboardIndicationProviderImpl: ClipboardIndicationProviderImpl
+ ): ClipboardIndicationProvider
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
index d1c728cd74fa..19238804fb12 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -18,19 +18,12 @@ package com.android.systemui.common.data
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.data.repository.PackageChangeRepositoryImpl
-import com.android.systemui.common.ui.data.repository.ConfigurationRepository
-import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
import dagger.Binds
import dagger.Module
@Module
abstract class CommonDataLayerModule {
@Binds
- abstract fun bindConfigurationRepository(
- impl: ConfigurationRepositoryImpl
- ): ConfigurationRepository
-
- @Binds
abstract fun bindPackageChangeRepository(
impl: PackageChangeRepositoryImpl
): PackageChangeRepository
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt
index b36da3bfcd26..7f50e4a06022 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationModule.kt
@@ -17,6 +17,9 @@
package com.android.systemui.common.ui
import android.content.Context
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -28,24 +31,31 @@ import javax.inject.Qualifier
/**
* Annotates elements that provide information from the global configuration.
*
- * The global configuration is the one associted with the main display. Secondary displays will
+ * The global configuration is the one associated with the main display. Secondary displays will
* apply override to the global configuration. Elements annotated with this shouldn't be used for
* secondary displays.
*/
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalConfig
@Module
-interface ConfigurationStateModule {
+interface ConfigurationModule {
/**
* Deprecated: [ConfigurationState] should be injected only with the correct annotation. For
* now, without annotation the global config associated state is provided.
*/
@Binds
+ @Deprecated("Use the @GlobalConfig annotated one instead of this.")
fun provideGlobalConfigurationState(
@GlobalConfig configurationState: ConfigurationState
): ConfigurationState
+ @Binds
+ @Deprecated("Use the @GlobalConfig annotated one instead of this.")
+ fun provideDefaultConfigurationState(
+ @GlobalConfig configurationState: ConfigurationInteractor
+ ): ConfigurationInteractor
+
companion object {
@SysUISingleton
@Provides
@@ -57,5 +67,14 @@ interface ConfigurationStateModule {
): ConfigurationState {
return configStateFactory.create(context, configurationController)
}
+
+ @SysUISingleton
+ @Provides
+ @GlobalConfig
+ fun provideGlobalConfigurationInteractor(
+ configurationRepository: ConfigurationRepository
+ ): ConfigurationInteractor {
+ return ConfigurationInteractorImpl(configurationRepository)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index 2052c70e740d..df891523798f 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -23,13 +23,17 @@ import android.view.DisplayInfo
import androidx.annotation.DimenRes
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.wrapper.DisplayUtilsWrapper
import dagger.Binds
import dagger.Module
-import javax.inject.Inject
+import dagger.Provides
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -57,66 +61,62 @@ interface ConfigurationRepository {
fun getDimensionPixelSize(id: Int): Int
}
-@SysUISingleton
class ConfigurationRepositoryImpl
-@Inject
+@AssistedInject
constructor(
- private val configurationController: ConfigurationController,
- private val context: Context,
+ @Assisted private val configurationController: ConfigurationController,
+ @Assisted private val context: Context,
@Application private val scope: CoroutineScope,
private val displayUtils: DisplayUtilsWrapper,
) : ConfigurationRepository {
private val displayInfo = MutableStateFlow(DisplayInfo())
- override val onAnyConfigurationChange: Flow<Unit> =
- conflatedCallbackFlow {
- val callback =
- object : ConfigurationController.ConfigurationListener {
- override fun onUiModeChanged() {
- sendUpdate("ConfigurationRepository#onUiModeChanged")
- }
-
- override fun onThemeChanged() {
- sendUpdate("ConfigurationRepository#onThemeChanged")
- }
-
- override fun onConfigChanged(newConfig: Configuration) {
- sendUpdate("ConfigurationRepository#onConfigChanged")
- }
-
- fun sendUpdate(reason: String) {
- trySendWithFailureLogging(Unit, reason)
- }
+ override val onAnyConfigurationChange: Flow<Unit> = conflatedCallbackFlow {
+ val callback =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onUiModeChanged() {
+ sendUpdate("ConfigurationRepository#onUiModeChanged")
}
- configurationController.addCallback(callback)
- awaitClose { configurationController.removeCallback(callback) }
- }
- override val onConfigurationChange: Flow<Unit> =
- conflatedCallbackFlow {
- val callback =
- object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration) {
- trySendWithFailureLogging(Unit, "ConfigurationRepository#onConfigChanged")
- }
+ override fun onThemeChanged() {
+ sendUpdate("ConfigurationRepository#onThemeChanged")
}
- configurationController.addCallback(callback)
- awaitClose { configurationController.removeCallback(callback) }
- }
- override val configurationValues: Flow<Configuration> =
- conflatedCallbackFlow {
- val callback =
- object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration) {
- trySend(newConfig)
- }
- }
-
- trySend(context.resources.configuration)
- configurationController.addCallback(callback)
- awaitClose { configurationController.removeCallback(callback) }
+ override fun onConfigChanged(newConfig: Configuration) {
+ sendUpdate("ConfigurationRepository#onConfigChanged")
+ }
+
+ fun sendUpdate(reason: String) {
+ trySendWithFailureLogging(Unit, reason)
+ }
+ }
+ configurationController.addCallback(callback)
+ awaitClose { configurationController.removeCallback(callback) }
+ }
+
+ override val onConfigurationChange: Flow<Unit> = conflatedCallbackFlow {
+ val callback =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration) {
+ trySendWithFailureLogging(Unit, "ConfigurationRepository#onConfigChanged")
+ }
}
+ configurationController.addCallback(callback)
+ awaitClose { configurationController.removeCallback(callback) }
+ }
+
+ override val configurationValues: Flow<Configuration> = conflatedCallbackFlow {
+ val callback =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration) {
+ trySend(newConfig)
+ }
+ }
+
+ trySend(context.resources.configuration)
+ configurationController.addCallback(callback)
+ awaitClose { configurationController.removeCallback(callback) }
+ }
override val scaleForResolution: StateFlow<Float> =
onConfigurationChange
@@ -134,7 +134,7 @@ constructor(
maxDisplayMode.physicalWidth,
maxDisplayMode.physicalHeight,
displayInfo.value.naturalWidth,
- displayInfo.value.naturalHeight
+ displayInfo.value.naturalHeight,
)
return if (scaleFactor == Float.POSITIVE_INFINITY) 1f else scaleFactor
}
@@ -144,9 +144,40 @@ constructor(
override fun getDimensionPixelSize(@DimenRes id: Int): Int {
return context.resources.getDimensionPixelSize(id)
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ context: Context,
+ configurationController: ConfigurationController,
+ ): ConfigurationRepositoryImpl
+ }
}
@Module
-interface ConfigurationRepositoryModule {
- @Binds fun bindImpl(impl: ConfigurationRepositoryImpl): ConfigurationRepository
+abstract class ConfigurationRepositoryModule {
+
+ /**
+ * For compatibility reasons. Ideally, only the an annotated [ConfigurationRepository] should be
+ * injected.
+ */
+ @Binds
+ @Deprecated("Use the ConfigurationRepository annotated with @GlobalConfig instead.")
+ @SysUISingleton
+ abstract fun provideDefaultConfigRepository(
+ @GlobalConfig configurationRepository: ConfigurationRepository
+ ): ConfigurationRepository
+
+ companion object {
+ @Provides
+ @GlobalConfig
+ @SysUISingleton
+ fun provideGlobalConfigRepository(
+ context: Context,
+ @GlobalConfig configurationController: ConfigurationController,
+ factory: ConfigurationRepositoryImpl.Factory,
+ ): ConfigurationRepository {
+ return factory.create(context, configurationController)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
index adb1ee2b22ee..97a23e1a5010 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
@@ -21,8 +21,6 @@ import android.content.res.Configuration
import android.graphics.Rect
import android.view.Surface
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -32,13 +30,52 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
/** Business logic related to configuration changes. */
-@SysUISingleton
-class ConfigurationInteractor @Inject constructor(private val repository: ConfigurationRepository) {
+interface ConfigurationInteractor {
/**
* Returns screen size adjusted to rotation, so returned screen size is stable across all
* rotations
*/
- private val Configuration.naturalScreenBounds: Rect
+ val Configuration.naturalScreenBounds: Rect
+
+ /** Returns the unadjusted screen size. */
+ val maxBounds: Flow<Rect>
+
+ /**
+ * Returns screen size adjusted to rotation, so returned screen sizes are stable across all
+ * rotations, could be useful if you need to react to screen resize (e.g. fold/unfold on
+ * foldable devices)
+ */
+ val naturalMaxBounds: Flow<Rect>
+
+ /**
+ * The layout direction. Will be either `View#LAYOUT_DIRECTION_LTR` or
+ * `View#LAYOUT_DIRECTION_RTL`.
+ */
+ val layoutDirection: Flow<Int>
+
+ /** Emit an event on any config change */
+ val onAnyConfigurationChange: Flow<Unit>
+
+ /** Emits the new configuration on any configuration change */
+ val configurationValues: Flow<Configuration>
+
+ /** Emits the current resolution scaling factor */
+ val scaleForResolution: Flow<Float>
+
+ /** Given [resourceId], emit the dimension pixel size on config change */
+ fun dimensionPixelSize(resourceId: Int): Flow<Int>
+
+ /** Emits the dimensional pixel size of the given resource, inverting it for RTL if necessary */
+ fun directionalDimensionPixelSize(originLayoutDirection: Int, resourceId: Int): Flow<Int>
+
+ /** Given a set of [resourceId]s, emit Map<ResourceId, DimensionPixelSize> on config change */
+ fun dimensionPixelSize(resourceIds: Set<Int>): Flow<Map<Int, Int>>
+}
+
+class ConfigurationInteractorImpl(private val repository: ConfigurationRepository) :
+ ConfigurationInteractor {
+
+ override val Configuration.naturalScreenBounds: Rect
get() {
val rotation = windowConfiguration.displayRotation
val maxBounds = windowConfiguration.maxBounds
@@ -49,53 +86,40 @@ class ConfigurationInteractor @Inject constructor(private val repository: Config
}
}
- /** Returns the unadjusted screen size. */
- val maxBounds: Flow<Rect> =
+ override val maxBounds: Flow<Rect> =
repository.configurationValues
.map { Rect(it.windowConfiguration.maxBounds) }
.distinctUntilChanged()
- /**
- * Returns screen size adjusted to rotation, so returned screen sizes are stable across all
- * rotations, could be useful if you need to react to screen resize (e.g. fold/unfold on
- * foldable devices)
- */
- val naturalMaxBounds: Flow<Rect> =
+ override val naturalMaxBounds: Flow<Rect> =
repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
- /**
- * The layout direction. Will be either `View#LAYOUT_DIRECTION_LTR` or
- * `View#LAYOUT_DIRECTION_RTL`.
- */
- val layoutDirection: Flow<Int> =
+ override val layoutDirection: Flow<Int> =
repository.configurationValues.map { it.layoutDirection }.distinctUntilChanged()
- /** Given [resourceId], emit the dimension pixel size on config change */
- fun dimensionPixelSize(resourceId: Int): Flow<Int> {
+ override fun dimensionPixelSize(resourceId: Int): Flow<Int> {
return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) }
}
- /** Emits the dimensional pixel size of the given resource, inverting it for RTL if necessary */
- fun directionalDimensionPixelSize(originLayoutDirection: Int, resourceId: Int): Flow<Int> {
+ override fun directionalDimensionPixelSize(
+ originLayoutDirection: Int,
+ resourceId: Int,
+ ): Flow<Int> {
return dimensionPixelSize(resourceId).combine(layoutDirection) { size, direction ->
if (originLayoutDirection == direction) size else -size
}
}
- /** Given a set of [resourceId]s, emit Map<ResourceId, DimensionPixelSize> on config change */
- fun dimensionPixelSize(resourceIds: Set<Int>): Flow<Map<Int, Int>> {
+ override fun dimensionPixelSize(resourceIds: Set<Int>): Flow<Map<Int, Int>> {
return onAnyConfigurationChange.mapLatest {
resourceIds.associateWith { repository.getDimensionPixelSize(it) }
}
}
- /** Emit an event on any config change */
- val onAnyConfigurationChange: Flow<Unit> =
+ override val onAnyConfigurationChange: Flow<Unit> =
repository.onAnyConfigurationChange.onStart { emit(Unit) }
- /** Emits the new configuration on any configuration change */
- val configurationValues: Flow<Configuration> = repository.configurationValues
+ override val configurationValues: Flow<Configuration> = repository.configurationValues
- /** Emits the current resolution scaling factor */
- val scaleForResolution: Flow<Float> = repository.scaleForResolution
+ override val scaleForResolution: Flow<Float> = repository.scaleForResolution
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
index 7cfad60b84cd..6423f8f7783b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
@@ -30,6 +30,7 @@ constructor(
@Named(LOGGABLE_PREFIXES) private val loggablePrefixes: List<String>,
private val statsLogProxy: StatsLogProxy,
) {
+
/** Logs an add widget event for metrics. No-op if widget is not loggable. */
fun logAddWidget(componentName: String, rank: Int?) {
if (!componentName.isLoggable()) {
@@ -69,11 +70,21 @@ constructor(
)
}
+ fun logResizeWidget(componentName: String, rank: Int, spanY: Int = 0) {
+ if (!componentName.isLoggable()) {
+ return
+ }
+
+ statsLogProxy.writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+ componentName,
+ rank = rank,
+ spanY = spanY,
+ )
+ }
+
/** Logs loggable widgets and the total widget count as a [StatsEvent]. */
- fun logWidgetsSnapshot(
- statsEvents: MutableList<StatsEvent>,
- componentNames: List<String>,
- ) {
+ fun logWidgetsSnapshot(statsEvents: MutableList<StatsEvent>, componentNames: List<String>) {
val loggableComponentNames = componentNames.filter { it.isLoggable() }.toTypedArray()
statsEvents.add(
statsLogProxy.buildCommunalHubSnapshotStatsEvent(
@@ -95,6 +106,7 @@ constructor(
action: Int,
componentName: String,
rank: Int,
+ spanY: Int = 0,
)
/** Builds a [SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT] stats event. */
@@ -112,12 +124,14 @@ class CommunalStatsLogProxyImpl @Inject constructor() : CommunalMetricsLogger.St
action: Int,
componentName: String,
rank: Int,
+ spanY: Int,
) {
SysUiStatsLog.write(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED,
action,
componentName,
rank,
+ spanY,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
index 012c844586bc..b80e77ce5b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
@@ -19,13 +19,13 @@ package com.android.systemui.communal.smartspace
import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession
-import android.content.Context
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB
+import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN
@@ -42,8 +42,7 @@ import javax.inject.Named
class CommunalSmartspaceController
@Inject
constructor(
- private val context: Context,
- private val smartspaceManager: SmartspaceManager?,
+ private val userTracker: UserTracker,
private val execution: Execution,
@Main private val uiExecutor: Executor,
@Named(LOCKSCREEN_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition,
@@ -55,6 +54,7 @@ constructor(
private const val TAG = "CommunalSmartspaceCtrlr"
}
+ private var userSmartspaceManager: SmartspaceManager? = null
private var session: SmartspaceSession? = null
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null)
@@ -104,7 +104,11 @@ constructor(
}
private fun connectSession() {
- if (smartspaceManager == null) {
+ if (userSmartspaceManager == null) {
+ userSmartspaceManager =
+ userTracker.userContext.getSystemService(SmartspaceManager::class.java)
+ }
+ if (userSmartspaceManager == null) {
return
}
if (plugin == null) {
@@ -119,11 +123,11 @@ constructor(
}
val newSession =
- smartspaceManager.createSmartspaceSession(
- SmartspaceConfig.Builder(context, UI_SURFACE_GLANCEABLE_HUB).build()
+ userSmartspaceManager?.createSmartspaceSession(
+ SmartspaceConfig.Builder(userTracker.userContext, UI_SURFACE_GLANCEABLE_HUB).build()
)
Log.d(TAG, "Starting smartspace session for communal")
- newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+ newSession?.addOnTargetsAvailableListener(uiExecutor, sessionListener)
this.session = newSession
plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
@@ -163,7 +167,7 @@ constructor(
private fun addAndRegisterListener(
listener: SmartspaceTargetListener,
- smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?,
) {
execution.assertIsMainThread()
smartspaceDataPlugin?.registerListener(listener)
@@ -174,7 +178,7 @@ constructor(
private fun removeAndUnregisterListener(
listener: SmartspaceTargetListener,
- smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?,
) {
execution.assertIsMainThread()
smartspaceDataPlugin?.unregisterListener(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index e25ea4cbfb25..4e0e11226faa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -178,7 +178,13 @@ abstract class BaseCommunalViewModel(
* alongside the resize, in case resizing also requires re-ordering. This ensures the
* re-ordering is done in the same database transaction as the resize.
*/
- open fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {}
+ open fun onResizeWidget(
+ appWidgetId: Int,
+ spanY: Int,
+ widgetIdToRankMap: Map<Int, Int>,
+ componentName: ComponentName,
+ rank: Int,
+ ) {}
/** Called as the UI requests opening the widget editor with an optional preselected widget. */
open fun onOpenWidgetEditor(shouldOpenWidgetPickerOnStart: Boolean = false) {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 6508e4b574a3..ccff23003aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -141,8 +141,19 @@ constructor(
override fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) =
communalInteractor.updateWidgetOrder(widgetIdToRankMap)
- override fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+ override fun onResizeWidget(
+ appWidgetId: Int,
+ spanY: Int,
+ widgetIdToRankMap: Map<Int, Int>,
+ componentName: ComponentName,
+ rank: Int,
+ ) {
communalInteractor.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+ metricsLogger.logResizeWidget(
+ componentName = componentName.flattenToString(),
+ rank = rank,
+ spanY = spanY,
+ )
}
override fun onReorderWidgetStart() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index ca4bbc0ae3bd..00c62092421d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -33,6 +33,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags.communalEditWidgetsActivityFinishFix
@@ -44,6 +45,7 @@ import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -52,13 +54,13 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlinx.coroutines.flow.first
-import com.android.app.tracing.coroutines.launchTraced as launch
/** An Activity for editing the widgets that appear in hub mode. */
class EditWidgetsActivity
@Inject
constructor(
private val communalViewModel: CommunalEditModeViewModel,
+ private val keyguardInteractor: KeyguardInteractor,
private var windowManagerService: IWindowManager? = null,
private val uiEventLogger: UiEventLogger,
private val widgetConfiguratorFactory: WidgetConfigurationController.Factory,
@@ -223,6 +225,9 @@ constructor(
communalViewModel.currentScene.first { it == CommunalScenes.Blank }
}
+ // Wait for dream to exit, if we were previously dreaming.
+ keyguardInteractor.isDreaming.first { !it }
+
communalViewModel.setEditModeState(EditModeState.SHOWING)
// Inform the ActivityController that we are now fully visible.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
index 202edf71fa11..2f686fd91ede 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
@@ -19,7 +19,10 @@ package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetHost.AppWidgetHostListener
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
+import android.content.IntentSender
import android.os.IBinder
+import android.os.OutcomeReceiver
+import android.os.RemoteException
import android.os.UserHandle
import android.widget.RemoteViews
import com.android.server.servicewatcher.ServiceWatcher
@@ -27,14 +30,19 @@ import com.android.server.servicewatcher.ServiceWatcher.ServiceListener
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IConfigureWidgetCallback
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.launch
/**
* Manages updates to Glanceable Hub widgets and requests to edit them from the headless system
@@ -47,6 +55,8 @@ import kotlinx.coroutines.channels.awaitClose
class GlanceableHubWidgetManager
@Inject
constructor(
+ @Background private val bgExecutor: Executor,
+ @Background private val bgScope: CoroutineScope,
glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
@CommunalLog logBuffer: LogBuffer,
serviceWatcherFactory: ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?>,
@@ -101,8 +111,7 @@ constructor(
rank: Int?,
configurator: WidgetConfigurator?,
) = runOnService { service ->
- // TODO(b/375036327): Add support for widget configuration
- service.addWidget(provider, user, rank ?: -1)
+ service.addWidget(provider, user, rank ?: -1, createIConfigureWidgetCallback(configurator))
}
/** Requests the foreground user to delete a widget. */
@@ -129,18 +138,54 @@ constructor(
)
}
- private fun runOnService(block: (IGlanceableHubWidgetManagerService) -> Unit) {
- serviceWatcher.runOnBinder(
- object : ServiceWatcher.BinderOperation {
- override fun run(binder: IBinder?) {
- block(IGlanceableHubWidgetManagerService.Stub.asInterface(binder))
+ /**
+ * Requests the foreground user for the [IntentSender] to start a configuration activity for the
+ * widget.
+ *
+ * @param appWidgetId Id of the widget to configure.
+ * @param outcomeReceiver Callback for receiving the result or error.
+ * @param executor Executor to run the callback on.
+ */
+ fun getIntentSenderForConfigureActivity(
+ appWidgetId: Int,
+ outcomeReceiver: OutcomeReceiver<IntentSender?, Throwable>,
+ executor: Executor,
+ ) {
+ bgExecutor.execute {
+ serviceWatcher.runOnBinder(
+ object : ServiceWatcher.BinderOperation {
+ override fun run(binder: IBinder?) {
+ val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+ try {
+ val result = service.getIntentSenderForConfigureActivity(appWidgetId)
+ executor.execute { outcomeReceiver.onResult(result) }
+ } catch (e: RemoteException) {
+ executor.execute { outcomeReceiver.onError(e) }
+ }
+ }
+
+ override fun onError(t: Throwable?) {
+ t?.let { executor.execute { outcomeReceiver.onError(t) } }
+ }
}
+ )
+ }
+ }
- override fun onError(t: Throwable?) {
- // TODO(b/375236794): handle failure in case service is unbound
+ private fun runOnService(block: (IGlanceableHubWidgetManagerService) -> Unit) {
+ bgExecutor.execute {
+ serviceWatcher.runOnBinder(
+ object : ServiceWatcher.BinderOperation {
+ override fun run(binder: IBinder?) {
+ block(IGlanceableHubWidgetManagerService.Stub.asInterface(binder))
+ }
+
+ override fun onError(t: Throwable?) {
+ // TODO(b/375236794): handle failure in case service is unbound
+ }
}
- }
- )
+ )
+ }
}
private fun createIAppWidgetHostListener(
@@ -165,6 +210,30 @@ constructor(
}
}
+ private fun createIConfigureWidgetCallback(
+ configurator: WidgetConfigurator?
+ ): IConfigureWidgetCallback? {
+ return configurator?.let {
+ object : IConfigureWidgetCallback.Stub() {
+ override fun onConfigureWidget(
+ appWidgetId: Int,
+ resultReceiver: IConfigureWidgetCallback.IResultReceiver?,
+ ) {
+ bgScope.launch {
+ val success = configurator.configureWidget(appWidgetId)
+ try {
+ resultReceiver?.onResult(success)
+ } catch (e: RemoteException) {
+ logger.e({ "Error reporting widget configuration result: $str1" }) {
+ str1 = e.localizedMessage
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
companion object {
private const val TAG = "GlanceableHubWidgetManager"
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
index 4d042fc277de..0e43e2aa4c14 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
@@ -20,8 +20,10 @@ import android.appwidget.AppWidgetHost.AppWidgetHostListener
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.Intent
+import android.content.IntentSender
import android.os.IBinder
import android.os.RemoteCallbackList
+import android.os.RemoteException
import android.os.UserHandle
import android.widget.RemoteViews
import androidx.lifecycle.LifecycleService
@@ -29,11 +31,13 @@ import androidx.lifecycle.lifecycleScope
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IConfigureWidgetCallback
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import javax.inject.Inject
+import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -98,7 +102,15 @@ constructor(
val job =
widgetRepository.communalWidgets
- .onEach { widgets -> listener.onWidgetsUpdated(widgets) }
+ .onEach { widgets ->
+ try {
+ listener.onWidgetsUpdated(widgets)
+ } catch (e: RemoteException) {
+ logger.e({ "Error pushing widget update: $str1" }) {
+ str1 = e.localizedMessage
+ }
+ }
+ }
.launchIn(lifecycleScope)
widgetListenersRegistry.register(listener, job)
}
@@ -122,7 +134,12 @@ constructor(
appWidgetHost.setListener(appWidgetId, createListener(listener))
}
- private fun addWidgetInternal(provider: ComponentName?, user: UserHandle?, rank: Int) {
+ private fun addWidgetInternal(
+ provider: ComponentName?,
+ user: UserHandle?,
+ rank: Int,
+ callback: IConfigureWidgetCallback?,
+ ) {
if (provider == null) {
throw IllegalStateException("Provider cannot be null")
}
@@ -131,8 +148,29 @@ constructor(
throw IllegalStateException("User cannot be null")
}
- // TODO(b/375036327): Add support for widget configuration
- widgetRepository.addWidget(provider, user, rank, configurator = null)
+ val configurator =
+ callback?.let {
+ WidgetConfigurator { appWidgetId ->
+ try {
+ val result = CompletableDeferred<Boolean>()
+ val resultReceiver =
+ object : IConfigureWidgetCallback.IResultReceiver.Stub() {
+ override fun onResult(success: Boolean) {
+ result.complete(success)
+ }
+ }
+
+ callback.onConfigureWidget(appWidgetId, resultReceiver)
+ result.await()
+ } catch (e: RemoteException) {
+ logger.e({ "Error configuring widget: $str1" }) {
+ str1 = e.localizedMessage
+ }
+ false
+ }
+ }
+ }
+ widgetRepository.addWidget(provider, user, rank, configurator)
}
private fun deleteWidgetInternal(appWidgetId: Int) {
@@ -168,22 +206,55 @@ constructor(
widgetRepository.resizeWidget(appWidgetId, spanY, appWidgetIds.zip(ranks).toMap())
}
+ private fun getIntentSenderForConfigureActivityInternal(appWidgetId: Int): IntentSender? {
+ return try {
+ appWidgetHost.getIntentSenderForConfigureActivity(appWidgetId, /* intentFlags= */ 0)
+ } catch (e: IntentSender.SendIntentException) {
+ logger.e({ "Error getting intent sender for configure activity" }) {
+ str1 = e.localizedMessage
+ }
+ null
+ }
+ }
+
private fun createListener(listener: IAppWidgetHostListener): AppWidgetHostListener {
return object : AppWidgetHostListener {
override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
- listener.onUpdateProviderInfo(appWidget)
+ try {
+ listener.onUpdateProviderInfo(appWidget)
+ } catch (e: RemoteException) {
+ logger.e({ "Error pushing on update provider info: $str1" }) {
+ str1 = e.localizedMessage
+ }
+ }
}
override fun updateAppWidget(views: RemoteViews?) {
- listener.updateAppWidget(views)
+ try {
+ listener.updateAppWidget(views)
+ } catch (e: RemoteException) {
+ logger.e({ "Error updating app widget: $str1" }) { str1 = e.localizedMessage }
+ }
}
override fun updateAppWidgetDeferred(packageName: String?, appWidgetId: Int) {
- listener.updateAppWidgetDeferred(packageName, appWidgetId)
+ try {
+ listener.updateAppWidgetDeferred(packageName, appWidgetId)
+ } catch (e: RemoteException) {
+ logger.e({ "Error updating app widget deferred: $str1" }) {
+ str1 = e.localizedMessage
+ }
+ }
}
override fun onViewDataChanged(viewId: Int) {
- listener.onViewDataChanged(viewId)
+ try {
+ listener.onViewDataChanged(viewId)
+ } catch (e: RemoteException) {
+ logger.e({ "Error pushing on view data changed: $str1" }) {
+ str1 = e.localizedMessage
+ }
+ }
}
}
}
@@ -219,11 +290,16 @@ constructor(
}
}
- override fun addWidget(provider: ComponentName?, user: UserHandle?, rank: Int) {
+ override fun addWidget(
+ provider: ComponentName?,
+ user: UserHandle?,
+ rank: Int,
+ callback: IConfigureWidgetCallback?,
+ ) {
val iden = clearCallingIdentity()
try {
- addWidgetInternal(provider, user, rank)
+ addWidgetInternal(provider, user, rank, callback)
} finally {
restoreCallingIdentity(iden)
}
@@ -263,6 +339,16 @@ constructor(
restoreCallingIdentity(iden)
}
}
+
+ override fun getIntentSenderForConfigureActivity(appWidgetId: Int): IntentSender? {
+ val iden = clearCallingIdentity()
+
+ try {
+ return getIntentSenderForConfigureActivityInternal(appWidgetId)
+ } finally {
+ restoreCallingIdentity(iden)
+ }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl b/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
index e556472c78a7..d71b2303db30 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
@@ -2,6 +2,7 @@ package com.android.systemui.communal.widgets;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
+import android.content.IntentSender;
import android.os.UserHandle;
import android.widget.RemoteViews;
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel;
@@ -21,7 +22,8 @@ interface IGlanceableHubWidgetManagerService {
oneway void setAppWidgetHostListener(int appWidgetId, in IAppWidgetHostListener listener);
// Requests to add a widget in the Glanceable Hub.
- oneway void addWidget(in ComponentName provider, in UserHandle user, int rank);
+ oneway void addWidget(in ComponentName provider, in UserHandle user, int rank,
+ in IConfigureWidgetCallback callback);
// Requests to delete a widget from the Glanceable Hub.
oneway void deleteWidget(int appWidgetId);
@@ -32,6 +34,9 @@ interface IGlanceableHubWidgetManagerService {
// Requests to resize a widget in the Glanceable Hub.
oneway void resizeWidget(int appWidgetId, int spanY, in int[] appWidgetIds, in int[] ranks);
+ // Returns the [IntentSender] for launching the configuration activity of the given widget.
+ IntentSender getIntentSenderForConfigureActivity(int appWidgetId);
+
// Listener for Glanceable Hub widget updates
oneway interface IGlanceableHubWidgetsListener {
// Called when widgets have updated.
@@ -48,4 +53,15 @@ interface IGlanceableHubWidgetManagerService {
void onViewDataChanged(int viewId);
}
+
+ oneway interface IConfigureWidgetCallback {
+ // Called when the given widget should launch its configuration activity. The caller should
+ // report the result through the [IResultReceiver].
+ void onConfigureWidget(int appWidgetId, in IResultReceiver resultReceiver);
+
+ interface IResultReceiver {
+ // Called when the widget configuration operation returns a result.
+ void onResult(boolean success);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
index d157cd7acb76..fddec5659b96 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
@@ -18,16 +18,19 @@ package com.android.systemui.communal.widgets
import android.app.Activity
import android.app.ActivityOptions
-import android.content.ActivityNotFoundException
+import android.content.IntentSender
+import android.os.OutcomeReceiver
import android.window.SplashScreen
import androidx.activity.ComponentActivity
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.nullableAtomicReference
import dagger.Lazy
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -43,12 +46,22 @@ constructor(
private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
@Background private val bgDispatcher: CoroutineDispatcher,
private val glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
-) : WidgetConfigurator {
+ private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
+ @Main private val mainExecutor: Executor,
+) : WidgetConfigurator, OutcomeReceiver<IntentSender?, Throwable> {
@AssistedFactory
fun interface Factory {
fun create(activity: ComponentActivity): WidgetConfigurationController
}
+ private val activityOptions: ActivityOptions
+ get() =
+ ActivityOptions.makeBasic().apply {
+ pendingIntentBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ splashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
+ }
+
private var result: CompletableDeferred<Boolean>? by nullableAtomicReference()
override suspend fun configureWidget(appWidgetId: Int): Boolean =
@@ -57,37 +70,64 @@ constructor(
throw IllegalStateException("There is already a pending configuration")
}
result = CompletableDeferred()
- val options =
- ActivityOptions.makeBasic().apply {
- pendingIntentBackgroundActivityStartMode =
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- splashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
- }
try {
- // TODO(b/375036327): Add support for widget configuration
if (
!glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled ||
!glanceableHubMultiUserHelper.isInHeadlessSystemUser()
) {
+ // Start configuration activity directly if we're running in a foreground user
with(appWidgetHostLazy.get()) {
startAppWidgetConfigureActivityForResult(
activity,
appWidgetId,
0,
REQUEST_CODE,
- options.toBundle(),
+ activityOptions.toBundle(),
+ )
+ }
+ } else {
+ with(glanceableHubWidgetManagerLazy.get()) {
+ // Use service to get intent sender and start configuration activity
+ // locally if running in a headless system user
+ getIntentSenderForConfigureActivity(
+ appWidgetId,
+ outcomeReceiver = this@WidgetConfigurationController,
+ mainExecutor,
)
}
}
- } catch (e: ActivityNotFoundException) {
+ } catch (_: Exception) {
setConfigurationResult(Activity.RESULT_CANCELED)
}
- val value = result?.await() ?: false
+ val value = result?.await() == true
result = null
return@withContext value
}
+ // Called when an intent sender is returned, and the configuration activity should be started.
+ override fun onResult(intentSender: IntentSender?) {
+ if (intentSender == null) {
+ setConfigurationResult(Activity.RESULT_CANCELED)
+ return
+ }
+
+ activity.startIntentSenderForResult(
+ intentSender,
+ REQUEST_CODE,
+ /* fillInIntent = */ null,
+ /* flagsMask = */ 0,
+ /* flagsValues = */ 0,
+ /* extraFlags = */ 0,
+ activityOptions.toBundle(),
+ )
+ }
+
+ // Called when there is an error getting the intent sender.
+ override fun onError(e: Throwable) {
+ setConfigurationResult(Activity.RESULT_CANCELED)
+ }
+
fun setConfigurationResult(resultCode: Int) {
result?.complete(resultCode == Activity.RESULT_OK)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
index d727a70de377..769d7faad2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.dagger;
+
import com.android.systemui.classifier.FalsingManagerProxy;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.globalactions.GlobalActionsImpl;
@@ -26,18 +27,20 @@ import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
+import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore;
import com.android.systemui.statusbar.phone.ActivityStarterImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher;
import com.android.systemui.volume.VolumeDialogControllerImpl;
import dagger.Binds;
import dagger.Module;
+import dagger.Provides;
/**
* Module for binding Plugin implementations.
*
- * TODO(b/166258224): Many of these should be moved closer to their implementations.
+ * <p>TODO(b/166258224): Many of these should be moved closer to their implementations.
*/
@Module
public abstract class PluginModule {
@@ -47,12 +50,18 @@ public abstract class PluginModule {
abstract ActivityStarter provideActivityStarter(ActivityStarterImpl activityStarterImpl);
/** */
- @Binds
- abstract DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl);
+ @Provides
+ @SysUISingleton
+ static DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherStore store) {
+ return store.getDefaultDisplay();
+ }
- @Binds
- abstract SysuiDarkIconDispatcher provideSysuiDarkIconDispatcher(
- DarkIconDispatcherImpl controllerImpl);
+ @Provides
+ @SysUISingleton
+ static SysuiDarkIconDispatcher provideSysuiDarkIconDispatcher(
+ SysuiDarkIconDispatcherStore store) {
+ return store.getDefaultDisplay();
+ }
/** */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 609b7330b600..2e323d40edcd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -29,7 +29,7 @@ import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
-import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlaySuppressionModule;
+import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayOverrideModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -125,7 +125,7 @@ import javax.inject.Named;
AospPolicyModule.class,
BatterySaverModule.class,
CentralSurfacesModule.class,
- ClipboardOverlaySuppressionModule.class,
+ ClipboardOverlayOverrideModule.class,
CollapsedStatusBarFragmentStartableModule.class,
ConnectingDisplayViewModel.StartableModule.class,
DefaultBlueprintModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index cb649f28f95b..4447dff7af00 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,7 +48,8 @@ import com.android.systemui.brightness.dagger.ScreenBrightnessModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.common.data.CommonDataLayerModule;
-import com.android.systemui.common.ui.ConfigurationStateModule;
+import com.android.systemui.common.ui.ConfigurationModule;
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryModule;
import com.android.systemui.common.usagestats.data.CommonUsageStatsDataLayerModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
@@ -211,7 +212,8 @@ import javax.inject.Named;
ClockRegistryModule.class,
CommunalModule.class,
CommonDataLayerModule.class,
- ConfigurationStateModule.class,
+ ConfigurationModule.class,
+ ConfigurationRepositoryModule.class,
CommonUsageStatsDataLayerModule.class,
ConfigurationControllerModule.class,
ConnectivityModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
index 9aa7fd100068..78d8d8fe4e48 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
@@ -81,7 +81,7 @@ class PrivacyDotCornerDecorProviderImpl(
override val viewId: Int,
@DisplayCutout.BoundsPosition override val alignedBound1: Int,
@DisplayCutout.BoundsPosition override val alignedBound2: Int,
- private val layoutId: Int,
+ val layoutId: Int,
) : CornerDecorProvider() {
override fun onReloadResAndMeasure(
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index c464a66ea0c3..6c335e71cfde 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -18,13 +18,18 @@ package com.android.systemui.deviceentry
import com.android.keyguard.EmptyLockIconViewController
import com.android.keyguard.LockIconViewController
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import dagger.Binds
import dagger.Lazy
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import dagger.multibindings.Multibinds
@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class])
@@ -34,6 +39,13 @@ abstract class DeviceEntryModule {
*/
@Multibinds abstract fun deviceEntryIconTransitionSet(): Set<DeviceEntryIconTransition>
+ @Binds
+ @IntoMap
+ @ClassKey(DeviceUnlockedInteractor.Activator::class)
+ abstract fun deviceUnlockedInteractorActivator(
+ activator: DeviceUnlockedInteractor.Activator
+ ): CoreStartable
+
companion object {
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 3f937bba46d4..675f00a89d23 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -5,6 +5,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.repository.UserRepository
import dagger.Binds
@@ -42,6 +43,8 @@ interface DeviceEntryRepository {
*/
val isBypassEnabled: StateFlow<Boolean>
+ val deviceUnlockStatus: MutableStateFlow<DeviceUnlockStatus>
+
/**
* Whether the lockscreen is enabled for the current user. This is `true` whenever the user has
* chosen any secure authentication method and even if they set the lockscreen to be dismissed
@@ -84,6 +87,9 @@ constructor(
initialValue = keyguardBypassController.bypassEnabled,
)
+ override val deviceUnlockStatus =
+ MutableStateFlow(DeviceUnlockStatus(isUnlocked = false, deviceUnlockSource = null))
+
override suspend fun isLockscreenEnabled(): Boolean {
return withContext(backgroundDispatcher) {
val selectedUserId = userRepository.getSelectedUserInfo().id
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index 5259c5dca39f..b74ca035a229 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -16,7 +16,10 @@
package com.android.systemui.deviceentry.domain.interactor
+import android.provider.Settings
+import android.util.Log
import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
@@ -26,42 +29,51 @@ import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReaso
import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus
import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.TrustInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
+import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class DeviceUnlockedInteractor
@Inject
constructor(
- @Application private val applicationScope: CoroutineScope,
- authenticationInteractor: AuthenticationInteractor,
- deviceEntryRepository: DeviceEntryRepository,
+ private val authenticationInteractor: AuthenticationInteractor,
+ private val repository: DeviceEntryRepository,
trustInteractor: TrustInteractor,
faceAuthInteractor: DeviceEntryFaceAuthInteractor,
fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
private val powerInteractor: PowerInteractor,
private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
private val systemPropertiesHelper: SystemPropertiesHelper,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) {
+ private val userAwareSecureSettingsRepository: UserAwareSecureSettingsRepository,
+ private val keyguardInteractor: KeyguardInteractor,
+) : ExclusiveActivatable() {
private val deviceUnlockSource =
merge(
@@ -69,7 +81,7 @@ constructor(
faceAuthInteractor.isAuthenticated
.filter { it }
.map {
- if (deviceEntryRepository.isBypassEnabled.value) {
+ if (repository.isBypassEnabled.value) {
DeviceUnlockSource.FaceWithBypass
} else {
DeviceUnlockSource.FaceWithoutBypass
@@ -163,43 +175,160 @@ constructor(
* proceed.
*/
val deviceUnlockStatus: StateFlow<DeviceUnlockStatus> =
- authenticationInteractor.authenticationMethod
- .flatMapLatest { authMethod ->
- if (!authMethod.isSecure) {
- flowOf(DeviceUnlockStatus(true, null))
- } else if (authMethod == AuthenticationMethodModel.Sim) {
- // Device is locked if SIM is locked.
- flowOf(DeviceUnlockStatus(false, null))
- } else {
- combine(
- powerInteractor.isAsleep,
- isInLockdown,
- keyguardTransitionInteractor
- .transitionValue(KeyguardState.AOD)
- .map { it == 1f }
- .distinctUntilChanged(),
- ::Triple,
- )
- .flatMapLatestConflated { (isAsleep, isInLockdown, isAod) ->
- val isForceLocked =
- when {
- isAsleep && !isAod -> true
- isInLockdown -> true
- else -> false
- }
- if (isForceLocked) {
- flowOf(DeviceUnlockStatus(false, null))
+ repository.deviceUnlockStatus.asStateFlow()
+
+ override suspend fun onActivated(): Nothing {
+ authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
+ if (!authMethod.isSecure) {
+ // Device remains unlocked as long as the authentication method is not secure.
+ Log.d(TAG, "remaining unlocked because auth method not secure")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
+ } else if (authMethod == AuthenticationMethodModel.Sim) {
+ // Device remains locked while SIM is locked.
+ Log.d(TAG, "remaining locked because SIM locked")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
+ } else {
+ handleLockAndUnlockEvents()
+ }
+ }
+
+ awaitCancellation()
+ }
+
+ private suspend fun handleLockAndUnlockEvents() {
+ try {
+ Log.d(TAG, "started watching for lock and unlock events")
+ coroutineScope {
+ launch { handleUnlockEvents() }
+ launch { handleLockEvents() }
+ }
+ } finally {
+ Log.d(TAG, "stopped watching for lock and unlock events")
+ }
+ }
+
+ private suspend fun handleUnlockEvents() {
+ // Unlock the device when a new unlock source is detected.
+ deviceUnlockSource.collect {
+ Log.d(TAG, "unlocking due to \"$it\"")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, it)
+ }
+ }
+
+ private suspend fun handleLockEvents() {
+ merge(
+ // Device wakefulness events.
+ powerInteractor.detailedWakefulness
+ .map { Pair(it.isAsleep(), it.lastSleepReason) }
+ .distinctUntilChangedBy { it.first }
+ .map { (isAsleep, lastSleepReason) ->
+ if (isAsleep) {
+ if (
+ lastSleepReason == WakeSleepReason.POWER_BUTTON &&
+ authenticationInteractor.getPowerButtonInstantlyLocks()
+ ) {
+ LockImmediately("locked instantly from power button")
+ } else {
+ LockWithDelay("entering sleep")
+ }
+ } else {
+ CancelDelayedLock("waking up")
+ }
+ },
+ // Device enters lockdown.
+ isInLockdown
+ .distinctUntilChanged()
+ .filter { it }
+ .map { LockImmediately("lockdown") },
+ // Started dreaming
+ powerInteractor.isInteractive.flatMapLatestConflated { isInteractive ->
+ // Only respond to dream state changes while the device is interactive.
+ if (isInteractive) {
+ keyguardInteractor.isDreamingAny.distinctUntilChanged().map { isDreaming ->
+ if (isDreaming) {
+ LockWithDelay("started dreaming")
} else {
- deviceUnlockSource.map { DeviceUnlockStatus(true, it) }
+ CancelDelayedLock("stopped dreaming")
}
}
+ } else {
+ emptyFlow()
+ }
+ },
+ )
+ .collectLatest(::onLockEvent)
+ }
+
+ private suspend fun onLockEvent(event: LockEvent) {
+ val debugReason = event.debugReason
+ when (event) {
+ is LockImmediately -> {
+ Log.d(TAG, "locking without delay due to \"$debugReason\"")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
+ }
+
+ is LockWithDelay -> {
+ val lockDelay = lockDelay()
+ Log.d(TAG, "locking in ${lockDelay}ms due to \"$debugReason\"")
+ try {
+ delay(lockDelay)
+ Log.d(
+ TAG,
+ "locking after having waited for ${lockDelay}ms due to \"$debugReason\"",
+ )
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
+ } catch (_: CancellationException) {
+ Log.d(
+ TAG,
+ "delayed locking canceled, original delay was ${lockDelay}ms and reason was \"$debugReason\"",
+ )
}
}
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Eagerly,
- initialValue = DeviceUnlockStatus(false, null),
- )
+
+ is CancelDelayedLock -> {
+ // Do nothing, the mere receipt of this inside of a "latest" block means that any
+ // previous coroutine is automatically canceled.
+ }
+ }
+ }
+
+ /**
+ * Returns the amount of time to wait before locking down the device after the device has been
+ * put to sleep by the user, in milliseconds.
+ */
+ private suspend fun lockDelay(): Long {
+ val lockAfterScreenTimeoutSetting =
+ userAwareSecureSettingsRepository
+ .getInt(
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT,
+ )
+ .toLong()
+ Log.d(TAG, "Lock after screen timeout setting set to ${lockAfterScreenTimeoutSetting}ms")
+
+ val maxTimeToLockDevicePolicy = authenticationInteractor.getMaximumTimeToLock()
+ Log.d(TAG, "Device policy max set to ${maxTimeToLockDevicePolicy}ms")
+
+ if (maxTimeToLockDevicePolicy <= 0) {
+ // No device policy enforced maximum.
+ Log.d(TAG, "No device policy max, delay is ${lockAfterScreenTimeoutSetting}ms")
+ return lockAfterScreenTimeoutSetting
+ }
+
+ val screenOffTimeoutSetting =
+ userAwareSecureSettingsRepository
+ .getInt(
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ KeyguardViewMediator.KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT,
+ )
+ .coerceAtLeast(0)
+ .toLong()
+ Log.d(TAG, "Screen off timeout setting set to ${screenOffTimeoutSetting}ms")
+
+ return (maxTimeToLockDevicePolicy - screenOffTimeoutSetting)
+ .coerceIn(minimumValue = 0, maximumValue = lockAfterScreenTimeoutSetting)
+ .also { Log.d(TAG, "Device policy max enforced, delay is ${it}ms") }
+ }
private fun DeviceEntryRestrictionReason?.isInLockdown(): Boolean {
return when (this) {
@@ -226,7 +355,30 @@ constructor(
return systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
}
+ /** [CoreStartable] that activates the [DeviceUnlockedInteractor]. */
+ class Activator
+ @Inject
+ constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val interactor: DeviceUnlockedInteractor,
+ ) : CoreStartable {
+ override fun start() {
+ applicationScope.launch { interactor.activate() }
+ }
+ }
+
+ private sealed interface LockEvent {
+ val debugReason: String
+ }
+
+ private data class LockImmediately(override val debugReason: String) : LockEvent
+
+ private data class LockWithDelay(override val debugReason: String) : LockEvent
+
+ private data class CancelDelayedLock(override val debugReason: String) : LockEvent
+
companion object {
+ private val TAG = "DeviceUnlockedInteractor"
@VisibleForTesting const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
@VisibleForTesting const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 034cb31dbc74..1fa829a675ec 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -264,7 +264,8 @@ constructor(
displayManager.registerDisplayListener(
callback,
backgroundHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
+ /* eventFlags */ 0,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
)
awaitClose { displayManager.unregisterDisplayListener(callback) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index dd08d3262546..7a95a41770ac 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -40,7 +40,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -566,8 +565,7 @@ public class DozeTriggers implements DozeMachine.Part {
}
// When already in pulsing, we can show the new Notification without requesting a new pulse.
- if (Flags.notificationPulsingFix()
- && dozeState == State.DOZE_PULSING && reason == DozeLog.PULSE_REASON_NOTIFICATION) {
+ if (dozeState == State.DOZE_PULSING && reason == DozeLog.PULSE_REASON_NOTIFICATION) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt
index 2bcfea8c1179..45a59015c1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt
@@ -16,20 +16,16 @@
package com.android.systemui.dreams.homecontrols.service
-import android.content.ComponentName
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy
-import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
+import com.android.systemui.dreams.homecontrols.shared.controlsSettings
import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.kotlin.FlowDumperImpl
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -45,30 +41,8 @@ constructor(
@Assisted private val proxy: IHomeControlsRemoteProxy,
) : FlowDumperImpl(dumpManager) {
- private companion object {
- const val TAG = "HomeControlsRemoteProxy"
- }
-
val componentInfo: Flow<HomeControlsComponentInfo> =
- conflatedCallbackFlow {
- val listener =
- object : IOnControlsSettingsChangeListener.Stub() {
- override fun onControlsSettingsChanged(
- panelComponent: ComponentName?,
- allowTrivialControlsOnLockscreen: Boolean,
- ) {
- trySendWithFailureLogging(
- HomeControlsComponentInfo(
- panelComponent,
- allowTrivialControlsOnLockscreen,
- ),
- TAG,
- )
- }
- }
- proxy.registerListenerForCurrentUser(listener)
- awaitClose { proxy.unregisterListenerForCurrentUser(listener) }
- }
+ proxy.controlsSettings
.distinctUntilChanged()
.stateIn(bgScope, SharingStarted.WhileSubscribed(), null)
.dumpValue("componentInfo")
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxyExt.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxyExt.kt
new file mode 100644
index 000000000000..2993ab5a464e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxyExt.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.homecontrols.shared
+
+import android.content.ComponentName
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+val IHomeControlsRemoteProxy.controlsSettings: Flow<HomeControlsComponentInfo>
+ get() = conflatedCallbackFlow {
+ val listener =
+ object : IOnControlsSettingsChangeListener.Stub() {
+ override fun onControlsSettingsChanged(
+ panelComponent: ComponentName?,
+ allowTrivialControlsOnLockscreen: Boolean,
+ ) {
+ trySend(
+ HomeControlsComponentInfo(panelComponent, allowTrivialControlsOnLockscreen)
+ )
+ }
+ }
+ registerListenerForCurrentUser(listener)
+ awaitClose { unregisterListenerForCurrentUser(listener) }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt
index a65d216aa5a2..6d1fd4df2852 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt
@@ -57,6 +57,11 @@ constructor(binderFactory: HomeControlsRemoteServiceBinder.Factory) : LifecycleS
super.onBind(intent)
return binder
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+ binder.onDestroy()
+ }
}
class HomeControlsRemoteServiceBinder
@@ -148,6 +153,14 @@ constructor(
}
}
+ fun onDestroy() {
+ logger.d("Service destroyed")
+ callbacks.kill()
+ callbackCount.set(0)
+ collectionJob?.cancel()
+ collectionJob = null
+ }
+
@AssistedFactory
interface Factory {
fun create(lifecycleOwner: LifecycleOwner): HomeControlsRemoteServiceBinder
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
index 4a8c040e33c1..fd913893b3ac 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
@@ -20,7 +20,6 @@ import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession
import android.app.smartspace.SmartspaceTarget
-import android.content.Context
import android.graphics.Color
import android.util.Log
import android.view.View
@@ -31,6 +30,7 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM
+import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN
@@ -44,13 +44,12 @@ import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Named
-/**
- * Controller for managing the smartspace view on the dream
- */
+/** Controller for managing the smartspace view on the dream */
@SysUISingleton
-class DreamSmartspaceController @Inject constructor(
- private val context: Context,
- private val smartspaceManager: SmartspaceManager?,
+class DreamSmartspaceController
+@Inject
+constructor(
+ private val userTracker: UserTracker,
private val execution: Execution,
@Main private val uiExecutor: Executor,
private val smartspaceViewComponentFactory: SmartspaceViewComponent.Factory,
@@ -65,6 +64,7 @@ class DreamSmartspaceController @Inject constructor(
private const val TAG = "DreamSmartspaceCtrlr"
}
+ private var userSmartspaceManager: SmartspaceManager? = null
private var session: SmartspaceSession? = null
private val weatherPlugin: BcSmartspaceDataPlugin? = optionalWeatherPlugin.orElse(null)
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
@@ -78,66 +78,68 @@ class DreamSmartspaceController @Inject constructor(
// Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>()
- var preconditionListener = object : SmartspacePrecondition.Listener {
- override fun onCriteriaChanged() {
- reloadSmartspace()
+ var preconditionListener =
+ object : SmartspacePrecondition.Listener {
+ override fun onCriteriaChanged() {
+ reloadSmartspace()
+ }
}
- }
init {
precondition.addListener(preconditionListener)
}
- var filterListener = object : SmartspaceTargetFilter.Listener {
- override fun onCriteriaChanged() {
- reloadSmartspace()
+ var filterListener =
+ object : SmartspaceTargetFilter.Listener {
+ override fun onCriteriaChanged() {
+ reloadSmartspace()
+ }
}
- }
init {
targetFilter?.addListener(filterListener)
}
- var stateChangeListener = object : View.OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(v: View) {
- val view = v as SmartspaceView
- // Until there is dream color matching
- view.setPrimaryTextColor(Color.WHITE)
- smartspaceViews.add(view)
- connectSession()
- view.setDozeAmount(0f)
- }
+ var stateChangeListener =
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ val view = v as SmartspaceView
+ // Until there is dream color matching
+ view.setPrimaryTextColor(Color.WHITE)
+ smartspaceViews.add(view)
+ connectSession()
+ view.setDozeAmount(0f)
+ }
- override fun onViewDetachedFromWindow(v: View) {
- smartspaceViews.remove(v as SmartspaceView)
+ override fun onViewDetachedFromWindow(v: View) {
+ smartspaceViews.remove(v as SmartspaceView)
- if (smartspaceViews.isEmpty()) {
- disconnect()
+ if (smartspaceViews.isEmpty()) {
+ disconnect()
+ }
}
}
- }
- private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
- execution.assertIsMainThread()
+ private val sessionListener =
+ SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
- // The weather data plugin takes unfiltered targets and performs the filtering internally.
- weatherPlugin?.onTargetsAvailable(targets)
+ // The weather data plugin takes unfiltered targets and performs the filtering
+ // internally.
+ weatherPlugin?.onTargetsAvailable(targets)
- onTargetsAvailableUnfiltered(targets)
- val filteredTargets = targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
- plugin?.onTargetsAvailable(filteredTargets)
- }
+ onTargetsAvailableUnfiltered(targets)
+ val filteredTargets =
+ targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
- /**
- * Constructs the weather view with custom layout and connects it to the weather plugin.
- */
+ /** Constructs the weather view with custom layout and connects it to the weather plugin. */
fun buildAndConnectWeatherView(parent: ViewGroup, customView: View?): View? {
return buildAndConnectViewWithPlugin(parent, weatherPlugin, customView)
}
- /**
- * Constructs the smartspace view and connects it to the smartspace service.
- */
+ /** Constructs the smartspace view and connects it to the smartspace service. */
fun buildAndConnectView(parent: ViewGroup): View? {
return buildAndConnectViewWithPlugin(parent, plugin, null)
}
@@ -145,7 +147,7 @@ class DreamSmartspaceController @Inject constructor(
private fun buildAndConnectViewWithPlugin(
parent: ViewGroup,
smartspaceDataPlugin: BcSmartspaceDataPlugin?,
- customView: View?
+ customView: View?,
): View? {
execution.assertIsMainThread()
@@ -163,12 +165,13 @@ class DreamSmartspaceController @Inject constructor(
private fun buildView(
parent: ViewGroup,
smartspaceDataPlugin: BcSmartspaceDataPlugin?,
- customView: View?
+ customView: View?,
): View? {
return if (smartspaceDataPlugin != null) {
- val view = smartspaceViewComponentFactory.create(parent, smartspaceDataPlugin,
- stateChangeListener, customView)
- .getView()
+ val view =
+ smartspaceViewComponentFactory
+ .create(parent, smartspaceDataPlugin, stateChangeListener, customView)
+ .getView()
if (view !is View) {
return null
}
@@ -179,12 +182,17 @@ class DreamSmartspaceController @Inject constructor(
}
private fun hasActiveSessionListeners(): Boolean {
- return smartspaceViews.isNotEmpty() || listeners.isNotEmpty() ||
+ return smartspaceViews.isNotEmpty() ||
+ listeners.isNotEmpty() ||
unfilteredListeners.isNotEmpty()
}
private fun connectSession() {
- if (smartspaceManager == null) {
+ if (userSmartspaceManager == null) {
+ userSmartspaceManager =
+ userTracker.userContext.getSystemService(SmartspaceManager::class.java)
+ }
+ if (userSmartspaceManager == null) {
return
}
if (plugin == null && weatherPlugin == null) {
@@ -198,25 +206,21 @@ class DreamSmartspaceController @Inject constructor(
return
}
- val newSession = smartspaceManager.createSmartspaceSession(
- SmartspaceConfig.Builder(context, UI_SURFACE_DREAM).build()
- )
+ val newSession =
+ userSmartspaceManager?.createSmartspaceSession(
+ SmartspaceConfig.Builder(userTracker.userContext, UI_SURFACE_DREAM).build()
+ )
Log.d(TAG, "Starting smartspace session for dream")
- newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+ newSession?.addOnTargetsAvailableListener(uiExecutor, sessionListener)
this.session = newSession
weatherPlugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
- plugin?.registerSmartspaceEventNotifier {
- e ->
- session?.notifySmartspaceEvent(e)
- }
+ plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) }
reloadSmartspace()
}
- /**
- * Disconnects the smartspace view from the smartspace service and cleans up any resources.
- */
+ /** Disconnects the smartspace view from the smartspace service and cleans up any resources. */
private fun disconnect() {
if (hasActiveSessionListeners()) return
@@ -259,7 +263,7 @@ class DreamSmartspaceController @Inject constructor(
private fun addAndRegisterListener(
listener: SmartspaceTargetListener,
- smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?,
) {
execution.assertIsMainThread()
smartspaceDataPlugin?.registerListener(listener)
@@ -270,7 +274,7 @@ class DreamSmartspaceController @Inject constructor(
private fun removeAndUnregisterListener(
listener: SmartspaceTargetListener,
- smartspaceDataPlugin: BcSmartspaceDataPlugin?
+ smartspaceDataPlugin: BcSmartspaceDataPlugin?,
) {
execution.assertIsMainThread()
smartspaceDataPlugin?.unregisterListener(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index 349236551ecf..b2fcc434630c 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -45,7 +45,7 @@ open class DumpManager @Inject constructor() {
/** See [registerCriticalDumpable]. */
fun registerCriticalDumpable(module: Dumpable) {
- registerCriticalDumpable(module::class.java.canonicalName, module)
+ registerCriticalDumpable(module::class.java.name, module)
}
/**
@@ -62,7 +62,7 @@ open class DumpManager @Inject constructor() {
/** See [registerNormalDumpable]. */
fun registerNormalDumpable(module: Dumpable) {
- registerNormalDumpable(module::class.java.canonicalName, module)
+ registerNormalDumpable(module::class.java.name, module)
}
/**
@@ -104,13 +104,10 @@ open class DumpManager @Inject constructor() {
dumpables[name] = DumpableEntry(module, name, priority)
}
- /**
- * Same as the above override, but automatically uses the canonical class name as the dumpable
- * name.
- */
+ /** Same as the above override, but automatically uses the class name as the dumpable name. */
@Synchronized
fun registerDumpable(module: Dumpable) {
- registerDumpable(module::class.java.canonicalName, module)
+ registerDumpable(module::class.java.name, module)
}
/** Unregisters a previously-registered dumpable. */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 303f91688575..911331b8bff1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -42,7 +42,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.GlobalSettings;
import java.io.PrintWriter;
@@ -51,6 +51,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -74,7 +75,6 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
static final String TAG = "SysUIFlags";
private final FlagManager mFlagManager;
- private final Context mContext;
private final GlobalSettings mGlobalSettings;
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
@@ -84,6 +84,8 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>();
private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>();
private final Restarter mRestarter;
+ private final UserTracker mUserTracker;
+ private final Executor mMainExecutor;
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
new ServerFlagReader.ChangeListener() {
@@ -118,6 +120,18 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
}
};
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mContext.unregisterReceiver(mReceiver);
+ mContext = userContext;
+ registerReceiver();
+ }
+ };
+
+ private Context mContext;
+
@Inject
public FeatureFlagsClassicDebug(
FlagManager flagManager,
@@ -128,10 +142,11 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
Restarter restarter,
- UserContextProvider userContextProvider) {
+ UserTracker userTracker,
+ @Main Executor executor) {
mFlagManager = flagManager;
if (classicFlagsMultiUser()) {
- mContext = userContextProvider.createCurrentUserContext(context);
+ mContext = userTracker.createCurrentUserContext(context);
} else {
mContext = context;
}
@@ -141,19 +156,28 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mRestarter = restarter;
+ mUserTracker = userTracker;
+ mMainExecutor = executor;
}
/** Call after construction to setup listeners. */
void init() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_SET_FLAG);
- filter.addAction(ACTION_GET_FLAGS);
mFlagManager.setOnSettingsChangedAction(
suppressRestart -> restartSystemUI(suppressRestart, "Settings changed"));
mFlagManager.setClearCacheAction(this::removeFromCache);
+ registerReceiver();
+ if (classicFlagsMultiUser()) {
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
+ }
+ mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
+ }
+
+ void registerReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SET_FLAG);
+ filter.addAction(ACTION_GET_FLAGS);
mContext.registerReceiver(mReceiver, filter, null, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
- mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b431636b0e8b..fe9c9cb4f7c8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.flags
-import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
import com.android.systemui.flags.FlagsFactory.releasedFlag
import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
@@ -78,12 +77,6 @@ object Flags {
resourceBooleanFlag(R.bool.config_enableLockScreenCustomClocks, "lockscreen_custom_clocks")
/**
- * Whether the clock on a wide lock screen should use the new "stepping" animation for moving
- * the digits when the clock moves.
- */
- @JvmField val STEP_CLOCK_ANIMATION = releasedFlag("step_clock_animation")
-
- /**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
* will occur in stages. This is one stage of many to come.
*/
@@ -220,10 +213,6 @@ object Flags {
// TODO(b/293380347): Tracking Bug
@JvmField val COLOR_FIDELITY = unreleasedFlag("color_fidelity")
- // 900 - media
- // TODO(b/254512697): Tracking Bug
- val MEDIA_TAP_TO_TRANSFER = releasedFlag("media_tap_to_transfer")
-
// TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag("dream_media_complication")
@@ -255,15 +244,6 @@ object Flags {
val WM_ENABLE_SHELL_TRANSITIONS =
sysPropBooleanFlag("persist.wm.debug.shell_transit", default = true)
- // TODO(b/254513207): Tracking Bug to delete
- @Keep
- @JvmField
- val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES =
- releasedFlag(
- name = "enable_screen_record_enterprise_policies",
- namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- )
-
// TODO(b/293252410) : Tracking Bug
@JvmField val LOCKSCREEN_ENABLE_LANDSCAPE = unreleasedFlag("lockscreen.enable_landscape")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 162047bb3b79..91b44e7a6202 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -36,7 +36,6 @@ import android.app.Dialog;
import android.app.IActivityManager;
import android.app.StatusBarManager;
import android.app.WallpaperManager;
-import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -138,6 +137,7 @@ import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserLogoutInteractor;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.GlobalSettings;
@@ -197,7 +197,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
private final AudioManager mAudioManager;
- private final DevicePolicyManager mDevicePolicyManager;
private final LockPatternUtils mLockPatternUtils;
private final SelectedUserInteractor mSelectedUserInteractor;
private final TelephonyListenerManager mTelephonyListenerManager;
@@ -260,6 +259,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final ShadeController mShadeController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
+ private final UserLogoutInteractor mLogoutInteractor;
private final GlobalActionsInteractor mInteractor;
@VisibleForTesting
@@ -344,7 +344,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Context context,
GlobalActionsManager windowManagerFuncs,
AudioManager audioManager,
- DevicePolicyManager devicePolicyManager,
LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
TelephonyListenerManager telephonyListenerManager,
@@ -376,11 +375,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogTransitionAnimator dialogTransitionAnimator,
SelectedUserInteractor selectedUserInteractor,
+ UserLogoutInteractor logoutInteractor,
GlobalActionsInteractor interactor) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
- mDevicePolicyManager = devicePolicyManager;
mLockPatternUtils = lockPatternUtils;
mTelephonyListenerManager = telephonyListenerManager;
mKeyguardStateController = keyguardStateController;
@@ -412,6 +411,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogTransitionAnimator = dialogTransitionAnimator;
mSelectedUserInteractor = selectedUserInteractor;
+ mLogoutInteractor = logoutInteractor;
mInteractor = interactor;
// receive broadcasts
@@ -639,12 +639,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
} else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
addIfShouldShowAction(tempActions, new ScreenshotAction());
} else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
- // TODO(b/206032495): should call mDevicePolicyManager.getLogoutUserId() instead of
- // hardcode it to USER_SYSTEM so it properly supports headless system user mode
- // (and then call mDevicePolicyManager.clearLogoutUser() after switched)
- if (mDevicePolicyManager.isLogoutEnabled()
- && currentUser.get() != null
- && currentUser.get().id != UserHandle.USER_SYSTEM) {
+ if (mLogoutInteractor.isLogoutEnabled().getValue()) {
addIfShouldShowAction(tempActions, new LogoutAction());
}
} else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
@@ -1134,7 +1129,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
// Add a little delay before executing, to give the dialog a chance to go away before
// switching user
mHandler.postDelayed(() -> {
- mDevicePolicyManager.logoutUser();
+ mLogoutInteractor.logOut();
}, mDialogPressDelay);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 4a369e7e849e..7758dcc0fc88 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -17,12 +17,14 @@
package com.android.systemui.inputdevice.tutorial.domain.interactor
import android.os.SystemProperties
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.LAUNCH_DELAY
import com.android.systemui.keyboard.data.repository.KeyboardRepository
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -35,6 +37,7 @@ import kotlin.time.Duration.Companion.hours
import kotlin.time.toKotlinDuration
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
@@ -96,6 +99,9 @@ constructor(
private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
isAnyDeviceConnected[deviceType]!!.filter { it }.first()
+ // Only for testing notifications. This should behave independently from scheduling
+ @VisibleForTesting val commandTutorials = MutableStateFlow(TutorialType.NONE)
+
// Merging two flows ensures that tutorial is launched consecutively to avoid race condition
val tutorials: Flow<TutorialType> =
merge(touchpadScheduleFlow, keyboardScheduleFlow).map {
@@ -146,6 +152,15 @@ constructor(
pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}")
pw.println(" launch time = ${repo.launchTime(TOUCHPAD)}")
}
+ "notify" -> {
+ if (args.size != 2) help(pw)
+ when (args[1]) {
+ "keyboard" -> commandTutorials.value = TutorialType.KEYBOARD
+ "touchpad" -> commandTutorials.value = TutorialType.TOUCHPAD
+ "both" -> commandTutorials.value = TutorialType.BOTH
+ else -> help(pw)
+ }
+ }
else -> help(pw)
}
}
@@ -155,6 +170,7 @@ constructor(
pw.println("Available commands:")
pw.println(" clear")
pw.println(" info")
+ pw.println(" notify [keyboard|touchpad|both]")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
index 3b26f2ff9deb..9dae64921d26 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
@@ -24,6 +24,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.app.NotificationCompat
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -41,7 +42,7 @@ import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.merge
/** When the scheduler is due, show a notification to launch tutorial */
@SysUISingleton
@@ -56,7 +57,11 @@ constructor(
) {
fun start() {
backgroundScope.launch {
- tutorialSchedulerInteractor.tutorials.collect { showNotification(it) }
+ merge(
+ tutorialSchedulerInteractor.tutorials,
+ tutorialSchedulerInteractor.commandTutorials,
+ )
+ .collect { showNotification(it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
index fa494150cbbf..fee08b31bd93 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
@@ -27,6 +27,7 @@ import androidx.compose.runtime.getValue
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
@@ -40,7 +41,6 @@ import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTUR
import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
import java.util.Optional
import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
@@ -90,6 +90,7 @@ constructor(
setContent {
PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) }
}
+ // TODO(b/376692701): Update launchTime when the activity is launched by Companion App
if (savedInstanceState == null) {
metricsLogger.logPeripheralTutorialLaunched(
intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY),
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
index f0b2b86d67a0..38fc2a80ad02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -19,17 +19,18 @@ package com.android.systemui.keyboard.docking.ui.viewmodel
import android.content.Context
import android.view.Surface
import android.view.WindowManager
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class KeyboardDockingIndicationViewModel
@@ -38,7 +39,7 @@ constructor(
private val windowManager: WindowManager,
private val context: Context,
keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
@Background private val backgroundScope: CoroutineScope,
) {
@@ -128,7 +129,7 @@ constructor(
blurAmount = BLUR_AMOUNT,
duration = DURATION,
easeInDuration = EASE_DURATION,
- easeOutDuration = EASE_DURATION
+ easeOutDuration = EASE_DURATION,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
new file mode 100644
index 000000000000..8c393e27da59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.model
+
+import android.graphics.drawable.Icon
+import android.hardware.input.InputGestureData
+import android.view.KeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+
+/**
+ * Internal Keyboard Shortcut models to use with [ShortcutCategoriesUtils.fetchShortcutCategory]
+ * when converting API models to Shortcut Helper Model [ShortcutCategory]. These Internal Models
+ * bridge the Gap between [InputGestureData] from custom shortcuts API and [KeyboardShortcutGroup]
+ * from default shortcuts API allowing us to have a single Utility Class that converts API models to
+ * Shortcut Helper models
+ *
+ * @param label Equivalent to shortcut helper's subcategory label
+ * @param items Keyboard Shortcuts received from API
+ * @param packageName package name of current app shortcut if available.
+ */
+data class InternalKeyboardShortcutGroup(
+ val label: String,
+ val items: List<InternalKeyboardShortcutInfo>,
+ val packageName: String? = null,
+)
+
+/**
+ * @param label Shortcut label
+ * @param keycode Key to trigger shortcut
+ * @param modifiers Mask of shortcut modifiers
+ * @param baseCharacter Key to trigger shortcut if is a character
+ * @param icon Shortcut icon if available - often used for app launch shortcuts
+ * @param isCustomShortcut If Shortcut is user customized or system defined.
+ */
+data class InternalKeyboardShortcutInfo(
+ val label: String,
+ val keycode: Int,
+ val modifiers: Int,
+ val baseCharacter: Char = Char.MIN_VALUE,
+ val icon: Icon? = null,
+ val isCustomShortcut: Boolean = false,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
new file mode 100644
index 000000000000..7f8fbb5065aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputGestureData.KeyTrigger
+import android.hardware.input.InputManager
+import android.hardware.input.InputSettings
+import android.hardware.input.KeyGestureEvent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class CustomShortcutCategoriesRepository
+@Inject
+constructor(
+ stateRepository: ShortcutHelperStateRepository,
+ private val userTracker: UserTracker,
+ @Background private val backgroundScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
+) : ShortcutCategoriesRepository {
+
+ private val userContext: Context
+ get() = userTracker.createCurrentUserContext(userTracker.userContext)
+
+ // Input manager created with user context to provide correct user id when requesting custom
+ // shortcut
+ private val inputManager: InputManager
+ get() = userContext.getSystemService(INPUT_SERVICE) as InputManager
+
+ private val activeInputDevice =
+ stateRepository.state.map {
+ if (it is Active) {
+ withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) }
+ } else {
+ null
+ }
+ }
+
+ override val categories: Flow<List<ShortcutCategory>> =
+ activeInputDevice
+ .map { inputDevice ->
+ if (inputDevice == null) {
+ emptyList()
+ } else {
+ val customInputGesturesForUser: List<InputGestureData> =
+ if (InputSettings.isCustomizableInputGesturesFeatureFlagEnabled()) {
+ inputManager.getCustomInputGestures(/* filter= */ null)
+ } else emptyList()
+ val sources = toInternalGroupSources(customInputGesturesForUser)
+ val supportedKeyCodes =
+ shortcutCategoriesUtils.fetchSupportedKeyCodes(
+ inputDevice.id,
+ sources.map { it.groups },
+ )
+
+ val result =
+ sources.mapNotNull { source ->
+ shortcutCategoriesUtils.fetchShortcutCategory(
+ type = source.type,
+ groups = source.groups,
+ inputDevice = inputDevice,
+ supportedKeyCodes = supportedKeyCodes,
+ )
+ }
+ result
+ }
+ }
+ .stateIn(
+ scope = backgroundScope,
+ initialValue = emptyList(),
+ started = SharingStarted.Lazily,
+ )
+
+ private fun toInternalGroupSources(
+ inputGestures: List<InputGestureData>
+ ): List<InternalGroupsSource> {
+ val ungroupedInternalGroupSources =
+ inputGestures.mapNotNull { gestureData ->
+ val keyTrigger = gestureData.trigger as KeyTrigger
+ val keyGestureType = gestureData.action.keyGestureType()
+ fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel ->
+ toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger)?.let {
+ internalKeyboardShortcutInfo ->
+ val group =
+ InternalKeyboardShortcutGroup(
+ label = groupLabel,
+ items = listOf(internalKeyboardShortcutInfo),
+ )
+
+ fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let {
+ InternalGroupsSource(groups = listOf(group), type = it)
+ }
+ }
+ }
+ }
+
+ return ungroupedInternalGroupSources
+ }
+
+ private fun toInternalKeyboardShortcutInfo(
+ keyGestureType: Int,
+ keyTrigger: KeyTrigger,
+ ): InternalKeyboardShortcutInfo? {
+ fetchShortcutInfoLabelByGestureType(keyGestureType)?.let {
+ return InternalKeyboardShortcutInfo(
+ label = it,
+ keycode = keyTrigger.keycode,
+ modifiers = keyTrigger.modifierState,
+ isCustomShortcut = true,
+ )
+ }
+ return null
+ }
+
+ private fun fetchGroupLabelByGestureType(
+ @KeyGestureEvent.KeyGestureType keyGestureType: Int
+ ): String? {
+ return InputGestures.gestureToInternalKeyboardShortcutGroupLabelMap.getOrDefault(
+ keyGestureType,
+ null,
+ )
+ }
+
+ private fun fetchShortcutInfoLabelByGestureType(
+ @KeyGestureEvent.KeyGestureType keyGestureType: Int
+ ): String? {
+ return InputGestures.gestureToInternalKeyboardShortcutInfoLabelMap.getOrDefault(
+ keyGestureType,
+ null,
+ )
+ }
+
+ private fun fetchShortcutCategoryTypeByGestureType(
+ @KeyGestureEvent.KeyGestureType keyGestureType: Int
+ ): ShortcutCategoryType? {
+ return InputGestures.gestureToShortcutCategoryTypeMap.getOrDefault(keyGestureType, null)
+ }
+
+ private data class InternalGroupsSource(
+ val groups: List<InternalKeyboardShortcutGroup>,
+ val type: ShortcutCategoryType,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
new file mode 100644
index 000000000000..5bb7cdd03b8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.hardware.input.InputManager
+import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource
+import com.android.systemui.keyboard.shortcut.qualifiers.AppCategoriesShortcuts
+import com.android.systemui.keyboard.shortcut.qualifiers.CurrentAppShortcuts
+import com.android.systemui.keyboard.shortcut.qualifiers.InputShortcuts
+import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts
+import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class DefaultShortcutCategoriesRepository
+@Inject
+constructor(
+ @Background private val backgroundScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ @SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource,
+ @MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource,
+ @AppCategoriesShortcuts private val appCategoriesShortcutsSource: KeyboardShortcutGroupsSource,
+ @InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource,
+ @CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource,
+ private val inputManager: InputManager,
+ stateRepository: ShortcutHelperStateRepository,
+ shortcutCategoriesUtils: ShortcutCategoriesUtils,
+) : ShortcutCategoriesRepository {
+
+ private val sources =
+ listOf(
+ InternalGroupsSource(source = systemShortcutsSource, typeProvider = { System }),
+ InternalGroupsSource(
+ source = multitaskingShortcutsSource,
+ typeProvider = { MultiTasking },
+ ),
+ InternalGroupsSource(
+ source = appCategoriesShortcutsSource,
+ typeProvider = { AppCategories },
+ ),
+ InternalGroupsSource(
+ source = inputShortcutsSource,
+ typeProvider = { InputMethodEditor },
+ ),
+ InternalGroupsSource(
+ source = currentAppShortcutsSource,
+ typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) },
+ ),
+ )
+
+ private val activeInputDevice =
+ stateRepository.state.map {
+ if (it is Active) {
+ withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) }
+ } else {
+ null
+ }
+ }
+
+ override val categories: Flow<List<ShortcutCategory>> =
+ activeInputDevice
+ .map { inputDevice ->
+ if (inputDevice == null) {
+ return@map emptyList()
+ }
+ val groupsFromAllSources =
+ sources.map {
+ toInternalKeyboardShortcutGroups(it.source.shortcutGroups(inputDevice.id))
+ }
+ val supportedKeyCodes =
+ shortcutCategoriesUtils.fetchSupportedKeyCodes(
+ inputDevice.id,
+ groupsFromAllSources,
+ )
+ return@map sources.mapIndexedNotNull { index, internalGroupsSource ->
+ shortcutCategoriesUtils.fetchShortcutCategory(
+ internalGroupsSource.typeProvider(groupsFromAllSources[index]),
+ groupsFromAllSources[index],
+ inputDevice,
+ supportedKeyCodes,
+ )
+ }
+ }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.Lazily,
+ initialValue = emptyList(),
+ )
+
+ private fun toInternalKeyboardShortcutGroups(
+ keyboardShortcutGroups: List<KeyboardShortcutGroup>
+ ): List<InternalKeyboardShortcutGroup> {
+ return keyboardShortcutGroups.map { group ->
+ InternalKeyboardShortcutGroup(
+ label = group.label.toString(),
+ items = group.items.map { toInternalKeyboardShortcutInfo(it) },
+ packageName = group.packageName?.toString(),
+ )
+ }
+ }
+
+ private fun toInternalKeyboardShortcutInfo(
+ keyboardShortcutInfo: KeyboardShortcutInfo
+ ): InternalKeyboardShortcutInfo {
+ return InternalKeyboardShortcutInfo(
+ label = keyboardShortcutInfo.label!!.toString(),
+ keycode = keyboardShortcutInfo.keycode,
+ modifiers = keyboardShortcutInfo.modifiers,
+ baseCharacter = keyboardShortcutInfo.baseCharacter,
+ icon = keyboardShortcutInfo.icon,
+ )
+ }
+
+ private fun getCurrentAppShortcutCategoryType(
+ shortcutGroups: List<InternalKeyboardShortcutGroup>
+ ): ShortcutCategoryType? {
+ val packageName = shortcutGroups.firstOrNull()?.packageName ?: return null
+ return CurrentApp(packageName)
+ }
+
+ private class InternalGroupsSource(
+ val source: KeyboardShortcutGroupsSource,
+ val typeProvider: (groups: List<InternalKeyboardShortcutGroup>) -> ShortcutCategoryType?,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt
new file mode 100644
index 000000000000..28134db4d588
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_BACK
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+
+object InputGestures {
+ val gestureToShortcutCategoryTypeMap =
+ mapOf(
+ // System Category
+ KEY_GESTURE_TYPE_HOME to System,
+ KEY_GESTURE_TYPE_RECENT_APPS to System,
+ KEY_GESTURE_TYPE_BACK to System,
+ KEY_GESTURE_TYPE_TAKE_SCREENSHOT to System,
+ KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to System,
+ KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to System,
+ KEY_GESTURE_TYPE_LOCK_SCREEN to System,
+ KEY_GESTURE_TYPE_OPEN_NOTES to System,
+ KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to System,
+ KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to System,
+ KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to System,
+ KEY_GESTURE_TYPE_ALL_APPS to System,
+
+ // Multitasking Category
+ KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to MultiTasking,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to MultiTasking,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to MultiTasking,
+ KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to MultiTasking,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to MultiTasking,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking,
+
+ // App Category
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to AppCategories,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to AppCategories,
+ )
+
+ // TODO move all string to to resources use the same resources as the original shortcuts
+ // - that way when the strings are translated there are no discrepancies
+ val gestureToInternalKeyboardShortcutGroupLabelMap =
+ mapOf(
+ // System Category
+ KEY_GESTURE_TYPE_HOME to "System controls",
+ KEY_GESTURE_TYPE_RECENT_APPS to "System controls",
+ KEY_GESTURE_TYPE_BACK to "System controls",
+ KEY_GESTURE_TYPE_TAKE_SCREENSHOT to "System controls",
+ KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to "System controls",
+ KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to "System controls",
+ KEY_GESTURE_TYPE_LOCK_SCREEN to "System controls",
+ KEY_GESTURE_TYPE_ALL_APPS to "System controls",
+ KEY_GESTURE_TYPE_OPEN_NOTES to "System apps",
+ KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to "System apps",
+ KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to "System apps",
+ KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to "System apps",
+
+ // Multitasking Category
+ KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to "Recent apps",
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to "Split screen",
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to "Split screen",
+ KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to "Split screen",
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to "Split screen",
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to "Split screen",
+
+ // App Category
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to "Applications",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to "Applications",
+ )
+
+ val gestureToInternalKeyboardShortcutInfoLabelMap =
+ mapOf(
+ // System Category
+ KEY_GESTURE_TYPE_HOME to "Go to home screen",
+ KEY_GESTURE_TYPE_RECENT_APPS to "View recent apps",
+ KEY_GESTURE_TYPE_BACK to "Go back",
+ KEY_GESTURE_TYPE_TAKE_SCREENSHOT to "Take screenshot",
+ KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to "Show shortcuts",
+ KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to "View notifications",
+ KEY_GESTURE_TYPE_LOCK_SCREEN to "Lock screen",
+ KEY_GESTURE_TYPE_ALL_APPS to "Open apps list",
+ KEY_GESTURE_TYPE_OPEN_NOTES to "Take a note",
+ KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to "Open settings",
+ KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to "Open assistant",
+ KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to "Open assistant",
+
+ // Multitasking Category
+ KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to "Cycle forward through recent apps",
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to
+ "Use split screen with current app on the left",
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to
+ "Use split screen with current app on the right",
+ KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to "Switch from split screen to full screen",
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to
+ "Switch to app on left or above while using split screen",
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
+ "Switch to app on right or below while using split screen",
+
+ // App Category
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to "Calculator",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to "Calendar",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to "Chrome",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to "Contacts",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to "Gmail",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to "Maps",
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to "Messages",
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt
new file mode 100644
index 000000000000..2e8cc000a63c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import kotlinx.coroutines.flow.Flow
+
+interface ShortcutCategoriesRepository {
+ val categories: Flow<List<ShortcutCategory>>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
index 12dd58176a71..899fd15d6115 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt
@@ -24,123 +24,34 @@ import android.util.Log
import android.view.InputDevice
import android.view.KeyCharacterMap
import android.view.KeyEvent
-import android.view.KeyboardShortcutGroup
-import android.view.KeyboardShortcutInfo
import com.android.systemui.Flags.shortcutHelperKeyGlyph
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource
-import com.android.systemui.keyboard.shortcut.qualifiers.AppCategoriesShortcuts
-import com.android.systemui.keyboard.shortcut.qualifiers.CurrentAppShortcuts
-import com.android.systemui.keyboard.shortcut.qualifiers.InputShortcuts
-import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts
-import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup
+import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext
-@SysUISingleton
-class ShortcutHelperCategoriesRepository
+class ShortcutCategoriesUtils
@Inject
constructor(
private val context: Context,
- @Background private val backgroundScope: CoroutineScope,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
- @SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource,
- @MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource,
- @AppCategoriesShortcuts private val appCategoriesShortcutsSource: KeyboardShortcutGroupsSource,
- @InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource,
- @CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
private val inputManager: InputManager,
- stateRepository: ShortcutHelperStateRepository,
) {
-
- private val sources =
- listOf(
- InternalGroupsSource(
- source = systemShortcutsSource,
- isTrusted = true,
- typeProvider = { System },
- ),
- InternalGroupsSource(
- source = multitaskingShortcutsSource,
- isTrusted = true,
- typeProvider = { MultiTasking },
- ),
- InternalGroupsSource(
- source = appCategoriesShortcutsSource,
- isTrusted = true,
- typeProvider = { AppCategories },
- ),
- InternalGroupsSource(
- source = inputShortcutsSource,
- isTrusted = false,
- typeProvider = { InputMethodEditor },
- ),
- InternalGroupsSource(
- source = currentAppShortcutsSource,
- isTrusted = false,
- typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) },
- ),
- )
-
- private val activeInputDevice =
- stateRepository.state.map {
- if (it is Active) {
- withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) }
- } else {
- null
- }
- }
-
- val categories: Flow<List<ShortcutCategory>> =
- activeInputDevice
- .map { inputDevice ->
- if (inputDevice == null) {
- return@map emptyList()
- }
- val groupsFromAllSources = sources.map { it.source.shortcutGroups(inputDevice.id) }
- val supportedKeyCodes = fetchSupportedKeyCodes(inputDevice.id, groupsFromAllSources)
- return@map sources.mapIndexedNotNull { index, internalGroupsSource ->
- fetchShortcutCategory(
- internalGroupsSource,
- groupsFromAllSources[index],
- inputDevice,
- supportedKeyCodes,
- )
- }
- }
- .stateIn(
- scope = backgroundScope,
- started = SharingStarted.Lazily,
- initialValue = emptyList(),
- )
-
- private fun fetchShortcutCategory(
- internalGroupsSource: InternalGroupsSource,
- groups: List<KeyboardShortcutGroup>,
+ fun fetchShortcutCategory(
+ type: ShortcutCategoryType?,
+ groups: List<InternalKeyboardShortcutGroup>,
inputDevice: InputDevice,
supportedKeyCodes: Set<Int>,
): ShortcutCategory? {
- val type = internalGroupsSource.typeProvider(groups)
return if (type == null) {
null
} else {
@@ -151,27 +62,17 @@ constructor(
inputDevice.keyCharacterMap,
type,
groups,
- internalGroupsSource.isTrusted,
+ type.isTrusted,
supportedKeyCodes,
)
}
}
- private fun getCurrentAppShortcutCategoryType(
- shortcutGroups: List<KeyboardShortcutGroup>
- ): ShortcutCategoryType? {
- return if (shortcutGroups.isEmpty()) {
- null
- } else {
- CurrentApp(packageName = shortcutGroups[0].packageName.toString())
- }
- }
-
private fun toShortcutCategory(
keyGlyphMap: KeyGlyphMap?,
keyCharacterMap: KeyCharacterMap,
type: ShortcutCategoryType,
- shortcutGroups: List<KeyboardShortcutGroup>,
+ shortcutGroups: List<InternalKeyboardShortcutGroup>,
keepIcons: Boolean,
supportedKeyCodes: Set<Int>,
): ShortcutCategory? {
@@ -179,7 +80,7 @@ constructor(
shortcutGroups
.map { shortcutGroup ->
ShortcutSubCategory(
- shortcutGroup.label.toString(),
+ shortcutGroup.label,
toShortcuts(
keyGlyphMap,
keyCharacterMap,
@@ -201,7 +102,7 @@ constructor(
private fun toShortcuts(
keyGlyphMap: KeyGlyphMap?,
keyCharacterMap: KeyCharacterMap,
- infoList: List<KeyboardShortcutInfo>,
+ infoList: List<InternalKeyboardShortcutInfo>,
keepIcons: Boolean,
supportedKeyCodes: Set<Int>,
) =
@@ -216,13 +117,13 @@ constructor(
private fun toShortcut(
keyGlyphMap: KeyGlyphMap?,
keyCharacterMap: KeyCharacterMap,
- shortcutInfo: KeyboardShortcutInfo,
+ shortcutInfo: InternalKeyboardShortcutInfo,
keepIcon: Boolean,
): Shortcut? {
val shortcutCommand =
toShortcutCommand(keyGlyphMap, keyCharacterMap, shortcutInfo) ?: return null
return Shortcut(
- label = shortcutInfo.label!!.toString(),
+ label = shortcutInfo.label,
icon = toShortcutIcon(keepIcon, shortcutInfo),
commands = listOf(shortcutCommand),
)
@@ -230,7 +131,7 @@ constructor(
private fun toShortcutIcon(
keepIcon: Boolean,
- shortcutInfo: KeyboardShortcutInfo,
+ shortcutInfo: InternalKeyboardShortcutInfo,
): ShortcutIcon? {
if (!keepIcon) {
return null
@@ -247,7 +148,7 @@ constructor(
private fun toShortcutCommand(
keyGlyphMap: KeyGlyphMap?,
keyCharacterMap: KeyCharacterMap,
- info: KeyboardShortcutInfo,
+ info: InternalKeyboardShortcutInfo,
): ShortcutCommand? {
val keys = mutableListOf<ShortcutKey>()
var remainingModifiers = info.modifiers
@@ -272,7 +173,7 @@ constructor(
Log.wtf(TAG, "No keys for $info")
return null
}
- return ShortcutCommand(keys)
+ return ShortcutCommand(keys = keys, isCustom = info.isCustomShortcut)
}
private fun toShortcutModifierKey(keyGlyphMap: KeyGlyphMap?, modifierMask: Int): ShortcutKey? {
@@ -325,11 +226,11 @@ constructor(
return null
}
- private suspend fun fetchSupportedKeyCodes(
+ suspend fun fetchSupportedKeyCodes(
deviceId: Int,
- groupsFromAllSources: List<List<KeyboardShortcutGroup>>,
+ groupsFromAllSources: List<List<InternalKeyboardShortcutGroup>>,
): Set<Int> =
- withContext(backgroundDispatcher) {
+ withContext(backgroundCoroutineContext) {
val allUsedKeyCodes =
groupsFromAllSources
.flatMap { groups -> groups.flatMap { group -> group.items } }
@@ -342,14 +243,8 @@ constructor(
.toSet()
}
- private class InternalGroupsSource(
- val source: KeyboardShortcutGroupsSource,
- val isTrusted: Boolean,
- val typeProvider: (groups: List<KeyboardShortcutGroup>) -> ShortcutCategoryType?,
- )
-
companion object {
- private const val TAG = "SHCategoriesRepo"
+ private const val TAG = "ShortcutCategoriesUtils"
private val SUPPORTED_MODIFIERS =
listOf(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index 05ff0cc30a44..0201f402af2a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.shortcut.data.source
import android.content.res.Resources
+import android.view.KeyEvent.KEYCODE_D
import android.view.KeyEvent.KEYCODE_DPAD_LEFT
import android.view.KeyEvent.KEYCODE_DPAD_RIGHT
import android.view.KeyEvent.KEYCODE_DPAD_UP
@@ -29,6 +30,7 @@ import android.view.KeyboardShortcutGroup
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
import com.android.systemui.res.R
+import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
import javax.inject.Inject
class MultitaskingShortcutsSource @Inject constructor(@Main private val resources: Resources) :
@@ -38,42 +40,62 @@ class MultitaskingShortcutsSource @Inject constructor(@Main private val resource
listOf(
KeyboardShortcutGroup(
resources.getString(R.string.shortcutHelper_category_recent_apps),
- recentsShortcuts()
+ recentsShortcuts(),
),
KeyboardShortcutGroup(
resources.getString(R.string.shortcutHelper_category_split_screen),
- splitScreenShortcuts()
- )
+ splitScreenShortcuts(),
+ ),
)
- private fun splitScreenShortcuts() =
- listOf(
- // Enter Split screen with current app to RHS:
- // - Meta + Ctrl + Right arrow
+ private fun splitScreenShortcuts() = buildList {
+ // Enter Split screen with current app to RHS:
+ // - Meta + Ctrl + Right arrow
+ add(
shortcutInfo(resources.getString(R.string.system_multitasking_rhs)) {
command(META_META_ON or META_CTRL_ON, KEYCODE_DPAD_RIGHT)
- },
- // Enter Split screen with current app to LHS:
- // - Meta + Ctrl + Left arrow
+ }
+ )
+ // Enter Split screen with current app to LHS:
+ // - Meta + Ctrl + Left arrow
+ add(
shortcutInfo(resources.getString(R.string.system_multitasking_lhs)) {
command(META_META_ON or META_CTRL_ON, KEYCODE_DPAD_LEFT)
- },
- // Switch from Split screen to full screen:
- // - Meta + Ctrl + Up arrow
+ }
+ )
+ // Switch from Split screen to full screen:
+ // - Meta + Ctrl + Up arrow
+ add(
shortcutInfo(resources.getString(R.string.system_multitasking_full_screen)) {
command(META_META_ON or META_CTRL_ON, KEYCODE_DPAD_UP)
- },
- // Change split screen focus to RHS:
- // - Meta + Alt + Right arrow
+ }
+ )
+ // Change split screen focus to RHS:
+ // - Meta + Alt + Right arrow
+ add(
shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_rhs)) {
command(META_META_ON or META_ALT_ON, KEYCODE_DPAD_RIGHT)
- },
- // Change split screen focus to LHS:
- // - Meta + Alt + Left arrow
+ }
+ )
+ // Change split screen focus to LHS:
+ // - Meta + Alt + Left arrow
+ add(
shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_lhs)) {
command(META_META_ON or META_ALT_ON, KEYCODE_DPAD_LEFT)
- },
+ }
)
+ if (enableMoveToNextDisplayShortcut()) {
+ // Move a window to the next display:
+ // - Meta + Ctrl + D
+ add(
+ shortcutInfo(
+ resources.getString(R.string.system_multitasking_move_to_next_display)
+ ) {
+ command(META_META_ON or META_CTRL_ON, KEYCODE_D)
+ }
+ )
+ }
+ }
private fun recentsShortcuts() =
listOf(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
new file mode 100644
index 000000000000..85d22144f201
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.domain.interactor
+
+import android.view.KeyEvent.META_META_ON
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+import javax.inject.Inject
+
+class ShortcutCustomizationInteractor @Inject constructor() {
+ fun getDefaultCustomShortcutModifierKey(): ShortcutKey.Icon.ResIdIcon {
+ return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.keyIcons[META_META_ON]!!)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
index 6f19561dd87b..39fc27d35082 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
@@ -28,9 +28,7 @@ import kotlinx.coroutines.flow.map
@SysUISingleton
class ShortcutHelperCategoriesInteractor
@Inject
-constructor(
- categoriesRepository: ShortcutHelperCategoriesRepository,
-) {
+constructor(categoriesRepository: DefaultShortcutCategoriesRepository) {
val shortcutCategories: Flow<List<ShortcutCategory>> =
categoriesRepository.categories.map { categories ->
@@ -42,12 +40,12 @@ constructor(
shortcutCategory.subCategories.map {
ShortcutSubCategory(
label = it.label,
- shortcuts = groupShortcutsInSubcategory(it.shortcuts)
+ shortcuts = groupShortcutsInSubcategory(it.shortcuts),
)
}
return ShortcutCategory(
type = shortcutCategory.type,
- subCategories = subCategoriesWithGroupedShortcuts
+ subCategories = subCategoriesWithGroupedShortcuts,
)
}
@@ -59,7 +57,7 @@ constructor(
Shortcut(
label = commonLabel,
icon = groupedShortcuts.firstOrNull()?.icon,
- commands = groupedShortcuts.flatMap { it.commands }
+ commands = groupedShortcuts.flatMap { it.commands },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
index c89ef1582f98..813a1fcac65d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
@@ -17,24 +17,36 @@
package com.android.systemui.keyboard.shortcut.shared.model
sealed interface ShortcutCategoryType {
- data object System : ShortcutCategoryType
+ val isTrusted: Boolean
- data object MultiTasking : ShortcutCategoryType
+ data object System : ShortcutCategoryType {
+ override val isTrusted: Boolean = true
+ }
+
+ data object MultiTasking : ShortcutCategoryType {
+ override val isTrusted: Boolean = true
+ }
- data object InputMethodEditor : ShortcutCategoryType
+ data object InputMethodEditor : ShortcutCategoryType {
+ override val isTrusted: Boolean = false
+ }
- data object AppCategories : ShortcutCategoryType
+ data object AppCategories : ShortcutCategoryType {
+ override val isTrusted: Boolean = true
+ }
- data class CurrentApp(val packageName: String) : ShortcutCategoryType
+ data class CurrentApp(val packageName: String) : ShortcutCategoryType {
+ override val isTrusted: Boolean = false
+ }
}
data class ShortcutCategory(
val type: ShortcutCategoryType,
- val subCategories: List<ShortcutSubCategory>
+ val subCategories: List<ShortcutSubCategory>,
) {
constructor(
type: ShortcutCategoryType,
- vararg subCategories: ShortcutSubCategory
+ vararg subCategories: ShortcutSubCategory,
) : this(type, subCategories.asList())
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
index 28451ae2bc14..c7e6b43b9624 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
@@ -18,10 +18,11 @@ package com.android.systemui.keyboard.shortcut.shared.model
import androidx.annotation.DrawableRes
-data class ShortcutCommand(val keys: List<ShortcutKey>)
+data class ShortcutCommand(val keys: List<ShortcutKey>, val isCustom: Boolean = false)
class ShortcutCommandBuilder {
private val keys = mutableListOf<ShortcutKey>()
+ private var isCustom = false
fun key(text: String) {
keys += ShortcutKey.Text(text)
@@ -31,7 +32,11 @@ class ShortcutCommandBuilder {
keys += ShortcutKey.Icon.ResIdIcon(drawableResId)
}
- fun build() = ShortcutCommand(keys)
+ fun isCustom(isCustom: Boolean) {
+ this.isCustom = isCustom
+ }
+
+ fun build() = ShortcutCommand(keys, isCustom)
}
fun shortcutCommand(block: ShortcutCommandBuilder.() -> Unit) =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutInfo.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutInfo.kt
new file mode 100644
index 000000000000..e4ccc2c553fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutInfo.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+data class ShortcutInfo(
+ val label: String,
+ val categoryType: ShortcutCategoryType,
+ val subCategoryLabel: String,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogDelegate.kt
new file mode 100644
index 000000000000..c98472ef7bda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogDelegate.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui
+
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import com.android.systemui.statusbar.phone.DialogDelegate
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+class ShortcutCustomizationDialogDelegate : DialogDelegate<SystemUIDialog> {
+
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ super.onCreate(dialog, savedInstanceState)
+ dialog.window?.apply { setGravity(Gravity.CENTER) }
+ }
+
+ override fun getWidth(dialog: SystemUIDialog): Int {
+ return WindowManager.LayoutParams.WRAP_CONTENT
+ }
+
+ override fun getHeight(dialog: SystemUIDialog): Int {
+ return WindowManager.LayoutParams.WRAP_CONTENT
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
new file mode 100644
index 000000000000..02e206e09bc7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui
+
+import android.app.Dialog
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutInfo
+import com.android.systemui.keyboard.shortcut.ui.composable.AssignNewShortcutDialog
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
+import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutCustomizationViewModel
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.statusbar.phone.create
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class ShortcutCustomizationDialogStarter
+@AssistedInject
+constructor(
+ viewModelFactory: ShortcutCustomizationViewModel.Factory,
+ private val dialogFactory: SystemUIDialogFactory,
+) : ExclusiveActivatable() {
+
+ private var dialog: Dialog? = null
+ private val viewModel = viewModelFactory.create()
+
+ override suspend fun onActivated(): Nothing {
+ viewModel.shortcutCustomizationUiState.collect { uiState ->
+ if (
+ uiState is ShortcutCustomizationUiState.AddShortcutDialog &&
+ !uiState.isDialogShowing
+ ) {
+ dialog = createAddShortcutDialog().also { it.show() }
+ viewModel.onAddShortcutDialogShown()
+ } else if (uiState is ShortcutCustomizationUiState.Inactive) {
+ dialog?.dismiss()
+ dialog = null
+ }
+ }
+ }
+
+ fun onAddShortcutDialogRequested(shortcutBeingCustomized: ShortcutInfo) {
+ viewModel.onAddShortcutDialogRequested(shortcutBeingCustomized)
+ }
+
+ private fun createAddShortcutDialog(): Dialog {
+ return dialogFactory.create(dialogDelegate = ShortcutCustomizationDialogDelegate()) { dialog
+ ->
+ val uiState by viewModel.shortcutCustomizationUiState.collectAsStateWithLifecycle()
+ AssignNewShortcutDialog(
+ uiState = uiState,
+ modifier = Modifier.width(364.dp).wrapContentHeight().padding(vertical = 24.dp),
+ onKeyPress = { viewModel.onKeyPressed(it) },
+ onCancel = { dialog.dismiss() },
+ )
+ dialog.setOnDismissListener { viewModel.onAddShortcutDialogDismissed() }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): ShortcutCustomizationDialogStarter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
index d33ab2acc8fb..10a201e976c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
@@ -24,8 +24,10 @@ import android.os.UserHandle
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
@@ -35,6 +37,7 @@ import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelperBottom
import com.android.systemui.keyboard.shortcut.ui.composable.getWidth
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import com.android.systemui.statusbar.phone.createBottomSheet
import javax.inject.Inject
@@ -47,15 +50,18 @@ class ShortcutHelperDialogStarter
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- private val viewModel: ShortcutHelperViewModel,
+ private val shortcutHelperViewModel: ShortcutHelperViewModel,
+ shortcutCustomizationDialogStarterFactory: ShortcutCustomizationDialogStarter.Factory,
private val dialogFactory: SystemUIDialogFactory,
private val activityStarter: ActivityStarter,
) : CoreStartable {
@VisibleForTesting var dialog: Dialog? = null
+ private val shortcutCustomizationDialogStarter =
+ shortcutCustomizationDialogStarterFactory.create()
override fun start() {
- viewModel.shouldShow
+ shortcutHelperViewModel.shouldShow
.map { shouldShow ->
if (shouldShow) {
dialog = createShortcutHelperDialog().also { it.show() }
@@ -69,16 +75,22 @@ constructor(
private fun createShortcutHelperDialog(): Dialog {
return dialogFactory.createBottomSheet(
content = { dialog ->
- val shortcutsUiState by viewModel.shortcutsUiState.collectAsStateWithLifecycle()
+ val shortcutsUiState by
+ shortcutHelperViewModel.shortcutsUiState.collectAsStateWithLifecycle()
+ LaunchedEffect(Unit) { shortcutCustomizationDialogStarter.activate() }
ShortcutHelper(
modifier = Modifier.width(getWidth()),
shortcutsUiState = shortcutsUiState,
onKeyboardSettingsClicked = { onKeyboardSettingsClicked(dialog) },
- onSearchQueryChanged = { viewModel.onSearchQueryChanged(it) },
+ onSearchQueryChanged = { shortcutHelperViewModel.onSearchQueryChanged(it) },
+ onCustomizationRequested = {
+ shortcutCustomizationDialogStarter.onAddShortcutDialogRequested(it)
+ },
)
- dialog.setOnDismissListener { viewModel.onViewClosed() }
+ dialog.setOnDismissListener { shortcutHelperViewModel.onViewClosed() }
+ dialog.setTitle(stringResource(R.string.shortcut_helper_title))
},
- maxWidth = ShortcutHelperBottomSheet.LargeScreenWidthLandscape
+ maxWidth = ShortcutHelperBottomSheet.LargeScreenWidthLandscape,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
new file mode 100644
index 000000000000..43f0f200ab23
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui.composable
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.ErrorOutline
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
+import com.android.systemui.res.R
+
+@Composable
+fun AssignNewShortcutDialog(
+ uiState: ShortcutCustomizationUiState,
+ modifier: Modifier = Modifier,
+ onKeyPress: (KeyEvent) -> Boolean,
+ onCancel: () -> Unit,
+) {
+ if (uiState is ShortcutCustomizationUiState.AddShortcutDialog) {
+ Column(modifier = modifier) {
+ Title(
+ uiState.shortcutLabel,
+ modifier = Modifier.padding(horizontal = 24.dp).width(316.dp),
+ )
+ Description(
+ modifier = Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp).width(316.dp)
+ )
+ PromptShortcutModifier(
+ modifier =
+ Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp)
+ .width(131.dp)
+ .height(48.dp),
+ defaultModifierKey = uiState.defaultCustomShortcutModifierKey,
+ )
+ SelectedKeyCombinationContainer(
+ shouldShowErrorMessage = uiState.shouldShowErrorMessage,
+ onKeyPress = onKeyPress,
+ )
+ KeyCombinationAlreadyInUseErrorMessage(uiState.shouldShowErrorMessage)
+ DialogButtons(onCancel, isValidKeyCombination = uiState.isValidKeyCombination)
+ }
+ }
+}
+
+@Composable
+fun DialogButtons(onCancel: () -> Unit, isValidKeyCombination: Boolean) {
+ Row(
+ modifier =
+ Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp)
+ .sizeIn(minWidth = 316.dp, minHeight = 48.dp),
+ verticalAlignment = Alignment.Bottom,
+ horizontalArrangement = Arrangement.End,
+ ) {
+ ShortcutHelperButton(
+ shape = RoundedCornerShape(50.dp),
+ onClick = onCancel,
+ color = Color.Transparent,
+ width = 80.dp,
+ contentColor = MaterialTheme.colorScheme.primary,
+ text = stringResource(R.string.shortcut_helper_customize_dialog_cancel_button_label),
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ ShortcutHelperButton(
+ onClick = {},
+ color = MaterialTheme.colorScheme.primary,
+ width = 116.dp,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ text =
+ stringResource(R.string.shortcut_helper_customize_dialog_set_shortcut_button_label),
+ enabled = isValidKeyCombination,
+ )
+ }
+}
+
+@Composable
+fun KeyCombinationAlreadyInUseErrorMessage(shouldShowErrorMessage: Boolean) {
+ if (shouldShowErrorMessage) {
+ Box(modifier = Modifier.padding(horizontal = 16.dp).width(332.dp).height(40.dp)) {
+ Text(
+ text = stringResource(R.string.shortcut_helper_customize_dialog_error_message),
+ style = MaterialTheme.typography.bodyMedium,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ fontWeight = FontWeight.W500,
+ color = MaterialTheme.colorScheme.error,
+ modifier = Modifier.padding(start = 24.dp).width(252.dp),
+ )
+ }
+ }
+}
+
+@Composable
+fun SelectedKeyCombinationContainer(
+ keyCombination: String =
+ stringResource(R.string.shortcut_helper_add_shortcut_dialog_placeholder),
+ shouldShowErrorMessage: Boolean,
+ onKeyPress: (KeyEvent) -> Boolean,
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ val isFocused by interactionSource.collectIsFocusedAsState()
+ val outlineColor =
+ if (!isFocused) MaterialTheme.colorScheme.outline
+ else if (shouldShowErrorMessage) MaterialTheme.colorScheme.error
+ else MaterialTheme.colorScheme.primary
+
+ ClickableShortcutSurface(
+ onClick = {},
+ color = Color.Transparent,
+ shape = RoundedCornerShape(50.dp),
+ modifier =
+ Modifier.padding(all = 16.dp)
+ .sizeIn(minWidth = 332.dp, minHeight = 56.dp)
+ .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp))
+ .onPreviewKeyEvent { onKeyPress(it) },
+ interactionSource = interactionSource,
+ ) {
+ Row(
+ modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 16.dp, bottom = 16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = keyCombination,
+ style = MaterialTheme.typography.headlineSmall,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ fontWeight = FontWeight.W500,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.width(252.dp),
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ if (shouldShowErrorMessage) {
+ Icon(
+ imageVector = Icons.Default.ErrorOutline,
+ contentDescription = null,
+ modifier = Modifier.size(20.dp),
+ tint = MaterialTheme.colorScheme.error,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun Title(title: String, modifier: Modifier = Modifier) {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.headlineSmall,
+ fontSize = 24.sp,
+ modifier = modifier.wrapContentSize(Alignment.Center),
+ color = MaterialTheme.colorScheme.onSurface,
+ lineHeight = 32.sp,
+ )
+}
+
+@Composable
+private fun Description(modifier: Modifier = Modifier) {
+ Text(
+ text = stringResource(id = R.string.shortcut_helper_customize_mode_sub_title),
+ style = MaterialTheme.typography.bodyMedium,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ modifier = modifier.wrapContentSize(Alignment.Center),
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+}
+
+@Composable
+private fun PromptShortcutModifier(
+ modifier: Modifier,
+ defaultModifierKey: ShortcutKey.Icon.ResIdIcon,
+) {
+ Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(2.dp)) {
+ ActionKeyContainer(defaultModifierKey)
+ PlusIconContainer()
+ }
+}
+
+@Composable
+private fun ActionKeyContainer(defaultModifierKey: ShortcutKey.Icon.ResIdIcon) {
+ Row(
+ modifier =
+ Modifier.height(48.dp)
+ .width(105.dp)
+ .background(
+ color = MaterialTheme.colorScheme.surface,
+ shape = RoundedCornerShape(16.dp),
+ )
+ .padding(all = 12.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ ActionKeyIcon(defaultModifierKey)
+ ActionKeyText()
+ }
+}
+
+@Composable
+fun ActionKeyText() {
+ Text(
+ text = "Action",
+ style = MaterialTheme.typography.titleMedium,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ modifier = Modifier.wrapContentSize(Alignment.Center),
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+}
+
+@Composable
+private fun ActionKeyIcon(defaultModifierKey: ShortcutKey.Icon.ResIdIcon) {
+ Icon(
+ painter = painterResource(id = defaultModifierKey.drawableResId),
+ contentDescription = stringResource(R.string.shortcut_helper_content_description_meta_key),
+ modifier = Modifier.size(24.dp).wrapContentSize(Alignment.Center),
+ )
+}
+
+@Composable
+private fun PlusIconContainer() {
+ Icon(
+ tint = MaterialTheme.colorScheme.onSurface,
+ imageVector = Icons.Default.Add,
+ contentDescription =
+ stringResource(id = R.string.shortcut_helper_content_description_plus_icon),
+ modifier = Modifier.padding(vertical = 12.dp).size(24.dp).wrapContentSize(Alignment.Center),
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index abddc7059ece..13934ea38233 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -110,6 +110,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutM
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutInfo
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.ui.model.IconSource
@@ -124,6 +125,7 @@ fun ShortcutHelper(
modifier: Modifier = Modifier,
shortcutsUiState: ShortcutsUiState,
useSinglePane: @Composable () -> Boolean = { shouldUseSinglePane() },
+ onCustomizationRequested: (ShortcutInfo) -> Unit = {},
) {
when (shortcutsUiState) {
is ShortcutsUiState.Active -> {
@@ -133,6 +135,7 @@ fun ShortcutHelper(
onSearchQueryChanged,
modifier,
onKeyboardSettingsClicked,
+ onCustomizationRequested,
)
}
else -> {
@@ -148,6 +151,7 @@ private fun ActiveShortcutHelper(
onSearchQueryChanged: (String) -> Unit,
modifier: Modifier,
onKeyboardSettingsClicked: () -> Unit,
+ onCustomizationRequested: (ShortcutInfo) -> Unit = {},
) {
var selectedCategoryType by
remember(shortcutsUiState.defaultSelectedCategory) {
@@ -173,6 +177,7 @@ private fun ActiveShortcutHelper(
onCategorySelected = { selectedCategoryType = it },
onKeyboardSettingsClicked,
shortcutsUiState.isShortcutCustomizerFlagEnabled,
+ onCustomizationRequested,
)
}
}
@@ -362,6 +367,7 @@ private fun ShortcutHelperTwoPane(
onCategorySelected: (ShortcutCategoryType?) -> Unit,
onKeyboardSettingsClicked: () -> Unit,
isShortcutCustomizerFlagEnabled: Boolean,
+ onCustomizationRequested: (ShortcutInfo) -> Unit = {},
) {
val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
var isCustomizeModeEntered by remember { mutableStateOf(false) }
@@ -400,6 +406,7 @@ private fun ShortcutHelperTwoPane(
Modifier.fillMaxSize().padding(top = 8.dp),
selectedCategory,
isCustomizing = isCustomizing,
+ onCustomizationRequested = onCustomizationRequested,
)
}
}
@@ -434,6 +441,7 @@ private fun EndSidePanel(
modifier: Modifier,
category: ShortcutCategoryUi?,
isCustomizing: Boolean,
+ onCustomizationRequested: (ShortcutInfo) -> Unit = {},
) {
val listState = rememberLazyListState()
LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) }
@@ -447,6 +455,15 @@ private fun EndSidePanel(
searchQuery = searchQuery,
subCategory = subcategory,
isCustomizing = isCustomizing,
+ onCustomizationRequested = { label, subCategoryLabel ->
+ onCustomizationRequested(
+ ShortcutInfo(
+ label = label,
+ subCategoryLabel = subCategoryLabel,
+ categoryType = category.type,
+ )
+ )
+ },
)
Spacer(modifier = Modifier.height(8.dp))
}
@@ -476,6 +493,7 @@ private fun SubCategoryContainerDualPane(
searchQuery: String,
subCategory: ShortcutSubCategory,
isCustomizing: Boolean,
+ onCustomizationRequested: (String, String) -> Unit = { _: String, _: String -> },
) {
Surface(
modifier = Modifier.fillMaxWidth(),
@@ -497,6 +515,7 @@ private fun SubCategoryContainerDualPane(
searchQuery = searchQuery,
shortcut = shortcut,
isCustomizing = isCustomizing,
+ onCustomizationRequested = { onCustomizationRequested(it, subCategory.label) },
)
}
}
@@ -518,6 +537,7 @@ private fun Shortcut(
searchQuery: String,
shortcut: ShortcutModel,
isCustomizing: Boolean = false,
+ onCustomizationRequested: (String) -> Unit = {},
) {
val interactionSource = remember { MutableInteractionSource() }
val isFocused by interactionSource.collectIsFocusedAsState()
@@ -541,7 +561,12 @@ private fun Shortcut(
ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut)
}
Spacer(modifier = Modifier.width(24.dp))
- ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut, isCustomizing)
+ ShortcutKeyCombinations(
+ modifier = Modifier.weight(1f),
+ shortcut = shortcut,
+ isCustomizing = isCustomizing,
+ onAddShortcutClicked = { onCustomizationRequested(shortcut.label) },
+ )
}
}
@@ -569,6 +594,7 @@ private fun ShortcutKeyCombinations(
modifier: Modifier = Modifier,
shortcut: ShortcutModel,
isCustomizing: Boolean = false,
+ onAddShortcutClicked: () -> Unit = {},
) {
FlowRow(
modifier = modifier,
@@ -590,7 +616,7 @@ private fun ShortcutKeyCombinations(
color = MaterialTheme.colorScheme.outline,
shape = CircleShape,
),
- onClick = {},
+ onClick = { onAddShortcutClicked() },
color = Color.Transparent,
width = 32.dp,
height = 32.dp,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index 435968ee79ca..e761c7313ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -44,6 +44,7 @@ import androidx.compose.material3.LocalAbsoluteTonalElevation
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTonalElevationEnabled
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.minimumInteractiveComponentSize
@@ -283,6 +284,108 @@ fun ShortcutHelperButton(
}
@Composable
+fun ShortcutHelperButton(
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit,
+ shape: Shape = RoundedCornerShape(360.dp),
+ color: Color,
+ width: Dp,
+ height: Dp = 40.dp,
+ iconSource: IconSource = IconSource(),
+ text: String? = null,
+ contentColor: Color,
+ contentPaddingHorizontal: Dp = 16.dp,
+ contentPaddingVertical: Dp = 10.dp,
+ enabled: Boolean = true,
+) {
+ ShortcutHelperButtonSurface(
+ onClick = onClick,
+ shape = shape,
+ color = color,
+ modifier = modifier,
+ enabled = enabled,
+ width = width,
+ height = height,
+ ) {
+ Row(
+ modifier =
+ Modifier.padding(
+ horizontal = contentPaddingHorizontal,
+ vertical = contentPaddingVertical,
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ if (iconSource.imageVector != null) {
+ Icon(
+ tint = contentColor,
+ imageVector = iconSource.imageVector,
+ contentDescription =
+ null, // TODO this probably should not be null for accessibility.
+ modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center),
+ )
+ }
+
+ if (iconSource.imageVector != null && text != null)
+ Spacer(modifier = Modifier.weight(1f))
+
+ if (text != null) {
+ Text(
+ text,
+ color = contentColor,
+ fontSize = 14.sp,
+ style = MaterialTheme.typography.labelLarge,
+ modifier = Modifier.wrapContentSize(Alignment.Center),
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ShortcutHelperButtonSurface(
+ onClick: () -> Unit,
+ shape: Shape,
+ color: Color,
+ modifier: Modifier = Modifier,
+ enabled: Boolean,
+ width: Dp,
+ height: Dp,
+ content: @Composable () -> Unit,
+) {
+ if (enabled) {
+ ClickableShortcutSurface(
+ onClick = onClick,
+ shape = shape,
+ color = color,
+ modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
+ interactionsConfig =
+ InteractionsConfig(
+ hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
+ hoverOverlayAlpha = 0.11f,
+ pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
+ pressedOverlayAlpha = 0.15f,
+ focusOutlineColor = MaterialTheme.colorScheme.secondary,
+ focusOutlineStrokeWidth = 3.dp,
+ focusOutlinePadding = 2.dp,
+ surfaceCornerRadius = 28.dp,
+ focusOutlineCornerRadius = 33.dp,
+ ),
+ ) {
+ content()
+ }
+ } else {
+ Surface(
+ shape = shape,
+ color = color.copy(0.38f),
+ modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
+ ) {
+ content()
+ }
+ }
+}
+
+@Composable
private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color {
return MaterialTheme.colorScheme.applyTonalElevation(color, elevation)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
new file mode 100644
index 000000000000..e9f2a3b8e5b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui.model
+
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+
+sealed interface ShortcutCustomizationUiState {
+ data class AddShortcutDialog(
+ val shortcutLabel: String,
+ val shouldShowErrorMessage: Boolean,
+ val isValidKeyCombination: Boolean,
+ val defaultCustomShortcutModifierKey: ShortcutKey.Icon.ResIdIcon,
+ val isDialogShowing: Boolean,
+ ) : ShortcutCustomizationUiState
+
+ data object Inactive : ShortcutCustomizationUiState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
new file mode 100644
index 000000000000..b9253878ba0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui.viewmodel
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.input.key.KeyEvent
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutInfo
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+class ShortcutCustomizationViewModel
+@AssistedInject
+constructor(private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor) {
+ private val _shortcutBeingCustomized = mutableStateOf<ShortcutInfo?>(null)
+
+ private val _shortcutCustomizationUiState =
+ MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive)
+
+ val shortcutCustomizationUiState = _shortcutCustomizationUiState.asStateFlow()
+
+ fun onAddShortcutDialogRequested(shortcutBeingCustomized: ShortcutInfo) {
+ _shortcutCustomizationUiState.value =
+ ShortcutCustomizationUiState.AddShortcutDialog(
+ shortcutLabel = shortcutBeingCustomized.label,
+ shouldShowErrorMessage = false,
+ isValidKeyCombination = false,
+ defaultCustomShortcutModifierKey =
+ shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(),
+ isDialogShowing = false,
+ )
+
+ _shortcutBeingCustomized.value = shortcutBeingCustomized
+ }
+
+ fun onAddShortcutDialogShown() {
+ _shortcutCustomizationUiState.update { uiState ->
+ (uiState as? ShortcutCustomizationUiState.AddShortcutDialog)
+ ?.let { it.copy(isDialogShowing = true) }
+ ?: uiState
+ }
+ }
+
+ fun onAddShortcutDialogDismissed() {
+ _shortcutBeingCustomized.value = null
+ _shortcutCustomizationUiState.value = ShortcutCustomizationUiState.Inactive
+ }
+
+ fun onKeyPressed(keyEvent: KeyEvent): Boolean {
+ // TODO Not yet implemented b/373638584
+ return false
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): ShortcutCustomizationViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
index 89cdd25181cb..922bc15c0633 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
@@ -33,13 +33,13 @@ import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
-import javax.inject.Inject
interface StickyKeysRepository {
val stickyKeys: Flow<LinkedHashMap<ModifierKey, Locked>>
@@ -52,6 +52,7 @@ class StickyKeysRepositoryImpl
constructor(
private val inputManager: InputManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ // TODO: b/377244768 - Change to inject SecureSettingsRepository
secureSettingsRepository: UserAwareSecureSettingsRepository,
private val stickyKeysLogger: StickyKeysLogger,
) : StickyKeysRepository {
@@ -71,7 +72,7 @@ constructor(
override val settingEnabled: Flow<Boolean> =
secureSettingsRepository
- .boolSettingForActiveUser(SETTING_KEY, defaultValue = false)
+ .boolSetting(SETTING_KEY, defaultValue = false)
.onEach { stickyKeysLogger.logNewSettingValue(it) }
.flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e79f5902575f..2d056001b669 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -60,6 +60,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.LightRevealScrim
@@ -93,7 +94,7 @@ constructor(
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val shadeInteractor: ShadeInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 60a306b3e245..2ee9ddb0e453 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -239,7 +239,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
Flags.ensureKeyguardDoesTransitionStarting();
- private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
+ public static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
private static final boolean DEBUG = KeyguardConstants.DEBUG;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 032af94e62aa..2914cb9fdfdc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -44,7 +44,7 @@ constructor(
private val keyguardStateController: KeyguardStateController,
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
- private val keyguardTransitions: KeyguardTransitions
+ private val keyguardTransitions: KeyguardTransitions,
) {
/**
@@ -108,27 +108,28 @@ constructor(
* Manager to effect the change.
*/
fun setSurfaceBehindVisibility(visible: Boolean) {
- if (isKeyguardGoingAway == visible) {
- Log.d(TAG, "WmLockscreenVisibilityManager#setVisibility -> already visible=$visible")
+ if (isKeyguardGoingAway && visible) {
+ Log.d(TAG, "#setSurfaceBehindVisibility: already visible, ignoring")
return
}
// The surface behind is always visible if the lockscreen is not showing, so we're already
// visible.
if (visible && isLockscreenShowing != true) {
- Log.d(TAG, "#setVisibility -> already visible since the lockscreen isn't showing")
+ Log.d(TAG, "#setSurfaceBehindVisibility: ignoring since the lockscreen isn't showing")
return
}
-
-
if (visible) {
if (enableNewKeyguardShellTransitions) {
- keyguardTransitions.startKeyguardTransition(false /* keyguardShowing */, false /* aodShowing */)
+ keyguardTransitions.startKeyguardTransition(
+ false /* keyguardShowing */,
+ false, /* aodShowing */
+ )
isKeyguardGoingAway = true
return
}
- // Make the surface visible behind the keyguard by calling keyguardGoingAway. The
+ // Make the surface behind the keyguard visible by calling keyguardGoingAway. The
// lockscreen is still showing as well, allowing us to animate unlocked.
Log.d(TAG, "ActivityTaskManagerService#keyguardGoingAway()")
activityTaskManagerService.keyguardGoingAway(0)
@@ -153,7 +154,7 @@ constructor(
apps: Array<RemoteAnimationTarget>,
wallpapers: Array<RemoteAnimationTarget>,
nonApps: Array<RemoteAnimationTarget>,
- finishedCallback: IRemoteAnimationFinishedCallback
+ finishedCallback: IRemoteAnimationFinishedCallback,
) {
// Ensure that we've started a dismiss keyguard transition. WindowManager can start the
// going away animation on its own, if an activity launches and then requests dismissing the
@@ -203,27 +204,25 @@ constructor(
*/
private fun setWmLockscreenState(
lockscreenShowing: Boolean? = this.isLockscreenShowing,
- aodVisible: Boolean = this.isAodVisible
+ aodVisible: Boolean = this.isAodVisible,
) {
- Log.d(
- TAG,
- "#setWmLockscreenState(" +
- "isLockscreenShowing=$lockscreenShowing, " +
- "aodVisible=$aodVisible)."
- )
-
if (lockscreenShowing == null) {
Log.d(
TAG,
"isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" +
"non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" +
- "will happen once KeyguardTransitionBootInteractor starts the boot transition."
+ "will happen once KeyguardTransitionBootInteractor starts the boot transition.",
)
this.isAodVisible = aodVisible
return
}
if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
+ Log.d(
+ TAG,
+ "#setWmLockscreenState: lockscreenShowing=$lockscreenShowing and " +
+ "isAodVisible=$aodVisible were both unchanged, not forwarding to ATMS.",
+ )
return
}
@@ -231,7 +230,7 @@ constructor(
TAG,
"ATMS#setLockScreenShown(" +
"isLockscreenShowing=$lockscreenShowing, " +
- "aodVisible=$aodVisible)."
+ "aodVisible=$aodVisible).",
)
if (enableNewKeyguardShellTransitions) {
keyguardTransitions.startKeyguardTransition(lockscreenShowing, aodVisible)
@@ -247,7 +246,7 @@ constructor(
Log.d(
TAG,
"#endKeyguardGoingAwayAnimation() called when isKeyguardGoingAway=false. " +
- "Short-circuiting."
+ "Short-circuiting.",
)
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 3a5614fbc430..eaf8fa9585f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -114,6 +114,18 @@ interface KeyguardTransitionRepository {
@FloatRange(from = 0.0, to = 1.0) value: Float,
state: TransitionState,
)
+
+ /**
+ * Forces the current transition to emit FINISHED, foregoing any additional RUNNING steps that
+ * otherwise would have been emitted.
+ *
+ * When the screen is off, upcoming performance changes cause all Animators to cease emitting
+ * frames, which means the Animator passed to [startTransition] will never finish if it was
+ * running when the screen turned off. Also, there's simply no reason to emit RUNNING steps when
+ * the screen isn't even on. As long as we emit FINISHED, everything should end up in the
+ * correct state.
+ */
+ suspend fun forceFinishCurrentTransition()
}
@SysUISingleton
@@ -134,6 +146,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
override val transitions = _transitions.asSharedFlow().distinctUntilChanged()
private var lastStep: TransitionStep = TransitionStep()
private var lastAnimator: ValueAnimator? = null
+ private var animatorListener: AnimatorListenerAdapter? = null
private val withContextMutex = Mutex()
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
@@ -233,7 +246,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
)
}
- val adapter =
+ animatorListener =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
emitTransition(
@@ -254,9 +267,10 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
animator.removeListener(this)
animator.removeUpdateListener(updateListener)
lastAnimator = null
+ animatorListener = null
}
}
- animator.addListener(adapter)
+ animator.addListener(animatorListener)
animator.addUpdateListener(updateListener)
animator.start()
return@withContext null
@@ -290,6 +304,33 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
}
}
+ override suspend fun forceFinishCurrentTransition() {
+ withContextMutex.lock()
+
+ if (lastAnimator?.isRunning != true) {
+ return
+ }
+
+ return withContext("$TAG#forceFinishCurrentTransition", mainDispatcher) {
+ withContextMutex.unlock()
+
+ Log.d(TAG, "forceFinishCurrentTransition() - emitting FINISHED early.")
+
+ lastAnimator?.apply {
+ // Cancel the animator, but remove listeners first so we don't emit CANCELED.
+ removeAllListeners()
+ cancel()
+
+ // Emit a final 1f RUNNING step to ensure that any transitions not listening for a
+ // FINISHED step end up in the right end state.
+ emitTransition(TransitionStep(currentTransitionInfo, 1f, TransitionState.RUNNING))
+
+ // Ask the listener to emit FINISHED and clean up its state.
+ animatorListener?.onAnimationEnd(this)
+ }
+ }
+ }
+
private suspend fun updateTransitionInternal(
transitionId: UUID,
@FloatRange(from = 0.0, to = 1.0) value: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index ca862896efaa..73a4cc3ad9df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -25,6 +25,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,7 +48,7 @@ constructor(
private val context: Context,
private val burnInHelperWrapper: BurnInHelperWrapper,
@Application private val scope: CoroutineScope,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
) {
val deviceEntryIconXOffset: StateFlow<Int> =
@@ -62,7 +63,7 @@ constructor(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- burnInHelperWrapper.burnInProgressOffset()
+ burnInHelperWrapper.burnInProgressOffset(),
)
/** Given the max x,y dimens, determine the current translation shifts. */
@@ -71,7 +72,7 @@ constructor(
burnInOffset(xDimenResourceId, isXAxis = true),
burnInOffset(yDimenResourceId, isXAxis = false).map {
it * 2 - context.resources.getDimensionPixelSize(yDimenResourceId)
- }
+ },
) { translationX, translationY ->
BurnInModel(translationX, translationY, burnInHelperWrapper.burnInScale())
}
@@ -117,7 +118,7 @@ constructor(
private fun calculateOffset(
maxBurnInOffsetPixels: Int,
isXAxis: Boolean,
- scale: Float = 1f
+ scale: Float = 1f,
): Int {
return (burnInHelperWrapper.burnInOffset(maxBurnInOffsetPixels, isXAxis) * scale).toInt()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 6ac0a3f8443f..021cce6d1e23 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -20,6 +20,7 @@ import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -41,7 +42,6 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.debounce
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class FromDozingTransitionInteractor
@@ -135,11 +135,22 @@ constructor(
if (!deviceEntryInteractor.isLockscreenEnabled()) {
if (!SceneContainerFlag.isEnabled) {
- startTransitionTo(KeyguardState.GONE)
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "lockscreen not enabled",
+ )
}
} else if (canDismissLockscreen() || isKeyguardGoingAway) {
if (!SceneContainerFlag.isEnabled) {
- startTransitionTo(KeyguardState.GONE)
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason =
+ if (canDismissLockscreen()) {
+ "canDismissLockscreen()"
+ } else {
+ "isKeyguardGoingAway"
+ },
+ )
}
} else if (primaryBouncerShowing) {
if (!SceneContainerFlag.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 6385b3cffbd4..2aaec8797cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
@@ -32,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.Intra
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -39,7 +41,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class KeyguardBlueprintInteractor
@@ -48,7 +49,7 @@ constructor(
private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
@Application private val applicationScope: CoroutineScope,
shadeInteractor: ShadeInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val fingerprintPropertyInteractor: FingerprintPropertyInteractor,
private val smartspaceSection: SmartspaceSection,
) : CoreStartable {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 2e0a160bfd16..26c286df01d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -49,6 +49,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
@@ -85,7 +86,7 @@ constructor(
private val repository: KeyguardRepository,
powerInteractor: PowerInteractor,
bouncerRepository: KeyguardBouncerRepository,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
shadeRepository: ShadeRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
sceneInteractorProvider: Provider<SceneInteractor>,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 21afd3e4c444..8c9473f4284b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -430,7 +430,9 @@ constructor(
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
- value = featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
+ value =
+ com.android.systemui.Flags.lockscreenCustomClocks() ||
+ featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 0dae17c594c8..cd62d5f3b6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -16,9 +16,11 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.log.core.LogLevel.VERBOSE
@@ -29,7 +31,6 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNoti
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.debounce
-import com.android.app.tracing.coroutines.launchTraced as launch
private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -48,6 +49,7 @@ constructor(
private val aodBurnInViewModel: AodBurnInViewModel,
private val shadeInteractor: ShadeInteractor,
private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
) {
fun start() {
@@ -84,6 +86,18 @@ constructor(
}
scope.launch {
+ deviceEntryInteractor.isUnlocked.collect {
+ logger.log(TAG, VERBOSE, "DeviceEntry isUnlocked", it)
+ }
+ }
+
+ scope.launch {
+ deviceEntryInteractor.isLockscreenEnabled.collect {
+ logger.log(TAG, VERBOSE, "DeviceEntry isLockscreenEnabled", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.primaryBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index b815f1988e7e..7cd2744cb7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -19,8 +19,10 @@ package com.android.systemui.keyguard.domain.interactor
import android.annotation.SuppressLint
import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.Flags.keyguardTransitionForceFinishOnScreenOff
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
@@ -30,6 +32,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
@@ -59,7 +63,6 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Encapsulates business-logic related to the keyguard transitions. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -70,6 +73,7 @@ constructor(
@Application val scope: CoroutineScope,
private val repository: KeyguardTransitionRepository,
private val sceneInteractor: SceneInteractor,
+ private val powerInteractor: PowerInteractor,
) {
private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
@@ -188,6 +192,18 @@ constructor(
}
}
}
+
+ if (keyguardTransitionForceFinishOnScreenOff()) {
+ /**
+ * If the screen is turning off, finish the current transition immediately. Further
+ * frames won't be visible anyway.
+ */
+ scope.launch {
+ powerInteractor.screenPowerState
+ .filter { it == ScreenPowerState.SCREEN_TURNING_OFF }
+ .collect { repository.forceFinishCurrentTransition() }
+ }
+ }
}
fun transition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<TransitionStep> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index abd7f90bbf22..7d4d377c768a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -33,7 +34,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Each TransitionInteractor is responsible for determining under which conditions to notify
@@ -201,9 +201,18 @@ sealed class TransitionInteractor(
scope.launch {
keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
if (!maybeHandleInsecurePowerGesture()) {
+ val lastStep = transitionInteractor.transitionState.value
+ val modeOnCanceled =
+ if (lastStep.to == KeyguardState.AOD) {
+ // Enabled smooth transition when double-tap camera cancels
+ // transition to AOD
+ TransitionModeOnCanceled.REVERSE
+ } else {
+ TransitionModeOnCanceled.RESET
+ }
startTransitionTo(
toState = KeyguardState.OCCLUDED,
- modeOnCanceled = TransitionModeOnCanceled.RESET,
+ modeOnCanceled = modeOnCanceled,
ownerReason = "keyguardInteractor.onCameraLaunchDetected",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index a1f606740cd9..f473a82138e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -18,7 +18,8 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.ObservableTransitionState.Idle
+import com.android.compose.animation.scene.ObservableTransitionState.Transition
import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -30,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.device
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
@@ -110,11 +112,8 @@ constructor(
}
.distinctUntilChanged()
- private val isDeviceEntered: Flow<Boolean> by lazy {
- deviceEntryInteractor.get().isDeviceEntered
- }
-
- private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } }
+ private val isDeviceEntered by lazy { deviceEntryInteractor.get().isDeviceEntered }
+ private val isDeviceNotEntered by lazy { isDeviceEntered.map { !it } }
/**
* Surface visibility, which is either determined by the default visibility when not
@@ -124,32 +123,17 @@ constructor(
@OptIn(ExperimentalCoroutinesApi::class)
val surfaceBehindVisibility: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState ->
- when (transitionState) {
- is ObservableTransitionState.Transition ->
- when {
- transitionState.fromContent == Scenes.Lockscreen &&
- transitionState.toContent == Scenes.Gone ->
- sceneInteractor
- .get()
- .isTransitionUserInputOngoing
- .flatMapLatestConflated { isUserInputOngoing ->
- if (isUserInputOngoing) {
- isDeviceEntered
- } else {
- flowOf(true)
- }
- }
- transitionState.fromContent == Scenes.Bouncer &&
- transitionState.toContent == Scenes.Gone ->
- transitionState.progress.map { progress ->
- progress >
- FromPrimaryBouncerTransitionInteractor
- .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
- }
- else -> isDeviceEntered
+ sceneInteractor.get().transitionState.flatMapLatestConflated { state ->
+ when {
+ state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) ->
+ isDeviceEntered
+ state.isTransitioning(from = Scenes.Bouncer, to = Scenes.Gone) ->
+ (state as Transition).progress.map { progress ->
+ progress >
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
}
- is ObservableTransitionState.Idle -> isDeviceEntered
+ else -> lockscreenVisibilityWithScenes.map { !it }
}
}
} else {
@@ -219,6 +203,123 @@ constructor(
}
/**
+ * Scenes that are part of the keyguard and are shown when the device is locked or when the
+ * keyguard still needs to be dismissed.
+ */
+ private val keyguardScenes = setOf(Scenes.Lockscreen, Scenes.Bouncer, Scenes.Communal)
+
+ /**
+ * Scenes that don't belong in the keyguard family and cannot show when the device is locked or
+ * when the keyguard still needs to be dismissed.
+ */
+ private val nonKeyguardScenes = setOf(Scenes.Gone)
+
+ /**
+ * Scenes that can show regardless of device lock or keyguard dismissal states. Other sources of
+ * state need to be consulted to know whether the device has been entered or not.
+ */
+ private val keyguardAgnosticScenes =
+ setOf(
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Overlays.NotificationsShade,
+ Overlays.QuickSettingsShade,
+ )
+
+ private val lockscreenVisibilityWithScenes =
+ combine(
+ sceneInteractor.get().transitionState.flatMapLatestConflated {
+ when (it) {
+ is Idle -> {
+ when (it.currentScene) {
+ in keyguardScenes -> flowOf(true)
+ in nonKeyguardScenes -> flowOf(false)
+ in keyguardAgnosticScenes -> isDeviceNotEntered
+ else ->
+ throw IllegalStateException("Unknown scene: ${it.currentScene}")
+ }
+ }
+ is Transition -> {
+ when {
+ it.isTransitioningSets(from = keyguardScenes) -> flowOf(true)
+ it.isTransitioningSets(from = nonKeyguardScenes) -> flowOf(false)
+ it.isTransitioningSets(from = keyguardAgnosticScenes) ->
+ isDeviceNotEntered
+ else ->
+ throw IllegalStateException("Unknown scene: ${it.fromContent}")
+ }
+ }
+ }
+ },
+ wakeToGoneInteractor.canWakeDirectlyToGone,
+ ::Pair,
+ )
+ .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) ->
+ lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone
+ }
+
+ private val lockscreenVisibilityLegacy =
+ combine(
+ transitionInteractor.currentKeyguardState,
+ wakeToGoneInteractor.canWakeDirectlyToGone,
+ ::Pair,
+ )
+ .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
+ .map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
+ val startedFromStep = startedWithPrev.previousValue
+ val startedStep = startedWithPrev.newValue
+ val returningToGoneAfterCancellation =
+ startedStep.to == KeyguardState.GONE &&
+ startedFromStep.transitionState == TransitionState.CANCELED &&
+ startedFromStep.from == KeyguardState.GONE
+
+ val transitionInfo =
+ if (transitionRaceCondition()) {
+ transitionRepository.currentTransitionInfo
+ } else {
+ transitionRepository.currentTransitionInfoInternal.value
+ }
+ val wakingDirectlyToGone =
+ deviceIsAsleepInState(transitionInfo.from) &&
+ transitionInfo.to == KeyguardState.GONE
+
+ if (returningToGoneAfterCancellation || wakingDirectlyToGone) {
+ // GONE -> AOD/DOZING (cancel) -> GONE is the camera launch transition,
+ // which means we never want to show the lockscreen throughout the
+ // transition. Same for waking directly to gone, due to the lockscreen being
+ // disabled or because the device was woken back up before the lock timeout
+ // duration elapsed.
+ false
+ } else if (canWakeDirectlyToGone) {
+ // Never show the lockscreen if we can wake directly to GONE. This means
+ // that the lock timeout has not yet elapsed, or the keyguard is disabled.
+ // In either case, we don't show the activity lock screen until one of those
+ // conditions changes.
+ false
+ } else if (
+ currentState == KeyguardState.DREAMING &&
+ deviceEntryInteractor.get().isUnlocked.value
+ ) {
+ // Dreams dismiss keyguard and return to GONE if they can.
+ false
+ } else if (
+ startedWithPrev.newValue.from == KeyguardState.OCCLUDED &&
+ startedWithPrev.newValue.to == KeyguardState.GONE
+ ) {
+ // OCCLUDED -> GONE directly, without transiting a *_BOUNCER state, occurs
+ // when an app uses intent flags to launch over an insecure keyguard without
+ // dismissing it, and then manually requests keyguard dismissal while
+ // OCCLUDED. This transition is not user-visible; the device unlocks in the
+ // background and the app remains on top, while we're now GONE. In this case
+ // we should simply tell WM that the lockscreen is no longer visible, and
+ // *not* play the going away animation or related animations.
+ false
+ } else {
+ currentState != KeyguardState.GONE
+ }
+ }
+
+ /**
* Whether the lockscreen is visible, from the Window Manager (WM) perspective.
*
* Note: This may briefly be true even if the lockscreen UI has animated out (alpha = 0f), as we
@@ -227,69 +328,11 @@ constructor(
*/
val lockscreenVisibility: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- isDeviceNotEntered
- } else {
- combine(
- transitionInteractor.currentKeyguardState,
- wakeToGoneInteractor.canWakeDirectlyToGone,
- ::Pair,
- )
- .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
- .map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
- val startedFromStep = startedWithPrev.previousValue
- val startedStep = startedWithPrev.newValue
- val returningToGoneAfterCancellation =
- startedStep.to == KeyguardState.GONE &&
- startedFromStep.transitionState == TransitionState.CANCELED &&
- startedFromStep.from == KeyguardState.GONE
-
- val transitionInfo =
- if (transitionRaceCondition()) {
- transitionRepository.currentTransitionInfo
- } else {
- transitionRepository.currentTransitionInfoInternal.value
- }
- val wakingDirectlyToGone =
- deviceIsAsleepInState(transitionInfo.from) &&
- transitionInfo.to == KeyguardState.GONE
-
- if (returningToGoneAfterCancellation || wakingDirectlyToGone) {
- // GONE -> AOD/DOZING (cancel) -> GONE is the camera launch transition,
- // which means we never want to show the lockscreen throughout the
- // transition. Same for waking directly to gone, due to the lockscreen being
- // disabled or because the device was woken back up before the lock timeout
- // duration elapsed.
- false
- } else if (canWakeDirectlyToGone) {
- // Never show the lockscreen if we can wake directly to GONE. This means
- // that the lock timeout has not yet elapsed, or the keyguard is disabled.
- // In either case, we don't show the activity lock screen until one of those
- // conditions changes.
- false
- } else if (
- currentState == KeyguardState.DREAMING &&
- deviceEntryInteractor.get().isUnlocked.value
- ) {
- // Dreams dismiss keyguard and return to GONE if they can.
- false
- } else if (
- startedWithPrev.newValue.from == KeyguardState.OCCLUDED &&
- startedWithPrev.newValue.to == KeyguardState.GONE
- ) {
- // OCCLUDED -> GONE directly, without transiting a *_BOUNCER state, occurs
- // when an app uses intent flags to launch over an insecure keyguard without
- // dismissing it, and then manually requests keyguard dismissal while
- // OCCLUDED. This transition is not user-visible; the device unlocks in the
- // background and the app remains on top, while we're now GONE. In this case
- // we should simply tell WM that the lockscreen is no longer visible, and
- // *not* play the going away animation or related animations.
- false
- } else {
- currentState != KeyguardState.GONE
- }
- }
- .distinctUntilChanged()
- }
+ lockscreenVisibilityWithScenes
+ } else {
+ lockscreenVisibilityLegacy
+ }
+ .distinctUntilChanged()
/**
* Whether always-on-display (AOD) is visible when the lockscreen is visible, from window
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index be4bc2305922..69856151e41c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -182,9 +182,11 @@ object DeviceEntryIconViewBinder {
fgIconView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Start with an empty state
+ Log.d(TAG, "Initializing device entry fgIconView")
fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
launch("$TAG#fpIconView.viewModel") {
fgViewModel.viewModel.collect { viewModel ->
+ Log.d(TAG, "Updating device entry icon image state $viewModel")
fgIconView.setImageState(
view.getIconState(viewModel.type, viewModel.useAodVariant),
/* merge */ false,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 7292ddaf43a7..b30e1e9b1103 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -26,6 +26,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -44,7 +45,6 @@ import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
object KeyguardClockViewBinder {
private val TAG = KeyguardClockViewBinder::class.simpleName!!
@@ -133,15 +133,26 @@ object KeyguardClockViewBinder {
launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
aodBurnInViewModel.movement.collect { burnInModel ->
- viewModel.currentClock.value?.let {
- it.largeClock.layout.applyAodBurnIn(
+ viewModel.currentClock.value
+ ?.largeClock
+ ?.layout
+ ?.applyAodBurnIn(
AodClockBurnInModel(
translationX = burnInModel.translationX.toFloat(),
translationY = burnInModel.translationY.toFloat(),
scale = burnInModel.scale,
)
)
- }
+ }
+ }
+
+ launch {
+ if (!MigrateClocksToBlueprint.isEnabled) return@launch
+ viewModel.largeClockTextSize.collect { fontSizePx ->
+ viewModel.currentClock.value
+ ?.largeClock
+ ?.events
+ ?.onFontSettingChanged(fontSizePx = fontSizePx.toFloat())
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 46f5c05092eb..914fdd20e48e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -18,34 +18,23 @@
package com.android.systemui.keyguard.ui.binder
import android.content.Context
-import android.util.DisplayMetrics
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.internal.policy.SystemBarUtils
-import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
-import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection.Companion.getDimen
import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.res.R
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.util.Utils
import kotlin.reflect.KSuspendFunction1
/** Binder for the small clock view, large clock view. */
@@ -131,78 +120,6 @@ object KeyguardPreviewClockViewBinder {
}
}
- private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) {
- constraints.apply {
- constrainWidth(customR.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
- // The following two lines make lockscreen_clock_view_large is constrained to available
- // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
- constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
- constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
- val largeClockTopMargin =
- SystemBarUtils.getStatusBarHeight(context) +
- context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
- context.resources.getDimensionPixelSize(
- R.dimen.keyguard_smartspace_top_offset
- ) +
- getDimen(context, DATE_WEATHER_VIEW_HEIGHT) +
- getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
- connect(
- customR.id.lockscreen_clock_view_large,
- TOP,
- PARENT_ID,
- TOP,
- largeClockTopMargin,
- )
- connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
- connect(
- customR.id.lockscreen_clock_view_large,
- ConstraintSet.END,
- PARENT_ID,
- ConstraintSet.END,
- )
-
- // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS
- // devices, but we need position of device entry icon to constrain clock
- if (getConstraint(lockId) != null) {
- connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP)
- } else {
- // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
- val bottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
- val defaultDensity =
- DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
- DisplayMetrics.DENSITY_DEFAULT.toFloat()
- val lockIconRadiusPx = (defaultDensity * 36).toInt()
- val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
- connect(
- customR.id.lockscreen_clock_view_large,
- BOTTOM,
- PARENT_ID,
- BOTTOM,
- clockBottomMargin,
- )
- }
-
- constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
- constrainHeight(
- customR.id.lockscreen_clock_view,
- context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
- )
- connect(
- customR.id.lockscreen_clock_view,
- START,
- PARENT_ID,
- START,
- context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
- )
- val smallClockTopMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
- Utils.getStatusBarHeaderHeightKeyguard(context)
- connect(customR.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin)
- }
- }
-
private fun applyPreviewConstraints(
context: Context,
rootView: ConstraintLayout,
@@ -210,9 +127,8 @@ object KeyguardPreviewClockViewBinder {
viewModel: KeyguardPreviewClockViewModel,
) {
val cs = ConstraintSet().apply { clone(rootView) }
- applyClockDefaultConstraints(context, cs)
- previewClock.largeClock.layout.applyPreviewConstraints(cs)
- previewClock.smallClock.layout.applyPreviewConstraints(cs)
+ previewClock.largeClock.layout.applyPreviewConstraints(context, cs)
+ previewClock.smallClock.layout.applyPreviewConstraints(context, cs)
// When selectedClockSize is the initial value, make both clocks invisible to avoid
// flickering
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 36ef78e08c4f..faa497833e7b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
@@ -47,7 +48,7 @@ class AodNotificationIconsSection
@Inject
constructor(
private val context: Context,
- private val configurationState: ConfigurationState,
+ @ShadeDisplayAware private val configurationState: ConfigurationState,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
@@ -70,7 +71,7 @@ constructor(
resources.getDimensionPixelSize(R.dimen.below_clock_padding_start_icons),
0,
0,
- 0
+ 0,
)
setVisibility(View.INVISIBLE)
}
@@ -113,18 +114,18 @@ constructor(
START,
PARENT_ID,
START,
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
)
connect(
nicId,
END,
PARENT_ID,
END,
- context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+ context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
)
constrainHeight(
nicId,
- context.resources.getDimensionPixelSize(R.dimen.notification_shelf_height)
+ context.resources.getDimensionPixelSize(R.dimen.notification_shelf_height),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index ee4f41ddd5a0..6096cf74a772 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -186,12 +186,23 @@ constructor(
constraints.apply {
connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
connect(customR.id.lockscreen_clock_view_large, END, guideline, END)
- connect(customR.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP)
+ connect(
+ customR.id.lockscreen_clock_view_large,
+ BOTTOM,
+ R.id.device_entry_icon_view,
+ TOP,
+ )
val largeClockTopMargin =
keyguardClockViewModel.getLargeClockTopMargin() +
getDimen(DATE_WEATHER_VIEW_HEIGHT) +
getDimen(ENHANCED_SMARTSPACE_HEIGHT)
- connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+ connect(
+ customR.id.lockscreen_clock_view_large,
+ TOP,
+ PARENT_ID,
+ TOP,
+ largeClockTopMargin,
+ )
constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
// The following two lines make lockscreen_clock_view_large is constrained to available
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index c11005d38986..a595d815e016 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -93,18 +93,18 @@ class ClockSizeTransition(
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
- toSSBounds: Rect?
+ toSSBounds: Rect?,
) {}
override fun createAnimator(
sceenRoot: ViewGroup,
startValues: TransitionValues?,
- endValues: TransitionValues?
+ endValues: TransitionValues?,
): Animator? {
if (startValues == null || endValues == null) {
Log.w(
TAG,
- "Couldn't create animator: startValues=$startValues; endValues=$endValues"
+ "Couldn't create animator: startValues=$startValues; endValues=$endValues",
)
return null
}
@@ -137,7 +137,7 @@ class ClockSizeTransition(
"Skipping no-op transition: $toView; " +
"vis: $fromVis -> $toVis; " +
"alpha: $fromAlpha -> $toAlpha; " +
- "bounds: $fromBounds -> $toBounds; "
+ "bounds: $fromBounds -> $toBounds; ",
)
}
return null
@@ -151,7 +151,7 @@ class ClockSizeTransition(
lerp(fromBounds.left, toBounds.left, fract),
lerp(fromBounds.top, toBounds.top, fract),
lerp(fromBounds.right, toBounds.right, fract),
- lerp(fromBounds.bottom, toBounds.bottom, fract)
+ lerp(fromBounds.bottom, toBounds.bottom, fract),
)
fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
@@ -160,7 +160,7 @@ class ClockSizeTransition(
if (DEBUG) {
Log.i(
TAG,
- "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;"
+ "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
)
}
toView.setVisibility(vis ?: View.VISIBLE)
@@ -174,7 +174,7 @@ class ClockSizeTransition(
"transitioning: $toView; " +
"vis: $fromVis -> $toVis; " +
"alpha: $fromAlpha -> $toAlpha; " +
- "bounds: $fromBounds -> $toBounds; "
+ "bounds: $fromBounds -> $toBounds; ",
)
}
@@ -258,7 +258,7 @@ class ClockSizeTransition(
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
- toSSBounds: Rect?
+ toSSBounds: Rect?,
) {
// Move normally if clock is not changing visibility
if (fromIsVis == toIsVis) return
@@ -347,12 +347,17 @@ class ClockSizeTransition(
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
- toSSBounds: Rect?
+ toSSBounds: Rect?,
) {
// If view is changing visibility, hold it in place
if (fromIsVis == toIsVis) return
if (DEBUG) Log.i(TAG, "Holding position of ${view.id}")
- toBounds.set(fromBounds)
+
+ if (fromIsVis) {
+ toBounds.set(fromBounds)
+ } else {
+ fromBounds.set(toBounds)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index 4908dbdec61e..56e3125f7078 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -24,6 +24,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.recents.utilities.Utilities.clamp
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,7 +42,7 @@ class AlternateBouncerUdfpsIconViewModel
@Inject
constructor(
val context: Context,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
fingerprintPropertyInteractor: FingerprintPropertyInteractor,
@@ -85,10 +86,7 @@ constructor(
}
private val fgIconPadding: Flow<Int> = udfpsOverlayInteractor.iconPadding
val fgViewModel: Flow<DeviceEntryForegroundViewModel.ForegroundIconViewModel> =
- combine(
- fgIconColor,
- fgIconPadding,
- ) { color, padding ->
+ combine(fgIconColor, fgIconPadding) { color, padding ->
DeviceEntryForegroundViewModel.ForegroundIconViewModel(
type = DeviceEntryIconView.IconType.FINGERPRINT,
useAodVariant = false,
@@ -100,12 +98,7 @@ constructor(
val bgColor: Flow<Int> = deviceEntryBackgroundViewModel.color
val bgAlpha: Flow<Float> = flowOf(1f)
- data class IconLocation(
- val left: Int,
- val top: Int,
- val right: Int,
- val bottom: Int,
- ) {
+ data class IconLocation(val left: Int, val top: Int, val right: Int, val bottom: Int) {
val width = right - left
val height = bottom - top
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index c78e0c9f5266..1c897237fe89 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -30,9 +30,11 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.ClockSize
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.math.max
import kotlinx.coroutines.CoroutineScope
@@ -42,8 +44,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
@@ -57,7 +61,7 @@ class AodBurnInViewModel
constructor(
@Application private val applicationScope: CoroutineScope,
private val burnInInteractor: BurnInInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
@@ -164,9 +168,17 @@ constructor(
private fun burnIn(params: BurnInParameters): Flow<BurnInModel> {
return combine(
- keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map {
- Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it)
- },
+ merge(
+ keyguardTransitionInteractor.transition(Edge.create(to = KeyguardState.AOD)),
+ keyguardTransitionInteractor
+ .transition(Edge.create(from = KeyguardState.AOD))
+ .map { it.copy(value = 1f - it.value) },
+ keyguardTransitionInteractor
+ .transition(Edge.create(to = KeyguardState.LOCKSCREEN))
+ .filter { it.from != KeyguardState.AOD }
+ .map { it.copy(value = 0f) },
+ )
+ .map { Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value) },
burnInInteractor.burnIn(
xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
yDimenResourceId = R.dimen.burn_in_prevention_offset_y,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 4c667c1c702d..12f9467c0f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -42,7 +43,7 @@ constructor(
val context: Context,
val deviceEntryIconViewModel: DeviceEntryIconViewModel,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel,
aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 87c32a54438e..749f19315409 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +46,7 @@ class DeviceEntryForegroundViewModel
@Inject
constructor(
val context: Context,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
transitionInteractor: KeyguardTransitionInteractor,
deviceEntryIconViewModel: DeviceEntryIconViewModel,
@@ -106,12 +107,11 @@ constructor(
}
val viewModel: Flow<ForegroundIconViewModel> =
- combine(
- deviceEntryIconViewModel.iconType,
- useAodIconVariant,
+ combine(deviceEntryIconViewModel.iconType, useAodIconVariant, color, padding) {
+ iconType,
+ useAodVariant,
color,
- padding,
- ) { iconType, useAodVariant, color, padding ->
+ padding ->
ForegroundIconViewModel(
type = iconType,
useAodVariant = useAodVariant,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index 11ed52ac35b7..c9fdf7a31458 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -41,7 +42,7 @@ class DreamingToGlanceableHubTransitionViewModel
@Inject
constructor(
animationFlow: KeyguardTransitionAnimationFlow,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow
@@ -49,15 +50,13 @@ constructor(
duration = TO_GLANCEABLE_HUB_DURATION,
edge = Edge.create(from = DREAMING, to = Scenes.Communal),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB))
val dreamOverlayTranslationX: Flow<Float> =
configurationInteractor
.directionalDimensionPixelSize(
LayoutDirection.LTR,
- R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x
+ R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x,
)
.flatMapLatest { translatePx ->
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index f69f9969ea37..723fba6d976e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -41,7 +42,7 @@ class GlanceableHubToDreamingTransitionViewModel
@Inject
constructor(
animationFlow: KeyguardTransitionAnimationFlow,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
@@ -50,9 +51,7 @@ constructor(
duration = FROM_GLANCEABLE_HUB_DURATION,
edge = Edge.create(from = Scenes.Communal, to = DREAMING),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING))
val dreamOverlayAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
@@ -66,7 +65,7 @@ constructor(
configurationInteractor
.directionalDimensionPixelSize(
LayoutDirection.LTR,
- R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x
+ R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x,
)
.flatMapLatest { translatePx: Int ->
transitionAnimation.sharedFlow(
@@ -74,7 +73,7 @@ constructor(
onStep = { value -> -translatePx + value * translatePx },
interpolator = Interpolators.EMPHASIZED,
onCancel = { -translatePx.toFloat() },
- name = "GLANCEABLE_HUB->LOCKSCREEN: dreamOverlayTranslationX"
+ name = "GLANCEABLE_HUB->LOCKSCREEN: dreamOverlayTranslationX",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index 67b009e50ce0..5a4d0689d209 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +46,7 @@ import kotlinx.coroutines.flow.map
class GlanceableHubToLockscreenTransitionViewModel
@Inject
constructor(
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
@@ -54,9 +55,7 @@ constructor(
duration = TO_LOCKSCREEN_DURATION,
edge = Edge.create(from = Scenes.Communal, to = LOCKSCREEN),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN))
val keyguardAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
@@ -75,7 +74,7 @@ constructor(
configurationInteractor
.directionalDimensionPixelSize(
LayoutDirection.LTR,
- R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x
+ R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
)
.flatMapLatest { translatePx: Int ->
transitionAnimation.sharedFlowWithState(
@@ -87,7 +86,7 @@ constructor(
// is cancelled.
onFinish = { 0f },
onCancel = { 0f },
- name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardTranslationX"
+ name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardTranslationX",
)
}
@@ -95,6 +94,8 @@ constructor(
val shortcutsAlpha: Flow<Float> = keyguardAlpha
+ val statusBarAlpha: Flow<Float> = keyguardAlpha
+
val notificationTranslationX: Flow<Float> =
keyguardTranslationX.map { it.value }.filterNotNull()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 36f684ee4759..3a7a640be85f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.ClockSize
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.ui.SystemBarUtilsProxy
@@ -50,7 +51,8 @@ constructor(
aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
@get:VisibleForTesting val shadeInteractor: ShadeInteractor,
private val systemBarUtils: SystemBarUtilsProxy,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+ // TODO: b/374267505 - Use ShadeDisplayAware resources here.
@Main private val resources: Resources,
) {
var burnInLayer: Layer? = null
@@ -179,12 +181,15 @@ constructor(
fun getLargeClockTopMargin(): Int {
return systemBarUtils.getStatusBarHeight() +
resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
- resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+ resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset)
}
val largeClockTopMargin: Flow<Int> =
configurationInteractor.onAnyConfigurationChange.map { getLargeClockTopMargin() }
+ val largeClockTextSize: Flow<Int> =
+ configurationInteractor.dimensionPixelSize(customR.dimen.large_clock_text_size)
+
enum class ClockLayout {
LARGE_CLOCK,
SMALL_CLOCK,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index ceae1b5e9038..bc3ef02a0ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import javax.inject.Inject
import javax.inject.Named
@@ -53,7 +54,7 @@ constructor(
burnInInteractor: BurnInInteractor,
@Named(KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE)
shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
communalSceneInteractor: CommunalSceneInteractor,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -70,7 +71,7 @@ constructor(
val visible: Flow<Boolean> =
anyOf(
keyguardInteractor.statusBarState.map { state -> state == StatusBarState.KEYGUARD },
- communalSceneInteractor.isCommunalVisible
+ communalSceneInteractor.isCommunalVisible,
)
/** An observable for whether the indication area should be padded. */
@@ -85,7 +86,7 @@ constructor(
} else {
combine(
keyguardBottomAreaViewModel.startButton,
- keyguardBottomAreaViewModel.endButton
+ keyguardBottomAreaViewModel.endButton,
) { startButtonModel, endButtonModel ->
startButtonModel.isVisible || endButtonModel.isVisible
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index 6579ea162ee2..65c0f57b76f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.res.R
@@ -39,20 +40,16 @@ constructor(
val selectedClockSize: StateFlow<ClockSizeSetting> = interactor.selectedClockSize
val shouldHideSmartspace: Flow<Boolean> =
- combine(
- interactor.selectedClockSize,
- interactor.currentClockId,
- ::Pair,
- )
- .map { (size, currentClockId) ->
- when (size) {
- // TODO (b/284122375) This is temporary. We should use clockController
- // .largeClock.config.hasCustomWeatherDataDisplay instead, but
- // ClockRegistry.createCurrentClock is not reliable.
- ClockSizeSetting.DYNAMIC -> currentClockId == "DIGITAL_CLOCK_WEATHER"
- ClockSizeSetting.SMALL -> false
- }
+ combine(interactor.selectedClockSize, interactor.currentClockId, ::Pair).map {
+ (size, currentClockId) ->
+ when (size) {
+ // TODO (b/284122375) This is temporary. We should use clockController
+ // .largeClock.config.hasCustomWeatherDataDisplay instead, but
+ // ClockRegistry.createCurrentClock is not reliable.
+ ClockSizeSetting.DYNAMIC -> currentClockId == "DIGITAL_CLOCK_WEATHER"
+ ClockSizeSetting.SMALL -> false
}
+ }
fun getSmartspaceStartPadding(context: Context): Int {
return KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context)
@@ -83,7 +80,7 @@ constructor(
} else {
getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
SystemBarUtils.getStatusBarHeight(context) +
- getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+ getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 850e943d17eb..ef6ae0dd6427 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -17,8 +17,10 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.res.Resources
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.customization.R as customR
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -42,7 +44,6 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
class LockscreenContentViewModel
@AssistedInject
@@ -82,10 +83,7 @@ constructor(
unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true),
unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false),
) { start, end ->
- UnfoldTranslations(
- start = start,
- end = end,
- )
+ UnfoldTranslations(start = start, end = end)
}
.collect { _unfoldTranslations.value = it }
}
@@ -102,17 +100,15 @@ constructor(
/** Returns a flow that indicates whether lockscreen notifications should be rendered. */
fun areNotificationsVisible(): Flow<Boolean> {
- return combine(
- clockSize,
- shadeInteractor.isShadeLayoutWide,
- ) { clockSize, isShadeLayoutWide ->
+ return combine(clockSize, shadeInteractor.isShadeLayoutWide) { clockSize, isShadeLayoutWide
+ ->
clockSize == ClockSize.SMALL || isShadeLayoutWide
}
}
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (clockSize.value == ClockSize.LARGE) {
- resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
+ resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset) +
resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
} else {
0
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index 378374e72c8b..acaa9b918da8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +46,7 @@ import kotlinx.coroutines.flow.map
class LockscreenToGlanceableHubTransitionViewModel
@Inject
constructor(
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
@@ -54,9 +55,7 @@ constructor(
duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
edge = Edge.create(from = LOCKSCREEN, to = Scenes.Communal),
)
- .setupWithoutSceneContainer(
- edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB),
- )
+ .setupWithoutSceneContainer(edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB))
val keyguardAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
@@ -74,7 +73,7 @@ constructor(
configurationInteractor
.directionalDimensionPixelSize(
LayoutDirection.LTR,
- R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x
+ R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x,
)
.flatMapLatest { translatePx: Int ->
transitionAnimation.sharedFlowWithState(
@@ -86,7 +85,7 @@ constructor(
onFinish = { 0f },
onCancel = { 0f },
interpolator = EMPHASIZED,
- name = "LOCKSCREEN->GLANCEABLE_HUB: keyguardTranslationX"
+ name = "LOCKSCREEN->GLANCEABLE_HUB: keyguardTranslationX",
)
}
@@ -94,6 +93,8 @@ constructor(
val shortcutsAlpha: Flow<Float> = keyguardAlpha
+ val statusBarAlpha: Flow<Float> = keyguardAlpha
+
val notificationTranslationX: Flow<Float> =
keyguardTranslationX.map { it.value }.filterNotNull()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index 88e8968501dd..6565e31c2c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -26,6 +26,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -40,7 +41,7 @@ class LockscreenToOccludedTransitionViewModel
@Inject
constructor(
shadeDependentFlows: ShadeDependentFlows,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 737bd7ac218d..d10970f28995 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -49,7 +50,7 @@ class OccludedToLockscreenTransitionViewModel
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -104,7 +105,7 @@ constructor(
!isOccluded &&
keyguardTransitionInteractor.getCurrentState() == OCCLUDED
}
- .map { 0f }
+ .map { 0f },
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
new file mode 100644
index 000000000000..913aa6f9d547
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.domain.pipeline
+
+import android.content.Context
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.Drawable
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import androidx.annotation.WorkerThread
+import androidx.media.utils.MediaConstants
+import androidx.media3.common.Player
+import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController as Media3Controller
+import androidx.media3.session.SessionCommand
+import androidx.media3.session.SessionToken
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.media.controls.shared.MediaControlDrawables
+import com.android.systemui.media.controls.shared.MediaLogger
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.util.MediaControllerFactory
+import com.android.systemui.media.controls.util.SessionTokenFactory
+import com.android.systemui.res.R
+import com.android.systemui.util.concurrency.Execution
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+private const val TAG = "Media3ActionFactory"
+
+@SysUISingleton
+class Media3ActionFactory
+@Inject
+constructor(
+ @Application val context: Context,
+ private val imageLoader: ImageLoader,
+ private val controllerFactory: MediaControllerFactory,
+ private val tokenFactory: SessionTokenFactory,
+ private val logger: MediaLogger,
+ @Background private val looper: Looper,
+ @Background private val handler: Handler,
+ @Background private val bgScope: CoroutineScope,
+ private val execution: Execution,
+) {
+
+ /**
+ * Generates action button info for this media session based on the Media3 session info
+ *
+ * @param packageName Package name for the media app
+ * @param controller The framework [MediaController] for the session
+ * @return The media action buttons, or null if the session token is null
+ */
+ suspend fun createActionsFromSession(
+ packageName: String,
+ sessionToken: MediaSession.Token,
+ ): MediaButton? {
+ // Get the Media3 controller using the legacy token
+ val token = tokenFactory.createTokenFromLegacy(sessionToken)
+ val m3controller = controllerFactory.create(token, looper)
+
+ // Build button info
+ val buttons = suspendCancellableCoroutine { continuation ->
+ // Media3Controller methods must always be called from a specific looper
+ val runnable = Runnable {
+ try {
+ val result = getMedia3Actions(packageName, m3controller, token)
+ continuation.resumeWith(Result.success(result))
+ } finally {
+ m3controller.release()
+ }
+ }
+ handler.post(runnable)
+ continuation.invokeOnCancellation {
+ // Ensure controller is released, even if loading was cancelled partway through
+ handler.post(m3controller::release)
+ handler.removeCallbacks(runnable)
+ }
+ }
+ return buttons
+ }
+
+ /** This method must be called on the Media3 looper! */
+ @WorkerThread
+ private fun getMedia3Actions(
+ packageName: String,
+ m3controller: Media3Controller,
+ token: SessionToken,
+ ): MediaButton? {
+ require(!execution.isMainThread())
+
+ // First, get standard actions
+ val playOrPause =
+ if (m3controller.playbackState == Player.STATE_BUFFERING) {
+ // Spinner needs to be animating to render anything. Start it here.
+ val drawable =
+ context.getDrawable(com.android.internal.R.drawable.progress_small_material)
+ (drawable as Animatable).start()
+ MediaAction(
+ drawable,
+ null, // no action to perform when clicked
+ context.getString(R.string.controls_media_button_connecting),
+ context.getDrawable(R.drawable.ic_media_connecting_container),
+ // Specify a rebind id to prevent the spinner from restarting on later binds.
+ com.android.internal.R.drawable.progress_small_material,
+ )
+ } else {
+ getStandardAction(m3controller, token, Player.COMMAND_PLAY_PAUSE)
+ }
+
+ val prevButton =
+ getStandardAction(
+ m3controller,
+ token,
+ Player.COMMAND_SEEK_TO_PREVIOUS,
+ Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
+ )
+ val nextButton =
+ getStandardAction(
+ m3controller,
+ token,
+ Player.COMMAND_SEEK_TO_NEXT,
+ Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
+ )
+
+ // Then, get custom actions
+ var customActions =
+ m3controller.customLayout
+ .asSequence()
+ .filter {
+ it.isEnabled &&
+ it.sessionCommand?.commandCode == SessionCommand.COMMAND_CODE_CUSTOM &&
+ m3controller.isSessionCommandAvailable(it.sessionCommand!!)
+ }
+ .map { getCustomAction(packageName, token, it) }
+ .iterator()
+ fun nextCustomAction() = if (customActions.hasNext()) customActions.next() else null
+
+ // Finally, assign the remaining button slots: play/pause A B C D
+ // A = previous, else custom action (if not reserved)
+ // B = next, else custom action (if not reserved)
+ // C and D are always custom actions
+ val reservePrev =
+ m3controller.sessionExtras.getBoolean(
+ MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV,
+ false,
+ )
+ val reserveNext =
+ m3controller.sessionExtras.getBoolean(
+ MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT,
+ false,
+ )
+
+ val prevOrCustom =
+ prevButton
+ ?: if (reservePrev) {
+ null
+ } else {
+ nextCustomAction()
+ }
+
+ val nextOrCustom =
+ nextButton
+ ?: if (reserveNext) {
+ null
+ } else {
+ nextCustomAction()
+ }
+
+ return MediaButton(
+ playOrPause = playOrPause,
+ nextOrCustom = nextOrCustom,
+ prevOrCustom = prevOrCustom,
+ custom0 = nextCustomAction(),
+ custom1 = nextCustomAction(),
+ reserveNext = reserveNext,
+ reservePrev = reservePrev,
+ )
+ }
+
+ /**
+ * Create a [MediaAction] for a given command, if supported
+ *
+ * @param controller Media3 controller for the session
+ * @param commands Commands to check, in priority order
+ * @return A [MediaAction] representing the first supported command, or null if not supported
+ */
+ private fun getStandardAction(
+ controller: Media3Controller,
+ token: SessionToken,
+ vararg commands: @Player.Command Int,
+ ): MediaAction? {
+ for (command in commands) {
+ if (!controller.isCommandAvailable(command)) {
+ continue
+ }
+
+ return when (command) {
+ Player.COMMAND_PLAY_PAUSE -> {
+ if (!controller.isPlaying) {
+ MediaAction(
+ context.getDrawable(R.drawable.ic_media_play),
+ { executeAction(token, Player.COMMAND_PLAY_PAUSE) },
+ context.getString(R.string.controls_media_button_play),
+ context.getDrawable(R.drawable.ic_media_play_container),
+ )
+ } else {
+ MediaAction(
+ context.getDrawable(R.drawable.ic_media_pause),
+ { executeAction(token, Player.COMMAND_PLAY_PAUSE) },
+ context.getString(R.string.controls_media_button_pause),
+ context.getDrawable(R.drawable.ic_media_pause_container),
+ )
+ }
+ }
+ else -> {
+ MediaAction(
+ icon = getIconForAction(command),
+ action = { executeAction(token, command) },
+ contentDescription = getDescriptionForAction(command),
+ background = null,
+ )
+ }
+ }
+ }
+ return null
+ }
+
+ /** Get a [MediaAction] representing a [CommandButton] */
+ private fun getCustomAction(
+ packageName: String,
+ token: SessionToken,
+ customAction: CommandButton,
+ ): MediaAction {
+ return MediaAction(
+ getIconForAction(customAction, packageName),
+ { executeAction(token, Player.COMMAND_INVALID, customAction) },
+ customAction.displayName,
+ null,
+ )
+ }
+
+ private fun getIconForAction(command: @Player.Command Int): Drawable? {
+ return when (command) {
+ Player.COMMAND_SEEK_TO_PREVIOUS -> MediaControlDrawables.getPrevIcon(context)
+ Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM -> MediaControlDrawables.getPrevIcon(context)
+ Player.COMMAND_SEEK_TO_NEXT -> MediaControlDrawables.getNextIcon(context)
+ Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM -> MediaControlDrawables.getNextIcon(context)
+ else -> {
+ Log.e(TAG, "Unknown icon for $command")
+ null
+ }
+ }
+ }
+
+ private fun getIconForAction(customAction: CommandButton, packageName: String): Drawable? {
+ val size = context.resources.getDimensionPixelSize(R.dimen.min_clickable_item_size)
+ // TODO(b/360196209): check customAction.icon field to use platform icons
+ if (customAction.iconResId != 0) {
+ val packageContext = context.createPackageContext(packageName, 0)
+ val source = ImageLoader.Res(customAction.iconResId, packageContext)
+ return runBlocking { imageLoader.loadDrawable(source, size, size) }
+ }
+
+ if (customAction.iconUri != null) {
+ val source = ImageLoader.Uri(customAction.iconUri!!)
+ return runBlocking { imageLoader.loadDrawable(source, size, size) }
+ }
+ return null
+ }
+
+ private fun getDescriptionForAction(command: @Player.Command Int): String? {
+ return when (command) {
+ Player.COMMAND_SEEK_TO_PREVIOUS ->
+ context.getString(R.string.controls_media_button_prev)
+ Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ->
+ context.getString(R.string.controls_media_button_prev)
+ Player.COMMAND_SEEK_TO_NEXT -> context.getString(R.string.controls_media_button_next)
+ Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM ->
+ context.getString(R.string.controls_media_button_next)
+ else -> {
+ Log.e(TAG, "Unknown content description for $command")
+ null
+ }
+ }
+ }
+
+ private fun executeAction(
+ token: SessionToken,
+ command: Int,
+ customAction: CommandButton? = null,
+ ) {
+ bgScope.launch {
+ val controller = controllerFactory.create(token, looper)
+ handler.post {
+ try {
+ when (command) {
+ Player.COMMAND_PLAY_PAUSE -> {
+ if (controller.isPlaying) controller.pause() else controller.play()
+ }
+
+ Player.COMMAND_SEEK_TO_PREVIOUS -> controller.seekToPrevious()
+ Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ->
+ controller.seekToPreviousMediaItem()
+
+ Player.COMMAND_SEEK_TO_NEXT -> controller.seekToNext()
+ Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM -> controller.seekToNextMediaItem()
+ Player.COMMAND_INVALID -> {
+ if (customAction?.sessionCommand != null) {
+ val sessionCommand = customAction.sessionCommand!!
+ if (controller.isSessionCommandAvailable(sessionCommand)) {
+ controller.sendCustomCommand(
+ sessionCommand,
+ customAction.extras,
+ )
+ } else {
+ logger.logMedia3UnsupportedCommand(
+ "$sessionCommand, action $customAction"
+ )
+ }
+ } else {
+ logger.logMedia3UnsupportedCommand("$command, action $customAction")
+ }
+ }
+ else -> logger.logMedia3UnsupportedCommand(command.toString())
+ }
+ } finally {
+ controller.release()
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
index 591a9cccdadd..a176e0c1c2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -84,6 +84,7 @@ constructor(
private val mediaFlags: MediaFlags,
private val imageLoader: ImageLoader,
private val statusBarManager: StatusBarManager,
+ private val media3ActionFactory: Media3ActionFactory,
) {
private val mediaProcessingJobs = ConcurrentHashMap<String, Job>()
@@ -364,7 +365,7 @@ constructor(
)
}
- private fun createActionsFromState(
+ private suspend fun createActionsFromState(
packageName: String,
controller: MediaController,
user: UserHandle,
@@ -373,6 +374,12 @@ constructor(
return null
}
+ if (mediaFlags.areMedia3ActionsEnabled(packageName, user)) {
+ return media3ActionFactory.createActionsFromSession(
+ packageName,
+ controller.sessionToken,
+ )
+ }
return createActionsFromState(context, packageName, controller)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
index 2bdee67dd57a..beb4d4103b11 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
@@ -141,6 +141,7 @@ private fun areActionsEqual(
new: MediaData,
old: MediaData,
): Boolean {
+ // TODO(b/360196209): account for actions generated from media3
val oldState = MediaController(context, old.token!!).playbackState
return if (
new.semanticActions == null &&
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
index 88c47ba4d243..0b598c13311f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
@@ -140,6 +140,10 @@ class MediaLogger @Inject constructor(@MediaLog private val buffer: LogBuffer) {
)
}
+ fun logMedia3UnsupportedCommand(command: String) {
+ buffer.log(TAG, LogLevel.DEBUG, { str1 = command }, { "Unsupported media3 command $str1" })
+ }
+
companion object {
private const val TAG = "MediaLog"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.java
deleted file mode 100644
index 6caf5c20b81c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 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.media.controls.util;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-
-import javax.inject.Inject;
-
-/**
- * Testable wrapper around {@link MediaController} constructor.
- */
-public class MediaControllerFactory {
-
- private final Context mContext;
-
- @Inject
- public MediaControllerFactory(Context context) {
- mContext = context;
- }
-
- /**
- * Creates a new MediaController from a session's token.
- *
- * @param token The token for the session. This value must never be null.
- */
- public MediaController create(@NonNull MediaSession.Token token) {
- return new MediaController(mContext, token);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
new file mode 100644
index 000000000000..d815852b790f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.media.controls.util
+
+import android.content.Context
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.os.Looper
+import androidx.concurrent.futures.await
+import androidx.media3.session.MediaController as Media3Controller
+import androidx.media3.session.SessionToken
+import javax.inject.Inject
+
+/** Testable wrapper for media controller construction */
+open class MediaControllerFactory @Inject constructor(private val context: Context) {
+ /**
+ * Creates a new [MediaController] from the framework session token.
+ *
+ * @param token The token for the session. This value must never be null.
+ */
+ open fun create(token: MediaSession.Token): MediaController {
+ return MediaController(context, token)
+ }
+
+ /**
+ * Creates a new [Media3Controller] from the media3 [SessionToken].
+ *
+ * @param token The token for the session
+ * @param looper The looper that will be used for this controller's operations
+ */
+ open suspend fun create(token: SessionToken, looper: Looper): Media3Controller {
+ return Media3Controller.Builder(context, token)
+ .setApplicationLooper(looper)
+ .buildAsync()
+ .await()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index d4af1b546369..ac60c47ee6ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -18,9 +18,10 @@ package com.android.systemui.media.controls.util
import android.app.StatusBarManager
import android.os.UserHandle
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags as FlagsClassic
import javax.inject.Inject
@SysUISingleton
@@ -29,22 +30,29 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlagsClass
* Check whether media control actions should be based on PlaybackState instead of notification
*/
fun areMediaSessionActionsEnabled(packageName: String, user: UserHandle): Boolean {
- // Allow global override with flag
return StatusBarManager.useMediaSessionActionsForApp(packageName, user)
}
+ /** Check whether media control actions should be derived from Media3 controller */
+ fun areMedia3ActionsEnabled(packageName: String, user: UserHandle): Boolean {
+ val compatFlag = StatusBarManager.useMedia3ControllerForApp(packageName, user)
+ val featureFlag = Flags.mediaControlsButtonMedia3()
+ return featureFlag && compatFlag
+ }
+
/**
* If true, keep active media controls for the lifetime of the MediaSession, regardless of
* whether the underlying notification was dismissed
*/
- fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
+ fun isRetainingPlayersEnabled() = featureFlags.isEnabled(FlagsClassic.MEDIA_RETAIN_SESSIONS)
/** Check whether to get progress information for resume players */
- fun isResumeProgressEnabled() = featureFlags.isEnabled(Flags.MEDIA_RESUME_PROGRESS)
+ fun isResumeProgressEnabled() = featureFlags.isEnabled(FlagsClassic.MEDIA_RESUME_PROGRESS)
/** If true, do not automatically dismiss the recommendation card */
- fun isPersistentSsCardEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_RECOMMENDATIONS)
+ fun isPersistentSsCardEnabled() =
+ featureFlags.isEnabled(FlagsClassic.MEDIA_RETAIN_RECOMMENDATIONS)
/** Check whether we allow remote media to generate resume controls */
- fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
+ fun isRemoteResumeAllowed() = featureFlags.isEnabled(FlagsClassic.MEDIA_REMOTE_RESUME)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/SessionTokenFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/SessionTokenFactory.kt
new file mode 100644
index 000000000000..b289fd40a3a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/SessionTokenFactory.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import android.content.Context
+import android.media.session.MediaSession
+import androidx.concurrent.futures.await
+import androidx.media3.session.SessionToken
+import javax.inject.Inject
+
+/** Testable wrapper for [SessionToken] creation */
+open class SessionTokenFactory @Inject constructor(private val context: Context) {
+ /** Create a new [SessionToken] from the framework [MediaSession.Token] */
+ open suspend fun createTokenFromLegacy(token: MediaSession.Token): SessionToken {
+ return SessionToken.createSessionToken(context, token).await()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 36a9fb3eb753..45a3a8ce60c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -27,17 +27,12 @@ import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager;
import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
-import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
-import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderLogBuffer;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-
import javax.inject.Named;
/** Dagger module for the media package. */
@@ -132,16 +127,4 @@ public interface MediaModule {
static LogBuffer provideMediaTttReceiverLogBuffer(LogBufferFactory factory) {
return factory.create("MediaTttReceiver", 20);
}
-
- /** */
- @Provides
- @SysUISingleton
- static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
- MediaTttFlags mediaTttFlags,
- Lazy<MediaTttCommandLineHelper> helperLazy) {
- if (!mediaTttFlags.isMediaTttEnabled()) {
- return Optional.empty();
- }
- return Optional.of(helperLazy.get());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 92db804d2730..1204cde19c76 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -43,7 +43,6 @@ import com.android.systemui.common.ui.binder.TintedIconViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttIcon
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.res.R
@@ -68,25 +67,27 @@ import javax.inject.Inject
* TODO(b/245610654): Re-name this to be MediaTttReceiverCoordinator.
*/
@SysUISingleton
-open class MediaTttChipControllerReceiver @Inject constructor(
- private val commandQueue: CommandQueue,
- context: Context,
- logger: MediaTttReceiverLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
- @Main mainExecutor: DelayableExecutor,
- accessibilityManager: AccessibilityManager,
- configurationController: ConfigurationController,
- dumpManager: DumpManager,
- powerManager: PowerManager,
- @Main private val mainHandler: Handler,
- private val mediaTttFlags: MediaTttFlags,
- private val uiEventLogger: MediaTttReceiverUiEventLogger,
- private val viewUtil: ViewUtil,
- wakeLockBuilder: WakeLock.Builder,
- systemClock: SystemClock,
- private val rippleController: MediaTttReceiverRippleController,
- private val temporaryViewUiEventLogger: TemporaryViewUiEventLogger,
-) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
+open class MediaTttChipControllerReceiver
+@Inject
+constructor(
+ private val commandQueue: CommandQueue,
+ context: Context,
+ logger: MediaTttReceiverLogger,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ @Main mainExecutor: DelayableExecutor,
+ accessibilityManager: AccessibilityManager,
+ configurationController: ConfigurationController,
+ dumpManager: DumpManager,
+ powerManager: PowerManager,
+ @Main private val mainHandler: Handler,
+ private val uiEventLogger: MediaTttReceiverUiEventLogger,
+ private val viewUtil: ViewUtil,
+ wakeLockBuilder: WakeLock.Builder,
+ systemClock: SystemClock,
+ private val rippleController: MediaTttReceiverRippleController,
+ private val temporaryViewUiEventLogger: TemporaryViewUiEventLogger,
+) :
+ TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
viewCaptureAwareWindowManager,
@@ -99,36 +100,43 @@ open class MediaTttChipControllerReceiver @Inject constructor(
wakeLockBuilder,
systemClock,
temporaryViewUiEventLogger,
-) {
+ ) {
@SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- override val windowLayoutParams = commonWindowLayoutParams.apply {
- gravity = Gravity.BOTTOM.or(Gravity.CENTER_HORIZONTAL)
- // Params below are needed for the ripple to work correctly
- width = WindowManager.LayoutParams.MATCH_PARENT
- height = WindowManager.LayoutParams.MATCH_PARENT
- layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- fitInsetsTypes = 0 // Ignore insets from all system bars
- }
+ override val windowLayoutParams =
+ commonWindowLayoutParams.apply {
+ gravity = Gravity.BOTTOM.or(Gravity.CENTER_HORIZONTAL)
+ // Params below are needed for the ripple to work correctly
+ width = WindowManager.LayoutParams.MATCH_PARENT
+ height = WindowManager.LayoutParams.MATCH_PARENT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ fitInsetsTypes = 0 // Ignore insets from all system bars
+ }
// Value animator that controls the bouncing animation of views.
- private val bounceAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
- repeatCount = ValueAnimator.INFINITE
- repeatMode = ValueAnimator.REVERSE
- duration = ICON_BOUNCE_ANIM_DURATION
- }
+ private val bounceAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ repeatCount = ValueAnimator.INFINITE
+ repeatMode = ValueAnimator.REVERSE
+ duration = ICON_BOUNCE_ANIM_DURATION
+ }
- private val commandQueueCallbacks = object : CommandQueue.Callbacks {
- override fun updateMediaTapToTransferReceiverDisplay(
- @StatusBarManager.MediaTransferReceiverState displayState: Int,
- routeInfo: MediaRoute2Info,
- appIcon: Icon?,
- appName: CharSequence?
- ) {
- this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay(
- displayState, routeInfo, appIcon, appName
- )
+ private val commandQueueCallbacks =
+ object : CommandQueue.Callbacks {
+ override fun updateMediaTapToTransferReceiverDisplay(
+ @StatusBarManager.MediaTransferReceiverState displayState: Int,
+ routeInfo: MediaRoute2Info,
+ appIcon: Icon?,
+ appName: CharSequence?,
+ ) {
+ this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay(
+ displayState,
+ routeInfo,
+ appIcon,
+ appName,
+ )
+ }
}
- }
// A map to store instance id per route info id.
private var instanceMap: MutableMap<String, InstanceId> = mutableMapOf()
@@ -139,7 +147,7 @@ open class MediaTttChipControllerReceiver @Inject constructor(
@StatusBarManager.MediaTransferReceiverState displayState: Int,
routeInfo: MediaRoute2Info,
appIcon: Icon?,
- appName: CharSequence?
+ appName: CharSequence?,
) {
val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
val stateName = chipState?.name ?: "Invalid"
@@ -150,8 +158,8 @@ open class MediaTttChipControllerReceiver @Inject constructor(
return
}
- val instanceId: InstanceId = instanceMap[routeInfo.id]
- ?: temporaryViewUiEventLogger.getNewInstanceId()
+ val instanceId: InstanceId =
+ instanceMap[routeInfo.id] ?: temporaryViewUiEventLogger.getNewInstanceId()
uiEventLogger.logReceiverStateChange(chipState, instanceId)
if (chipState != ChipStateReceiver.CLOSE_TO_SENDER) {
@@ -175,53 +183,51 @@ open class MediaTttChipControllerReceiver @Inject constructor(
}
appIcon.loadDrawableAsync(
- context,
- Icon.OnDrawableLoadedListener { drawable ->
- displayView(
- ChipReceiverInfo(
- routeInfo,
- drawable,
- appName,
- id = routeInfo.id,
- instanceId = instanceId,
- )
+ context,
+ Icon.OnDrawableLoadedListener { drawable ->
+ displayView(
+ ChipReceiverInfo(
+ routeInfo,
+ drawable,
+ appName,
+ id = routeInfo.id,
+ instanceId = instanceId,
)
- },
- // Notify the listener on the main handler since the listener will update
- // the UI.
- mainHandler
+ )
+ },
+ // Notify the listener on the main handler since the listener will update
+ // the UI.
+ mainHandler,
)
}
override fun start() {
super.start()
- if (mediaTttFlags.isMediaTttEnabled()) {
- commandQueue.addCallback(commandQueueCallbacks)
- }
+ commandQueue.addCallback(commandQueueCallbacks)
registerListener(displayListener)
}
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
val packageName: String? = newInfo.routeInfo.clientPackageName
- var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
- context,
- packageName,
- isReceiver = true,
- ) {
- packageName?.let { logger.logPackageNotFound(it) }
- }
+ var iconInfo =
+ MediaTttUtils.getIconInfoFromPackageName(context, packageName, isReceiver = true) {
+ packageName?.let { logger.logPackageNotFound(it) }
+ }
if (newInfo.appNameOverride != null) {
- iconInfo = iconInfo.copy(
- contentDescription = ContentDescription.Loaded(newInfo.appNameOverride.toString())
- )
+ iconInfo =
+ iconInfo.copy(
+ contentDescription =
+ ContentDescription.Loaded(newInfo.appNameOverride.toString())
+ )
}
if (newInfo.appIconDrawableOverride != null) {
- iconInfo = iconInfo.copy(
- icon = MediaTttIcon.Loaded(newInfo.appIconDrawableOverride),
- isAppIcon = true,
- )
+ iconInfo =
+ iconInfo.copy(
+ icon = MediaTttIcon.Loaded(newInfo.appIconDrawableOverride),
+ isAppIcon = true,
+ )
}
val iconPadding =
@@ -298,16 +304,14 @@ open class MediaTttChipControllerReceiver @Inject constructor(
alphaDuration: Long = ICON_ALPHA_ANIM_DURATION,
onAnimationEnd: Runnable? = null,
) {
- view.animate()
+ view
+ .animate()
.translationYBy(translationYBy)
.setInterpolator(interpolator)
.setDuration(translationDuration)
.withEndAction { onAnimationEnd?.run() }
.start()
- view.animate()
- .alpha(alphaEndValue)
- .setDuration(alphaDuration)
- .start()
+ view.animate().alpha(alphaEndValue).setDuration(alphaDuration).start()
}
/** Returns the amount that the chip will be translated by in its intro animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 3e6d46c00df9..6ca04710d74c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -28,7 +28,6 @@ import com.android.systemui.Dumpable
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
@@ -53,7 +52,6 @@ constructor(
private val context: Context,
private val dumpManager: DumpManager,
private val logger: MediaTttSenderLogger,
- private val mediaTttFlags: MediaTttFlags,
private val uiEventLogger: MediaTttSenderUiEventLogger,
) : CoreStartable, Dumpable {
@@ -68,27 +66,25 @@ constructor(
override fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
routeInfo: MediaRoute2Info,
- undoCallback: IUndoMediaTransferCallback?
+ undoCallback: IUndoMediaTransferCallback?,
) {
this@MediaTttSenderCoordinator.updateMediaTapToTransferSenderDisplay(
displayState,
routeInfo,
- undoCallback
+ undoCallback,
)
}
}
override fun start() {
- if (mediaTttFlags.isMediaTttEnabled()) {
- commandQueue.addCallback(commandQueueCallbacks)
- dumpManager.registerNormalDumpable(this)
- }
+ commandQueue.addCallback(commandQueueCallbacks)
+ dumpManager.registerNormalDumpable(this)
}
private fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
routeInfo: MediaRoute2Info,
- undoCallback: IUndoMediaTransferCallback?
+ undoCallback: IUndoMediaTransferCallback?,
) {
val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
val stateName = chipState?.name ?: "Invalid"
@@ -107,7 +103,7 @@ constructor(
// ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
logger.logInvalidStateTransitionError(
currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
- chipState.name
+ chipState.name,
)
return
}
@@ -126,7 +122,7 @@ constructor(
// still be able to see the status of the transfer.
logger.logRemovalBypass(
removalReason,
- bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
+ bypassReason = "transferStatus=${currentStateForId.transferStatus.name}",
)
return
}
@@ -139,14 +135,7 @@ constructor(
logger.logStateMap(stateMap)
chipbarCoordinator.registerListener(displayListener)
chipbarCoordinator.displayView(
- createChipbarInfo(
- chipState,
- routeInfo,
- undoCallback,
- context,
- logger,
- instanceId,
- )
+ createChipbarInfo(chipState, routeInfo, undoCallback, context, logger, instanceId)
)
}
}
@@ -245,10 +234,7 @@ constructor(
)
}
- return ChipbarEndItem.Button(
- Text.Resource(R.string.media_transfer_undo),
- onClickListener,
- )
+ return ChipbarEndItem.Button(Text.Resource(R.string.media_transfer_undo), onClickListener)
}
private val displayListener =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index bf2aa7efc0c4..56885c3eea9f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -18,7 +18,7 @@ package com.android.systemui.mediaprojection.appselector.data
import android.annotation.ColorInt
import android.annotation.UserIdInt
-import android.app.ActivityManager.RecentTaskInfo
+import android.app.TaskInfo
import android.content.ComponentName
import com.android.wm.shell.shared.split.SplitBounds
@@ -34,7 +34,7 @@ data class RecentTask(
val splitBounds: SplitBounds?,
) {
constructor(
- taskInfo: RecentTaskInfo,
+ taskInfo: TaskInfo,
isForegroundTask: Boolean,
userType: UserType,
splitBounds: SplitBounds? = null
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 82e58cc7f1d9..d94424c59376 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -23,7 +23,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.getOrNull
import com.android.wm.shell.recents.RecentTasks
-import com.android.wm.shell.shared.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.GroupedTaskInfo
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -51,7 +51,7 @@ constructor(
override suspend fun loadRecentTasks(): List<RecentTask> =
withContext(coroutineDispatcher) {
- val groupedTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+ val groupedTasks: List<GroupedTaskInfo> = recents?.getTasks() ?: emptyList()
// Note: the returned task list is from the most-recent to least-recent order.
// When opening the app selector in full screen, index 0 will be just the app selector
// activity and a null second task, so the foreground task will be index 1, but when
@@ -86,7 +86,7 @@ constructor(
}
}
- private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
+ private suspend fun RecentTasks.getTasks(): List<GroupedTaskInfo> =
suspendCoroutine { continuation ->
getRecentTasks(
Integer.MAX_VALUE,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 47dacae6e0a0..2fda2013d6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -57,7 +57,6 @@ import android.view.Display;
import android.view.Window;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
import com.android.systemui.mediaprojection.MediaProjectionUtils;
@@ -187,11 +186,9 @@ public class MediaProjectionPermissionActivity extends Activity {
return;
}
- if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) {
- if (showScreenCaptureDisabledDialogIfNeeded()) {
- finishAsCancelled();
- return;
- }
+ if (showScreenCaptureDisabledDialogIfNeeded()) {
+ finishAsCancelled();
+ return;
}
final String appName = extractAppName(aInfo, packageManager);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index b019c136b6ca..a3b7590117c1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -406,7 +406,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.checkNavBarModes();
} else {
- mTaskbarDelegate.checkNavBarModes();
+ mTaskbarDelegate.checkNavBarModes(displayId);
}
}
@@ -416,7 +416,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.finishBarAnimations();
} else {
- mTaskbarDelegate.finishBarAnimations();
+ mTaskbarDelegate.finishBarAnimations(displayId);
}
}
@@ -426,7 +426,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.touchAutoDim();
} else {
- mTaskbarDelegate.touchAutoDim();
+ mTaskbarDelegate.touchAutoDim(displayId);
}
}
@@ -436,7 +436,7 @@ public class NavigationBarControllerImpl implements
if (navBar != null) {
navBar.transitionTo(barMode, animate);
} else {
- mTaskbarDelegate.transitionTo(barMode, animate);
+ mTaskbarDelegate.transitionTo(displayId, barMode, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 1216a8879751..2a3aeae2a550 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -159,7 +159,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
@Override
public void synchronizeState() {
- checkNavBarModes();
+ checkNavBarModes(mDisplayId);
}
@Override
@@ -220,6 +220,16 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
}
+ @Override
+ public void onDisplayReady(int displayId) {
+ CommandQueue.Callbacks.super.onDisplayReady(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
+ }
+
// Separated into a method to keep setDependencies() clean/readable.
private LightBarTransitionsController createLightBarTransitionsController() {
@@ -349,31 +359,31 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
}
- void checkNavBarModes() {
+ void checkNavBarModes(int displayId) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().checkNavBarModes();
+ mOverviewProxyService.getProxy().checkNavBarModes(displayId);
} catch (RemoteException e) {
Log.e(TAG, "checkNavBarModes() failed", e);
}
}
- void finishBarAnimations() {
+ void finishBarAnimations(int displayId) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().finishBarAnimations();
+ mOverviewProxyService.getProxy().finishBarAnimations(displayId);
} catch (RemoteException e) {
Log.e(TAG, "finishBarAnimations() failed", e);
}
}
- void touchAutoDim() {
+ void touchAutoDim(int displayId) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
@@ -382,19 +392,19 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
int state = mStatusBarStateController.getState();
boolean shouldReset =
state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED;
- mOverviewProxyService.getProxy().touchAutoDim(shouldReset);
+ mOverviewProxyService.getProxy().touchAutoDim(displayId, shouldReset);
} catch (RemoteException e) {
Log.e(TAG, "touchAutoDim() failed", e);
}
}
- void transitionTo(@BarTransitions.TransitionMode int barMode, boolean animate) {
+ void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode, boolean animate) {
if (mOverviewProxyService.getProxy() == null) {
return;
}
try {
- mOverviewProxyService.getProxy().transitionTo(barMode, animate);
+ mOverviewProxyService.getProxy().transitionTo(displayId, barMode, animate);
} catch (RemoteException e) {
Log.e(TAG, "transitionTo() failed, barMode: " + barMode, e);
}
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 53177de89733..80ac2fcd4aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -696,7 +696,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mTaskStackListener);
DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
- mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
+ mPipOptional.ifPresent(pip -> pip.removeOnIsInPipStateChangedListener(
+ mOnIsInPipStateChangedListener));
try {
mWindowManagerService.unregisterSystemGestureExclusionListener(
@@ -720,7 +721,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mTaskStackListener);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
mUiThreadContext.getExecutor()::execute, mOnPropertiesChangedListener);
- mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(
+ mPipOptional.ifPresent(pip -> pip.addOnIsInPipStateChangedListener(
mOnIsInPipStateChangedListener));
mDesktopModeOptional.ifPresent(
dm -> dm.addDesktopGestureExclusionRegionListener(
@@ -1191,11 +1192,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
private void pilferPointers() {
- // Capture inputs
- mInputMonitor.pilferPointers();
- // Notify FalsingManager that an intentional gesture has occurred.
- mFalsingManager.isFalseTouch(BACK_GESTURE);
- mInputEventReceiver.setBatchingEnabled(true);
+ if (mInputMonitor != null) {
+ // Capture inputs
+ mInputMonitor.pilferPointers();
+ // Notify FalsingManager that an intentional gesture has occurred.
+ mFalsingManager.isFalseTouch(BACK_GESTURE);
+ mInputEventReceiver.setBatchingEnabled(true);
+ }
}
private boolean isButtonPressFromTrackpad(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 96c0cac53908..40613c0edc68 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -149,6 +149,7 @@ import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.data.repository.LightBarControllerStore;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -258,8 +259,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private boolean mTransientShownFromGestureOnSystemBar;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private LightBarController mLightBarController;
- private final LightBarController mMainLightBarController;
- private final LightBarController.Factory mLightBarControllerFactory;
+ private final LightBarControllerStore mLightBarControllerStore;
private AutoHideController mAutoHideController;
private final AutoHideController mMainAutoHideController;
private final AutoHideController.Factory mAutoHideControllerFactory;
@@ -580,8 +580,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
@Background Executor bgExecutor,
UiEventLogger uiEventLogger,
NavBarHelper navBarHelper,
- LightBarController mainLightBarController,
- LightBarController.Factory lightBarControllerFactory,
+ LightBarControllerStore lightBarControllerStore,
AutoHideController mainAutoHideController,
AutoHideController.Factory autoHideControllerFactory,
Optional<TelecomManager> telecomManagerOptional,
@@ -628,8 +627,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mUiEventLogger = uiEventLogger;
mNavBarHelper = navBarHelper;
mNotificationShadeDepthController = notificationShadeDepthController;
- mMainLightBarController = mainLightBarController;
- mLightBarControllerFactory = lightBarControllerFactory;
+ mLightBarControllerStore = lightBarControllerStore;
mMainAutoHideController = mainAutoHideController;
mAutoHideControllerFactory = autoHideControllerFactory;
mTelecomManagerOptional = telecomManagerOptional;
@@ -842,8 +840,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
// Unfortunately, we still need it because status bar needs LightBarController
// before notifications creation. We cannot directly use getLightBarController()
// from NavigationBarFragment directly.
- LightBarController lightBarController = mIsOnDefaultDisplay
- ? mMainLightBarController : mLightBarControllerFactory.create(mContext);
+ LightBarController lightBarController = mLightBarControllerStore.forDisplay(mDisplayId);
setLightBarController(lightBarController);
// TODO(b/118592525): to support multi-display, we start to add something which is
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d0f6f7961889..1fa5baaa21ae 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -118,9 +118,7 @@ constructor(
getUserForHandlingNotesTaking(entryPoint)
}
activityContext.startActivityAsUser(
- Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply {
- putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
- },
+ createNotesRoleHolderSettingsIntent(),
user
)
}
@@ -399,6 +397,10 @@ constructor(
* @see com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
*/
const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE = "extra_shortcut_badge_override_package"
+
+ /** Returns notes role holder settings intent. */
+ fun createNotesRoleHolderSettingsIntent() = Intent(Intent.ACTION_MANAGE_DEFAULT_APP).
+ putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index 442000281862..2d62c1067401 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -44,4 +44,7 @@ enum class NoteTaskEntryPoint {
/** @see [NoteTaskInitializer.callbacks] */
KEYBOARD_SHORTCUT,
+
+ /** @see [NotesTile] */
+ QS_NOTES_TILE,
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
index a79057e5464b..f152b01360df 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
@@ -19,6 +19,7 @@ import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
+import com.android.systemui.notetask.NoteTaskEntryPoint.QS_NOTES_TILE
import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
@@ -43,45 +44,47 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
/** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */
fun logNoteTaskOpened(info: NoteTaskInfo) {
val event =
- when (info.entryPoint) {
- TAIL_BUTTON -> {
- if (info.isKeyguardLocked) {
- NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
- } else {
- NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
- }
+ when (info.entryPoint) {
+ TAIL_BUTTON -> {
+ if (info.isKeyguardLocked) {
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ } else {
+ NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
}
+ }
- WIDGET_PICKER_SHORTCUT,
- WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT
+ WIDGET_PICKER_SHORTCUT,
+ WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT
- QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
- APP_CLIPS,
- KEYBOARD_SHORTCUT,
- null -> return
- }
+ QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
+ APP_CLIPS,
+ KEYBOARD_SHORTCUT,
+ QS_NOTES_TILE, // TODO(b/376640872): Add logging for QS Tile entry point.
+ null -> return
+ }
uiEventLogger.log(event, info.uid, info.packageName)
}
/** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */
fun logNoteTaskClosed(info: NoteTaskInfo) {
val event =
- when (info.entryPoint) {
- TAIL_BUTTON -> {
- if (info.isKeyguardLocked) {
- NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
- } else {
- NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
- }
+ when (info.entryPoint) {
+ TAIL_BUTTON -> {
+ if (info.isKeyguardLocked) {
+ NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED
+ } else {
+ NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
}
-
- WIDGET_PICKER_SHORTCUT,
- WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE,
- QUICK_AFFORDANCE,
- APP_CLIPS,
- KEYBOARD_SHORTCUT,
- null -> return
}
+
+ WIDGET_PICKER_SHORTCUT,
+ WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE,
+ QUICK_AFFORDANCE,
+ APP_CLIPS,
+ KEYBOARD_SHORTCUT,
+ QS_NOTES_TILE,
+ null -> return
+ }
uiEventLogger.log(event, info.uid, info.packageName)
}
@@ -90,19 +93,20 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
@UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.")
NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294),
-
- @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was unlocked.") // ktlint-disable max-line-length
+ @UiEvent(
+ doc =
+ "User opened a note by pressing the stylus tail button while the screen was unlocked."
+ )
NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295),
-
- @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was locked.") // ktlint-disable max-line-length
+ @UiEvent(
+ doc =
+ "User opened a note by pressing the stylus tail button while the screen was locked."
+ )
NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296),
-
@UiEvent(doc = "User opened a note by tapping on an app shortcut.")
NOTE_OPENED_VIA_SHORTCUT(1297),
-
@UiEvent(doc = "Note closed via a tail button while device is unlocked")
NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON(1311),
-
@UiEvent(doc = "Note closed via a tail button while device is locked")
NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1312);
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index c7aae3ca788e..a1c5c9c682c3 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -27,11 +27,27 @@ import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesSe
import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.NotesTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
/** Compose all dependencies required by Note Task feature. */
@Module(includes = [NoteTaskQuickAffordanceModule::class])
@@ -54,8 +70,22 @@ interface NoteTaskModule {
@[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity
+ @Binds
+ @IntoMap
+ @StringKey(NOTES_TILE_SPEC)
+ fun provideNotesAvailabilityInteractor(
+ impl: NotesTileDataInteractor
+ ): QSTileAvailabilityInteractor
+
+ @Binds
+ @IntoMap
+ @StringKey(NotesTile.TILE_SPEC)
+ fun bindNotesTile(notesTile: NotesTile): QSTileImpl<*>
+
companion object {
+ const val NOTES_TILE_SPEC = "notes"
+
@[Provides NoteTaskEnabledKey]
fun provideIsNoteTaskEnabled(
featureFlags: FeatureFlags,
@@ -65,5 +95,37 @@ interface NoteTaskModule {
val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
return isRoleAvailable && isFeatureEnabled
}
+
+ /** Inject NotesTile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(NOTES_TILE_SPEC)
+ fun provideNotesTileViewModel(
+ factory: QSTileViewModelFactory.Static<NotesTileModel>,
+ mapper: NotesTileMapper,
+ stateInteractor: NotesTileDataInteractor,
+ userActionInteractor: NotesTileUserActionInteractor,
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(NOTES_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+
+ @Provides
+ @IntoMap
+ @StringKey(NOTES_TILE_SPEC)
+ fun provideNotesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(NOTES_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.ic_qs_notes,
+ labelRes = R.string.quick_settings_notes_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index 63bfbd1dc1ba..195b0cebe2eb 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.notifications.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
@@ -38,7 +37,7 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
mapOf(
Swipe.Up to HideOverlay(Overlays.NotificationsShade),
Back to HideOverlay(Overlays.NotificationsShade),
- Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
index 7178d095d230..4fe63379aed4 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
@@ -16,19 +16,23 @@
package com.android.systemui.notifications.ui.viewmodel
+import androidx.compose.runtime.getValue
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Models UI state used to render the content of the notifications shade overlay.
@@ -43,10 +47,32 @@ constructor(
val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
val sceneInteractor: SceneInteractor,
private val shadeInteractor: ShadeInteractor,
+ activeNotificationsInteractor: ActiveNotificationsInteractor,
) : ExclusiveActivatable() {
+ private val hydrator = Hydrator("NotificationsShadeOverlayContentViewModel.hydrator")
+
+ val showHeader: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "showHeader",
+ initialValue =
+ shouldShowHeader(
+ isShadeLayoutWide = shadeInteractor.isShadeLayoutWide.value,
+ areAnyNotificationsPresent =
+ activeNotificationsInteractor.areAnyNotificationsPresentValue,
+ ),
+ source =
+ combine(
+ shadeInteractor.isShadeLayoutWide,
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ this::shouldShowHeader,
+ ),
+ )
+
override suspend fun onActivated(): Nothing {
coroutineScope {
+ launch { hydrator.activate() }
+
launch {
sceneInteractor.currentScene.collect { currentScene ->
when (currentScene) {
@@ -77,6 +103,13 @@ constructor(
shadeInteractor.collapseNotificationsShade(loggingReason = "shade scrim clicked")
}
+ private fun shouldShowHeader(
+ isShadeLayoutWide: Boolean,
+ areAnyNotificationsPresent: Boolean,
+ ): Boolean {
+ return !isShadeLayoutWide && areAnyNotificationsPresent
+ }
+
@AssistedFactory
interface Factory {
fun create(): NotificationsShadeOverlayContentViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
index 11854d9317c9..398ace4b67f4 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.notifications.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
@@ -40,7 +39,7 @@ class NotificationsShadeUserActionsViewModel @AssistedInject constructor() :
mapOf(
Back to SceneFamilies.Home,
Swipe.Up to SceneFamilies.Home,
- Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
+ Swipe.Down(fromSource = SceneContainerEdge.TopRight) to
ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index 8d48c1d1d23f..1cf4c23415da 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -26,11 +26,13 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.PowerRepository
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessModel
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -56,7 +58,7 @@ constructor(
* Unless you need to respond differently to different [WakeSleepReason]s, you should use
* [isAwake].
*/
- val detailedWakefulness = repository.wakefulness
+ val detailedWakefulness: StateFlow<WakefulnessModel> = repository.wakefulness
/**
* Whether we're awake (screen is on and responding to user touch) or asleep (screen is off, or
@@ -189,9 +191,7 @@ constructor(
* In tests, you should be able to use [setAsleepForTest] rather than calling this method
* directly.
*/
- fun onFinishedGoingToSleep(
- powerButtonLaunchGestureTriggeredDuringSleep: Boolean,
- ) {
+ fun onFinishedGoingToSleep(powerButtonLaunchGestureTriggeredDuringSleep: Boolean) {
// If the launch gesture was previously detected via onCameraLaunchGestureDetected, carry
// that state forward. It will be reset by the next onStartedGoingToSleep.
val powerButtonLaunchGestureTriggered =
@@ -255,7 +255,7 @@ constructor(
@JvmOverloads
fun PowerInteractor.setAwakeForTest(
@PowerManager.WakeReason reason: Int = PowerManager.WAKE_REASON_UNKNOWN,
- forceEmit: Boolean = false
+ forceEmit: Boolean = false,
) {
emitDuplicateWakefulnessValue = forceEmit
@@ -286,9 +286,7 @@ constructor(
emitDuplicateWakefulnessValue = forceEmit
this.onStartedGoingToSleep(reason = sleepReason)
- this.onFinishedGoingToSleep(
- powerButtonLaunchGestureTriggeredDuringSleep = false,
- )
+ this.onFinishedGoingToSleep(powerButtonLaunchGestureTriggeredDuringSleep = false)
}
/** Helper method for tests to simulate the device screen state change event. */
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
index 765b45bdbf2e..bab88c0b0bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
@@ -159,7 +159,7 @@ public class QRCodeScannerController implements
* Returns true if lock screen entry point for QR Code Scanner is to be enabled.
*/
public boolean isEnabledForLockScreenButton() {
- return mQRCodeScannerEnabled && isAbleToLaunchScannerActivity() && isAllowedOnLockScreen();
+ return isAbleToLaunchScannerActivity() && isAllowedOnLockScreen();
}
/** Returns whether the QR scanner button is allowed on lockscreen. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index a1071907cd3d..2a5ffc6cc391 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -27,6 +27,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.pm.UserInfo
+import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.os.IBinder
import android.os.PowerExemptionManager
@@ -54,6 +55,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.Dumpable
+import com.android.systemui.Flags;
import com.android.systemui.res.R
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
@@ -137,7 +139,7 @@ interface FgsManagerController {
@SysUISingleton
class FgsManagerControllerImpl @Inject constructor(
- private val context: Context,
+ @Main private val resources: Resources,
@Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor,
private val systemClock: SystemClock,
@@ -223,6 +225,14 @@ class FgsManagerControllerImpl @Inject constructor(
private val userVisibleJobObserver = UserVisibleJobObserver()
+ private val stoppableApps by lazy { resources
+ .getStringArray(com.android.internal.R.array.stoppable_fgs_system_apps)
+ }
+
+ private val vendorStoppableApps by lazy { resources
+ .getStringArray(com.android.internal.R.array.vendor_stoppable_fgs_system_apps)
+ }
+
override fun init() {
synchronized(lock) {
if (initialized) {
@@ -725,9 +735,22 @@ class FgsManagerControllerImpl @Inject constructor(
}
else -> UIControl.NORMAL
}
+ // If the app wants to be a good citizen by being stoppable, even if the category it
+ // belongs to is exempted for background restriction, let it be stoppable by user.
+ if (Flags.stoppableFgsSystemApp()) {
+ if (isStoppableApp(packageName)) {
+ uiControl = UIControl.NORMAL
+ }
+ }
+
uiControlInitialized = true
}
+ fun isStoppableApp(packageName: String): Boolean {
+ return stoppableApps.contains(packageName) ||
+ vendorStoppableApps.contains(packageName)
+ }
+
override fun equals(other: Any?): Boolean {
if (other !is UserPackage) {
return false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index bacff99fe048..ec6a17b24989 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -31,6 +31,7 @@ import android.widget.FrameLayout
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
@@ -41,14 +42,14 @@ import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
-import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -59,6 +60,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerEventPass
@@ -68,6 +70,7 @@ import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
@@ -75,7 +78,6 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastRoundToInt
import androidx.compose.ui.viewinterop.AndroidView
@@ -97,6 +99,7 @@ import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.PlatformTheme
import com.android.systemui.Dumpable
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -107,6 +110,7 @@ import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
import com.android.systemui.qs.composefragment.SceneKeys.QuickSettings
import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
+import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
import com.android.systemui.qs.composefragment.ui.notificationScrimClip
import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
@@ -115,12 +119,13 @@ import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.footer.ui.compose.FooterActions
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.QuickQuickSettings
+import com.android.systemui.qs.panels.ui.compose.TileGrid
import com.android.systemui.qs.shared.ui.ElementKeys
-import com.android.systemui.qs.ui.composable.QuickSettingsLayout
import com.android.systemui.qs.ui.composable.QuickSettingsShade
import com.android.systemui.qs.ui.composable.QuickSettingsTheme
import com.android.systemui.res.R
import com.android.systemui.util.LifecycleFragment
+import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
import com.android.systemui.util.println
@@ -195,6 +200,7 @@ constructor(
val context = inflater.context
val composeView =
ComposeView(context).apply {
+ id = R.id.quick_settings_container
repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
setViewTreeOnBackPressedDispatcherOwner(
@@ -239,7 +245,6 @@ constructor(
visible = viewModel.isQsVisible,
modifier =
Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
- .windowInsetsPadding(WindowInsets.navigationBars)
// Clipping before translation to match QSContainerImpl.onDraw
.offset {
IntOffset(x = 0, y = viewModel.viewTranslationY.fastRoundToInt())
@@ -299,7 +304,7 @@ constructor(
transitions =
transitions {
from(QuickQuickSettings, QuickSettings) {
- quickQuickSettingsToQuickSettings(viewModel::inFirstPage::get)
+ quickQuickSettingsToQuickSettings(viewModel::animateTilesExpansion::get)
}
},
)
@@ -444,8 +449,7 @@ constructor(
}
override fun setShouldUpdateSquishinessOnMedia(shouldUpdate: Boolean) {
- super.setShouldUpdateSquishinessOnMedia(shouldUpdate)
- // TODO (b/353253280)
+ viewModel.shouldUpdateSquishinessOnMedia = shouldUpdate
}
override fun setInSplitShade(isInSplitShade: Boolean) {
@@ -596,8 +600,21 @@ constructor(
}
.padding(top = { qqsPadding }, bottom = { bottomPadding })
) {
+ val Tiles =
+ @Composable {
+ QuickQuickSettings(
+ viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel
+ )
+ }
+ val Media =
+ @Composable {
+ if (viewModel.qqsMediaVisible) {
+ MediaObject(mediaHost = viewModel.qqsMediaHost)
+ }
+ }
+
if (viewModel.isQsEnabled) {
- Column(
+ Box(
modifier =
Modifier.collapseExpandSemanticAction(
stringResource(
@@ -608,16 +625,13 @@ constructor(
horizontal = {
QuickSettingsShade.Dimensions.Padding.roundToPx()
}
- ),
- verticalArrangement =
- spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+ )
) {
- QuickQuickSettings(
- viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel
+ QuickQuickSettingsLayout(
+ tiles = Tiles,
+ media = Media,
+ mediaInRow = viewModel.qqsMediaInRow,
)
- if (viewModel.qqsMediaVisible) {
- MediaObject(mediaHost = viewModel.qqsMediaHost)
- }
}
}
}
@@ -647,7 +661,20 @@ constructor(
Column(
modifier =
- Modifier.offset {
+ Modifier.onPlaced { coordinates ->
+ val positionOnScreen = coordinates.positionOnScreen()
+ val left = positionOnScreen.x
+ val right = left + coordinates.size.width
+ val top = positionOnScreen.y
+ val bottom = top + coordinates.size.height
+ viewModel.applyNewQsScrollerBounds(
+ left = left,
+ top = top,
+ right = right,
+ bottom = bottom,
+ )
+ }
+ .offset {
IntOffset(
x = 0,
y = viewModel.qsScrollTranslationY.fastRoundToInt(),
@@ -657,23 +684,61 @@ constructor(
.verticalScroll(scrollState)
.sysuiResTag(ResIdTags.qsScroll)
) {
+ val containerViewModel = viewModel.containerViewModel
Spacer(
modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
)
- QuickSettingsLayout(
- viewModel = viewModel.containerViewModel,
- modifier = Modifier.sysuiResTag(ResIdTags.quickSettingsPanel),
- )
- Spacer(modifier = Modifier.height(8.dp))
- if (viewModel.qsMediaVisible) {
- MediaObject(
- mediaHost = viewModel.qsMediaHost,
- modifier =
- Modifier.padding(
- horizontal = {
- QuickSettingsShade.Dimensions.Padding.roundToPx()
- }
- ),
+ val BrightnessSlider =
+ @Composable {
+ BrightnessSliderContainer(
+ viewModel = containerViewModel.brightnessSliderViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .height(
+ QuickSettingsShade.Dimensions.BrightnessSliderHeight
+ ),
+ )
+ }
+ val TileGrid =
+ @Composable {
+ Box {
+ GridAnchor()
+ TileGrid(
+ viewModel = containerViewModel.tileGridViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .heightIn(
+ max =
+ QuickSettingsShade.Dimensions.GridMaxHeight
+ ),
+ containerViewModel.editModeViewModel::startEditing,
+ )
+ }
+ }
+ val Media =
+ @Composable {
+ if (viewModel.qsMediaVisible) {
+ MediaObject(
+ mediaHost = viewModel.qsMediaHost,
+ update = { translationY = viewModel.qsMediaTranslationY },
+ )
+ }
+ }
+ Box(
+ modifier =
+ Modifier.fillMaxWidth()
+ .sysuiResTag(ResIdTags.quickSettingsPanel)
+ .padding(
+ top = QuickSettingsShade.Dimensions.Padding,
+ start = QuickSettingsShade.Dimensions.Padding,
+ end = QuickSettingsShade.Dimensions.Padding,
+ )
+ ) {
+ QuickSettingsLayout(
+ brightness = BrightnessSlider,
+ tiles = TileGrid,
+ media = Media,
+ mediaInRow = viewModel.qsMediaInRow,
)
}
}
@@ -939,7 +1004,11 @@ private fun Modifier.gesturesDisabled(disabled: Boolean) =
}
@Composable
-private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
+private fun MediaObject(
+ mediaHost: MediaHost,
+ modifier: Modifier = Modifier,
+ update: UniqueObjectHostView.() -> Unit = {},
+) {
Box {
AndroidView(
modifier = modifier,
@@ -952,11 +1021,69 @@ private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
)
}
},
+ update = { view -> view.update() },
onReset = {},
)
}
}
+@Composable
+@VisibleForTesting
+fun QuickQuickSettingsLayout(
+ tiles: @Composable () -> Unit,
+ media: @Composable () -> Unit,
+ mediaInRow: Boolean,
+) {
+ if (mediaInRow) {
+ Row(
+ horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(modifier = Modifier.weight(1f)) { tiles() }
+ Box(modifier = Modifier.weight(1f)) { media() }
+ }
+ } else {
+ Column(verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical))) {
+ tiles()
+ media()
+ }
+ }
+}
+
+@Composable
+@VisibleForTesting
+fun QuickSettingsLayout(
+ brightness: @Composable () -> Unit,
+ tiles: @Composable () -> Unit,
+ media: @Composable () -> Unit,
+ mediaInRow: Boolean,
+) {
+ if (mediaInRow) {
+ Column(
+ verticalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ brightness()
+ Row(
+ horizontalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(modifier = Modifier.weight(1f)) { tiles() }
+ Box(modifier = Modifier.weight(1f)) { media() }
+ }
+ }
+ } else {
+ Column(
+ verticalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ brightness()
+ tiles()
+ media()
+ }
+ }
+}
+
private object ResIdTags {
const val quickSettingsPanel = "quick_settings_panel"
const val quickQsPanel = "quick_qs_panel"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
index 512732090036..676f6a426264 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
@@ -19,6 +19,7 @@ package com.android.systemui.qs.composefragment.dagger
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.util.Utils
import dagger.Module
import dagger.Provides
@@ -34,7 +35,7 @@ interface QSFragmentComposeModule {
@SysUISingleton
@Named(QS_USING_MEDIA_PLAYER)
fun providesUsingMedia(@Application context: Context): Boolean {
- return Utils.useQsMediaPlayer(context)
+ return QSComposeFragment.isEnabled && Utils.useQsMediaPlayer(context)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
index 9e3945ecba57..c1a417411975 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
@@ -20,7 +20,9 @@ import com.android.compose.animation.scene.TransitionBuilder
import com.android.systemui.qs.composefragment.SceneKeys
import com.android.systemui.qs.shared.ui.ElementKeys
-fun TransitionBuilder.quickQuickSettingsToQuickSettings(inFirstPage: () -> Boolean = { true }) {
+fun TransitionBuilder.quickQuickSettingsToQuickSettings(
+ animateTilesExpansion: () -> Boolean = { true }
+) {
fractionRange(start = 0.5f) { fade(ElementKeys.QuickSettingsContent) }
@@ -28,7 +30,7 @@ fun TransitionBuilder.quickQuickSettingsToQuickSettings(inFirstPage: () -> Boole
anchoredTranslate(ElementKeys.QuickSettingsContent, ElementKeys.GridAnchor)
- sharedElement(ElementKeys.TileElementMatcher, enabled = inFirstPage())
+ sharedElement(ElementKeys.TileElementMatcher, enabled = animateTilesExpansion())
// This will animate between 0f (QQS) and 0.6, fading in the QQS tiles when coming back
// from non first page QS. The QS content ends fading out at 0.5f, so there's a brief
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 0ca621d7d2e2..9029563b6321 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.BouncerPanelExpansionCalculator
import com.android.systemui.Dumpable
@@ -39,6 +40,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.QS_PANEL
@@ -49,16 +52,19 @@ import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.InFirstPageViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.MediaInRowInLandscapeViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.asIndenting
+import com.android.systemui.util.kotlin.emitOnStart
import com.android.systemui.util.printSection
import com.android.systemui.util.println
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
@@ -90,10 +96,11 @@ constructor(
disableFlagsRepository: DisableFlagsRepository,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val squishinessInteractor: TileSquishinessInteractor,
private val inFirstPageViewModel: InFirstPageViewModel,
+ mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
@Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost,
@Named(QS_PANEL) val qsMediaHost: MediaHost,
@Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
@@ -101,6 +108,8 @@ constructor(
) : Dumpable, ExclusiveActivatable() {
val containerViewModel = containerViewModelFactory.create(true)
+ private val qqsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QQS)
+ private val qsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QS)
private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
@@ -195,7 +204,7 @@ constructor(
}
}
- val isQsFullyExpanded by derivedStateOf { expansionState.progress >= 1f }
+ val isQsFullyExpanded by derivedStateOf { expansionState.progress >= 1f && isQsExpanded }
/**
* Accessibility action for collapsing/expanding QS. The provided runnable is responsible for
@@ -203,9 +212,6 @@ constructor(
*/
var collapseExpandAccessibilityAction: Runnable? = null
- val inFirstPage: Boolean
- get() = inFirstPageViewModel.inFirstPage
-
var overScrollAmount by mutableStateOf(0)
val viewTranslationY by derivedStateOf {
@@ -252,6 +258,9 @@ constructor(
},
)
+ val qqsMediaInRow: Boolean
+ get() = qqsMediaInRowViewModel.shouldMediaShowInRow
+
val qsMediaVisible by
hydrator.hydratedStateOf(
traceName = "qsMediaVisible",
@@ -259,6 +268,65 @@ constructor(
source = if (usingMedia) mediaHostVisible(qsMediaHost) else flowOf(false),
)
+ val qsMediaInRow: Boolean
+ get() = qsMediaInRowViewModel.shouldMediaShowInRow
+
+ var shouldUpdateSquishinessOnMedia by mutableStateOf(false)
+
+ val qsMediaTranslationY by derivedStateOf {
+ if (
+ qsExpansion > 0f &&
+ !isKeyguardState &&
+ !qqsMediaVisible &&
+ !qsMediaInRow &&
+ !isInSplitShade
+ ) {
+ val interpolation = Interpolators.ACCELERATE.getInterpolation(1f - qsExpansion)
+ -qsMediaHost.hostView.height * 1.3f * interpolation
+ } else {
+ 0f
+ }
+ }
+
+ val animateTilesExpansion: Boolean
+ get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
+
+ private val inFirstPage: Boolean
+ get() = inFirstPageViewModel.inFirstPage
+
+ private val mediaSuddenlyAppearingInLandscape: Boolean
+ get() = !qqsMediaInRow && qsMediaInRow
+
+ private val collapsedLandscapeMedia by
+ hydrator.hydratedStateOf(
+ traceName = "collapsedLandscapeMedia",
+ initialValue = resources.getBoolean(R.bool.config_quickSettingsMediaLandscapeCollapsed),
+ source =
+ configurationInteractor.onAnyConfigurationChange.emitOnStart().map {
+ resources.getBoolean(R.bool.config_quickSettingsMediaLandscapeCollapsed)
+ },
+ )
+
+ private val qqsMediaExpansion: Float
+ get() =
+ if (qqsMediaInRow && collapsedLandscapeMedia) {
+ MediaHostState.COLLAPSED
+ } else {
+ MediaHostState.EXPANDED
+ }
+
+ private val shouldApplySquishinessToMedia by derivedStateOf {
+ shouldUpdateSquishinessOnMedia || (isInSplitShade && statusBarState == StatusBarState.SHADE)
+ }
+
+ private val mediaSquishiness by derivedStateOf {
+ if (shouldApplySquishinessToMedia) {
+ squishinessFraction
+ } else {
+ 1f
+ }
+ }
+
private var qsBounds by mutableStateOf(Rect())
private val constrainedSquishinessFraction: Float
@@ -317,8 +385,6 @@ constructor(
private val isOverscrolling: Boolean
get() = overScrollAmount != 0
- private var shouldUpdateMediaSquishiness by mutableStateOf(false)
-
private val forceQs by derivedStateOf {
(isQsExpanded || isStackScrollerOverscrolling) &&
(isKeyguardState && !showCollapsedOnKeyguard)
@@ -356,19 +422,37 @@ constructor(
),
)
+ fun applyNewQsScrollerBounds(left: Float, top: Float, right: Float, bottom: Float) {
+ if (usingMedia) {
+ qsMediaHost.currentClipping.set(
+ left.toInt(),
+ top.toInt(),
+ right.toInt(),
+ bottom.toInt(),
+ )
+ }
+ }
+
override suspend fun onActivated(): Nothing {
initMediaHosts() // init regardless of using media (same as current QS).
coroutineScope {
launch { hydrateSquishinessInteractor() }
+ if (usingMedia) {
+ launch { hydrateQqsMediaExpansion() }
+ launch { hydrateMediaSquishiness() }
+ launch { hydrateMediaDisappearParameters() }
+ }
launch { hydrator.activate() }
launch { containerViewModel.activate() }
+ launch { qqsMediaInRowViewModel.activate() }
+ launch { qsMediaInRowViewModel.activate() }
awaitCancellation()
}
}
private fun initMediaHosts() {
qqsMediaHost.apply {
- expansion = MediaHostState.EXPANDED
+ expansion = qqsMediaExpansion
showsOnlyActiveMedia = true
init(MediaHierarchyManager.LOCATION_QQS)
}
@@ -384,6 +468,25 @@ constructor(
.collect { squishinessInteractor.setSquishinessValue(it) }
}
+ private suspend fun hydrateQqsMediaExpansion() {
+ snapshotFlow { qqsMediaExpansion }.collect { qqsMediaHost.expansion = it }
+ }
+
+ private suspend fun hydrateMediaSquishiness() {
+ snapshotFlow { mediaSquishiness }.collect { qsMediaHost.squishFraction = it }
+ }
+
+ private suspend fun hydrateMediaDisappearParameters() {
+ coroutineScope {
+ launch {
+ snapshotFlow { qqsMediaInRow }.collect { qqsMediaHost.applyDisappearParameters(it) }
+ }
+ launch {
+ snapshotFlow { qsMediaInRow }.collect { qsMediaHost.applyDisappearParameters(it) }
+ }
+ }
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.asIndenting().run {
printSection("Quick Settings state") {
@@ -391,6 +494,7 @@ constructor(
println("isQSVisible", isQsVisible)
println("isQSEnabled", isQsEnabled)
println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value)
+ println("inFirstPage", inFirstPage)
}
printSection("Expansion state") {
println("qsExpansion", qsExpansion)
@@ -423,7 +527,14 @@ constructor(
}
printSection("Media") {
println("qqsMediaVisible", qqsMediaVisible)
+ println("qqsMediaInRow", qqsMediaInRow)
println("qsMediaVisible", qsMediaVisible)
+ println("qsMediaInRow", qsMediaInRow)
+ println("collapsedLandscapeMedia", collapsedLandscapeMedia)
+ println("qqsMediaExpansion", qqsMediaExpansion)
+ println("shouldUpdateSquishinessOnMedia", shouldUpdateSquishinessOnMedia)
+ println("mediaSquishiness", mediaSquishiness)
+ println("qsMediaTranslationY", qsMediaTranslationY)
}
}
}
@@ -460,3 +571,22 @@ private fun mediaHostVisible(mediaHost: MediaHost): Flow<Boolean> {
// lazily.
.onStart { emit(mediaHost.visible) }
}
+
+// Taken from QSPanelControllerBase
+private fun MediaHost.applyDisappearParameters(inRow: Boolean) {
+ disappearParameters.apply {
+ fadeStartPosition = 0.95f
+ disappearStart = 0f
+ if (inRow) {
+ disappearSize.set(0f, 0.4f)
+ gonePivot.set(1f, 0f)
+ contentTranslationFraction.set(0.25f, 1f)
+ disappearEnd = 0.6f
+ } else {
+ disappearSize.set(1f, 0f)
+ gonePivot.set(0f, 0f)
+ contentTranslationFraction.set(0f, 1f)
+ disappearEnd = 0.95f
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 43fd0f5feec7..1f55ac777de5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -35,8 +35,6 @@ import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout
import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModelImpl
-import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsSizeViewModelImpl
-import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -58,8 +56,6 @@ interface PanelsModule {
@Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
- @Binds fun bindQSColumnsViewModel(impl: QSColumnsSizeViewModelImpl): QSColumnsViewModel
-
@Binds
@PaginatedBaseLayoutType
fun bindPaginatedBaseGridLayout(impl: InfiniteGridLayout): PaginatableGridLayout
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt
new file mode 100644
index 000000000000..58834037e2b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.emitOnStart
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class LargeTileSpanRepository
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ @Main private val resources: Resources,
+ configurationRepository: ConfigurationRepository,
+) {
+ val span: StateFlow<Int> =
+ configurationRepository.onConfigurationChange
+ .emitOnStart()
+ .mapLatest {
+ if (resources.configuration.fontScale >= FONT_SCALE_THRESHOLD) {
+ resources.getInteger(R.integer.quick_settings_infinite_grid_tile_max_width)
+ } else {
+ 2
+ }
+ }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 2)
+
+ private companion object {
+ const val FONT_SCALE_THRESHOLD = 2f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
index 971598dea0bd..b0c607303297 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
@@ -17,9 +17,16 @@
package com.android.systemui.qs.panels.data.repository
import android.content.Context
+import android.content.IntentFilter
import android.content.SharedPreferences
+import com.android.systemui.backup.BackupHelper
+import com.android.systemui.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.settings.UserFileManager
import com.android.systemui.user.data.repository.UserRepository
@@ -29,9 +36,11 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
/** Repository for QS user preferences. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -43,26 +52,31 @@ constructor(
private val userRepository: UserRepository,
private val defaultLargeTilesRepository: DefaultLargeTilesRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ @PanelsLog private val logBuffer: LogBuffer,
+ broadcastDispatcher: BroadcastDispatcher,
) {
- /** Whether to show the labels on icon tiles for the current user. */
- val showLabels: Flow<Boolean> =
- userRepository.selectedUserInfo
- .flatMapLatest { userInfo ->
- val prefs = getSharedPrefs(userInfo.id)
- prefs.observe().emitOnStart().map { prefs.getBoolean(ICON_LABELS_KEY, false) }
- }
- .flowOn(backgroundDispatcher)
+ private val logger by lazy { Logger(logBuffer, TAG) }
+
+ private val backupRestorationEvents: Flow<Unit> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter = IntentFilter(ACTION_RESTORE_FINISHED),
+ flags = Context.RECEIVER_NOT_EXPORTED,
+ permission = BackupHelper.PERMISSION_SELF,
+ )
+ .onEach { logger.i("Restored state for QS preferences.") }
+ .emitOnStart()
/** Set of [TileSpec] to display as large tiles for the current user. */
val largeTilesSpecs: Flow<Set<TileSpec>> =
- userRepository.selectedUserInfo
- .flatMapLatest { userInfo ->
+ combine(backupRestorationEvents, userRepository.selectedUserInfo, ::Pair)
+ .flatMapLatest { (_, userInfo) ->
val prefs = getSharedPrefs(userInfo.id)
prefs.observe().emitOnStart().map {
prefs
.getStringSet(
LARGE_TILES_SPECS_KEY,
- defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet()
+ defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet(),
)
?.map { TileSpec.create(it) }
?.toSet() ?: defaultLargeTilesRepository.defaultLargeTiles
@@ -70,13 +84,6 @@ constructor(
}
.flowOn(backgroundDispatcher)
- /** Sets for the current user whether to show the labels on icon tiles. */
- fun setShowLabels(showLabels: Boolean) {
- with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
- edit().putBoolean(ICON_LABELS_KEY, showLabels).apply()
- }
- }
-
/** Sets for the current user the set of [TileSpec] to display as large tiles. */
fun setLargeTilesSpecs(specs: Set<TileSpec>) {
with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
@@ -85,15 +92,11 @@ constructor(
}
private fun getSharedPrefs(userId: Int): SharedPreferences {
- return userFileManager.getSharedPreferences(
- FILE_NAME,
- Context.MODE_PRIVATE,
- userId,
- )
+ return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, userId)
}
companion object {
- private const val ICON_LABELS_KEY = "show_icon_labels"
+ private const val TAG = "QSPreferencesRepository"
private const val LARGE_TILES_SPECS_KEY = "large_tiles_specs"
const val FILE_NAME = "quick_settings_prefs"
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt
new file mode 100644
index 000000000000..2c51ca9c253f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.backup
+
+import android.app.backup.SharedPreferencesBackupHelper
+import android.content.Context
+import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository.Companion.FILE_NAME
+import com.android.systemui.settings.UserFileManagerImpl
+
+class QSPreferencesBackupHelper(context: Context, userId: Int) :
+ SharedPreferencesBackupHelper(
+ context,
+ UserFileManagerImpl.createFile(userId = userId, fileName = FILE_NAME).path,
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index ec61a0d5769e..23c79f576df5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -21,12 +21,14 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
+import com.android.systemui.qs.panels.data.repository.LargeTileSpanRepository
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -38,6 +40,7 @@ constructor(
private val repo: DefaultLargeTilesRepository,
private val currentTilesInteractor: CurrentTilesInteractor,
private val preferencesInteractor: QSPreferencesInteractor,
+ largeTilesSpanRepo: LargeTileSpanRepository,
@PanelsLog private val logBuffer: LogBuffer,
@Application private val applicationScope: CoroutineScope,
) {
@@ -46,6 +49,8 @@ constructor(
.onEach { logChange(it) }
.stateIn(applicationScope, SharingStarted.Eagerly, repo.defaultLargeTiles)
+ val largeTilesSpan: StateFlow<Int> = largeTilesSpanRepo.span
+
fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)
fun setLargeTiles(specs: Set<TileSpec>) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 74fa0fef21d7..c729c7c15176 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -37,13 +37,17 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
fun rememberEditListState(
tiles: List<SizedTile<EditTileViewModel>>,
columns: Int,
+ largeTilesSpan: Int,
): EditTileListState {
- return remember(tiles, columns) { EditTileListState(tiles, columns) }
+ return remember(tiles, columns) { EditTileListState(tiles, columns, largeTilesSpan) }
}
/** Holds the temporary state of the tile list during a drag movement where we move tiles around. */
-class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val columns: Int) :
- DragAndDropState {
+class EditTileListState(
+ tiles: List<SizedTile<EditTileViewModel>>,
+ private val columns: Int,
+ private val largeTilesSpan: Int,
+) : DragAndDropState {
private val _draggedCell = mutableStateOf<SizedTile<EditTileViewModel>?>(null)
override val draggedCell
get() = _draggedCell.value
@@ -86,7 +90,7 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c
if (fromIndex != -1) {
val cell = _tiles.removeAt(fromIndex)
cell as TileGridCell
- _tiles.add(fromIndex, cell.copy(width = if (cell.isIcon) 2 else 1))
+ _tiles.add(fromIndex, cell.copy(width = if (cell.isIcon) largeTilesSpan else 1))
regenerateGrid(fromIndex)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 6cc2cbc63d09..2efe500912cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -73,7 +73,7 @@ constructor(
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val columns by viewModel.columns
+ val columns = viewModel.columns
val rows = viewModel.rows
val pages =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index d10722287f5d..177a5be35592 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.compose.infinitegrid
import android.graphics.drawable.Animatable
+import android.graphics.drawable.Drawable
import android.text.TextUtils
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
@@ -30,10 +31,11 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -63,11 +65,14 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.size
import com.android.compose.modifiers.thenIf
+import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconHeight
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconWidth
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
import com.android.systemui.res.R
@@ -79,6 +84,7 @@ fun LargeTileContent(
label: String,
secondaryLabel: String?,
icon: Icon,
+ sideDrawable: Drawable?,
colors: TileColors,
squishiness: () -> Float,
accessibilityUiState: AccessibilityUiState? = null,
@@ -135,6 +141,14 @@ fun LargeTileContent(
colors = colors,
accessibilityUiState = accessibilityUiState,
)
+
+ if (sideDrawable != null) {
+ Image(
+ painter = rememberDrawablePainter(sideDrawable),
+ contentDescription = null,
+ modifier = Modifier.width(SideIconWidth).height(SideIconHeight),
+ )
+ }
}
}
@@ -194,32 +208,43 @@ fun SmallTileContent(
is Icon.Resource -> context.getDrawable(icon.res)
}
}
- if (loadedDrawable !is Animatable) {
- Icon(icon = icon, tint = animatedColor, modifier = iconModifier)
- } else if (icon is Icon.Resource) {
- val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
+ if (loadedDrawable is Animatable) {
val painter =
- key(icon) {
- if (animateToEnd) {
- rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
- } else {
- var atEnd by remember(icon.res) { mutableStateOf(false) }
- LaunchedEffect(key1 = icon.res) { atEnd = true }
- rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
+ when (icon) {
+ is Icon.Resource -> {
+ val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
+ key(icon) {
+ if (animateToEnd) {
+ rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
+ } else {
+ var atEnd by remember(icon.res) { mutableStateOf(false) }
+ LaunchedEffect(key1 = icon.res) { atEnd = true }
+ rememberAnimatedVectorPainter(
+ animatedImageVector = image,
+ atEnd = atEnd,
+ )
+ }
+ }
}
+ is Icon.Loaded -> rememberDrawablePainter(loadedDrawable)
}
+
Image(
painter = painter,
contentDescription = icon.contentDescription?.load(),
colorFilter = ColorFilter.tint(color = animatedColor),
modifier = iconModifier,
)
+ } else {
+ Icon(icon = icon, tint = animatedColor, modifier = iconModifier)
}
}
object CommonTileDefaults {
val IconSize = 32.dp
val LargeTileIconSize = 28.dp
+ val SideIconWidth = 32.dp
+ val SideIconHeight = 20.dp
val ToggleTargetSize = 56.dp
val TileHeight = 72.dp
val TilePadding = 8.dp
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index b5cec120987f..31ea60e2f0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -26,7 +26,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.LocalOverscrollConfiguration
+import androidx.compose.foundation.LocalOverscrollFactory
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.Orientation
@@ -173,6 +173,7 @@ fun DefaultEditTileGrid(
listState: EditTileListState,
otherTiles: List<SizedTile<EditTileViewModel>>,
columns: Int,
+ largeTilesSpan: Int,
modifier: Modifier,
onRemoveTile: (TileSpec) -> Unit,
onSetTiles: (List<TileSpec>) -> Unit,
@@ -203,7 +204,7 @@ fun DefaultEditTileGrid(
containerColor = Color.Transparent,
topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
) { innerPadding ->
- CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
+ CompositionLocalProvider(LocalOverscrollFactory provides null) {
val scrollState = rememberScrollState()
LaunchedEffect(listState.dragInProgress) {
if (listState.dragInProgress) {
@@ -230,7 +231,14 @@ fun DefaultEditTileGrid(
}
}
- CurrentTilesGrid(listState, selectionState, columns, onResize, onSetTiles)
+ CurrentTilesGrid(
+ listState,
+ selectionState,
+ columns,
+ largeTilesSpan,
+ onResize,
+ onSetTiles,
+ )
// Hide available tiles when dragging
AnimatedVisibility(
@@ -273,7 +281,7 @@ private fun EditGridHeader(
) {
Box(
contentAlignment = Alignment.Center,
- modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight),
+ modifier = modifier.fillMaxWidth().wrapContentHeight(),
) {
content()
}
@@ -300,6 +308,7 @@ private fun CurrentTilesGrid(
listState: EditTileListState,
selectionState: MutableSelectionState,
columns: Int,
+ largeTilesSpan: Int,
onResize: (TileSpec, toIcon: Boolean) -> Unit,
onSetTiles: (List<TileSpec>) -> Unit,
) {
@@ -340,7 +349,8 @@ private fun CurrentTilesGrid(
}
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
- EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec ->
+ EditTiles(cells, columns, listState, selectionState, coroutineScope, largeTilesSpan) { spec
+ ->
// Toggle the current size of the tile
currentListState.isIcon(spec)?.let { onResize(spec, !it) }
}
@@ -425,6 +435,7 @@ fun LazyGridScope.EditTiles(
dragAndDropState: DragAndDropState,
selectionState: MutableSelectionState,
coroutineScope: CoroutineScope,
+ largeTilesSpan: Int,
onToggleSize: (spec: TileSpec) -> Unit,
) {
items(
@@ -456,6 +467,7 @@ fun LazyGridScope.EditTiles(
onToggleSize = onToggleSize,
coroutineScope = coroutineScope,
bounceableInfo = cells.bounceableInfo(index, columns),
+ largeTilesSpan = largeTilesSpan,
modifier = Modifier.animateItem(),
)
}
@@ -472,6 +484,7 @@ private fun TileGridCell(
selectionState: MutableSelectionState,
onToggleSize: (spec: TileSpec) -> Unit,
coroutineScope: CoroutineScope,
+ largeTilesSpan: Int,
bounceableInfo: BounceableInfo,
modifier: Modifier = Modifier,
) {
@@ -514,8 +527,11 @@ private fun TileGridCell(
.fillMaxWidth()
.onSizeChanged {
// Grab the size before the bounceable to get the idle width
- val min = if (cell.isIcon) it.width else (it.width - padding) / 2
- val max = if (cell.isIcon) (it.width * 2) + padding else it.width
+ val totalPadding = (largeTilesSpan - 1) * padding
+ val min =
+ if (cell.isIcon) it.width else (it.width - totalPadding) / largeTilesSpan
+ val max =
+ if (cell.isIcon) (it.width * largeTilesSpan) + totalPadding else it.width
tileWidths = TileWidths(it.width, min, max)
}
.bounceable(
@@ -554,15 +570,13 @@ private fun TileGridCell(
val targetValue = if (cell.isIcon) 0f else 1f
val animatedProgress = remember { Animatable(targetValue) }
- if (selected) {
- val resizingState = selectionState.resizingState
- LaunchedEffect(targetValue, resizingState) {
- if (resizingState == null) {
- animatedProgress.animateTo(targetValue)
- } else {
- snapshotFlow { resizingState.progression }
- .collectLatest { animatedProgress.snapTo(it) }
- }
+ val resizingState = selectionState.resizingState?.takeIf { selected }
+ LaunchedEffect(targetValue, resizingState) {
+ if (resizingState == null) {
+ animatedProgress.animateTo(targetValue)
+ } else {
+ snapshotFlow { resizingState.progression }
+ .collectLatest { animatedProgress.snapTo(it) }
}
}
@@ -705,7 +719,6 @@ private fun Modifier.tileBackground(color: Color): Modifier {
private object EditModeTileDefaults {
const val PLACEHOLDER_ALPHA = .3f
- val EditGridHeaderHeight = 60.dp
val CurrentTilesGridPadding = 8.dp
@Composable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 19ab29e6c796..29ff1715dea2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -30,6 +30,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
import com.android.systemui.qs.panels.ui.compose.bounceableInfo
@@ -72,8 +73,14 @@ constructor(
rememberViewModel(traceName = "InfiniteGridLayout.TileGrid") {
viewModel.dynamicIconTilesViewModelFactory.create()
}
- val columns by viewModel.gridSizeViewModel.columns
- val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
+ val columnsWithMediaViewModel =
+ rememberViewModel(traceName = "InfiniteGridLAyout.TileGrid") {
+ viewModel.columnsWithMediaViewModelFactory.create(LOCATION_QS)
+ }
+
+ val columns = columnsWithMediaViewModel.columns
+ val largeTilesSpan by iconTilesViewModel.largeTilesSpanState
+ val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width(largeTilesSpan)) }
val bounceables =
remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
@@ -118,22 +125,28 @@ constructor(
rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
viewModel.dynamicIconTilesViewModelFactory.create()
}
- val columns by viewModel.gridSizeViewModel.columns
+ val columnsViewModel =
+ rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
+ viewModel.columnsWithMediaViewModelFactory.createWithoutMediaTracking()
+ }
+ val columns = columnsViewModel.columns
+ val largeTilesSpan by iconTilesViewModel.largeTilesSpanState
val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
// Non-current tiles should always be displayed as icon tiles.
val sizedTiles =
- remember(tiles, largeTiles) {
+ remember(tiles, largeTiles, largeTilesSpan) {
tiles.map {
SizedTileImpl(
it,
- if (!it.isCurrent || !largeTiles.contains(it.tileSpec)) 1 else 2,
+ if (!it.isCurrent || !largeTiles.contains(it.tileSpec)) 1
+ else largeTilesSpan,
)
}
}
val (currentTiles, otherTiles) = sizedTiles.partition { it.tile.isCurrent }
- val currentListState = rememberEditListState(currentTiles, columns)
+ val currentListState = rememberEditListState(currentTiles, columns, largeTilesSpan)
DefaultEditTileGrid(
listState = currentListState,
otherTiles = otherTiles,
@@ -144,6 +157,7 @@ constructor(
onResize = iconTilesViewModel::resize,
onStopEditing = onStopEditing,
onReset = viewModel::showResetDialog,
+ largeTilesSpan = largeTilesSpan,
)
}
@@ -161,7 +175,7 @@ constructor(
.map { it.flatten().map { it.tile } }
}
- private fun TileSpec.width(): Int {
- return if (iconTilesViewModel.isIconTile(this)) 1 else 2
+ private fun TileSpec.width(largeSize: Int = iconTilesViewModel.largeTilesSpan.value): Int {
+ return if (iconTilesViewModel.isIconTile(this)) 1 else largeSize
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 5bebdbc7a13e..fe59c4d36edc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -130,15 +130,7 @@ fun Tile(
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape = TileDefaults.animateTileShape(uiState.state)
- val animatedColor by
- animateColorAsState(
- if (iconOnly || !uiState.handlesSecondaryClick) {
- colors.iconBackground
- } else {
- colors.background
- },
- label = "QSTileBackgroundColor",
- )
+ val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
TileExpandable(
color = { animatedColor },
@@ -156,6 +148,14 @@ fun Tile(
bounceEnd = currentBounceableInfo.bounceEnd,
),
) { expandable ->
+ val longClick: (() -> Unit)? =
+ {
+ hapticsViewModel?.setTileInteractionState(
+ TileHapticsViewModel.TileInteractionState.LONG_CLICKED
+ )
+ tile.onLongClick(expandable)
+ }
+ .takeIf { uiState.handlesLongClick }
TileContainer(
onClick = {
tile.onClick(expandable)
@@ -166,12 +166,7 @@ fun Tile(
coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
}
},
- onLongClick = {
- hapticsViewModel?.setTileInteractionState(
- TileHapticsViewModel.TileInteractionState.LONG_CLICKED
- )
- tile.onLongClick(expandable)
- },
+ onLongClick = longClick,
uiState = uiState,
iconOnly = iconOnly,
) {
@@ -192,18 +187,11 @@ fun Tile(
tile.onSecondaryClick()
}
.takeIf { uiState.handlesSecondaryClick }
- val longClick: (() -> Unit)? =
- {
- hapticsViewModel?.setTileInteractionState(
- TileHapticsViewModel.TileInteractionState.LONG_CLICKED
- )
- tile.onLongClick(expandable)
- }
- .takeIf { uiState.handlesLongClick }
LargeTileContent(
label = uiState.label,
secondaryLabel = uiState.secondaryLabel,
icon = icon,
+ sideDrawable = uiState.sideDrawable,
colors = colors,
iconShape = iconShape,
toggleClick = secondaryClick,
@@ -237,7 +225,7 @@ private fun TileExpandable(
@Composable
fun TileContainer(
onClick: () -> Unit,
- onLongClick: () -> Unit,
+ onLongClick: (() -> Unit)?,
uiState: TileUiState,
iconOnly: Boolean,
content: @Composable BoxScope.() -> Unit,
@@ -281,7 +269,7 @@ fun Modifier.tilePadding(): Modifier {
@Composable
fun Modifier.tileCombinedClickable(
onClick: () -> Unit,
- onLongClick: () -> Unit,
+ onLongClick: (() -> Unit)?,
uiState: TileUiState,
iconOnly: Boolean,
): Modifier {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
index 9552aa935bbf..41c3de55af70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
@@ -22,7 +22,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue
import com.android.systemui.qs.panels.ui.compose.selection.ResizingDefaults.RESIZING_THRESHOLD
-class ResizingState(private val widths: TileWidths, private val onResize: () -> Unit) {
+class ResizingState(val widths: TileWidths, private val onResize: () -> Unit) {
/** Total drag offset of this resize operation. */
private var totalOffset by mutableFloatStateOf(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
index 9feaab83cc1f..a9d673aa7400 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
@@ -17,9 +17,13 @@
package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.qs.panels.domain.interactor.DynamicIconTilesInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
/** View model to resize QS tiles down to icons when removed from the current tiles. */
class DynamicIconTilesViewModel
@@ -28,10 +32,21 @@ constructor(
interactorFactory: DynamicIconTilesInteractor.Factory,
iconTilesViewModel: IconTilesViewModel,
) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
+ private val hydrator = Hydrator("DynamicIconTilesViewModel")
private val interactor = interactorFactory.create()
+ val largeTilesSpanState =
+ hydrator.hydratedStateOf(
+ traceName = "largeTilesSpan",
+ source = iconTilesViewModel.largeTilesSpan,
+ )
+
override suspend fun onActivated(): Nothing {
- interactor.activate()
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch { interactor.activate() }
+ awaitCancellation()
+ }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 7fe856b871bd..4e34e73654fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -30,6 +30,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import javax.inject.Named
@@ -54,7 +55,7 @@ constructor(
private val currentTilesInteractor: CurrentTilesInteractor,
private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
private val minTilesInteractor: MinimumTilesInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
@Application private val applicationContext: Context,
@Named("Default") private val defaultGridLayout: GridLayout,
@Application private val applicationScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
index 4e698edf4e34..b8c5fbb72614 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
@@ -25,6 +25,8 @@ import kotlinx.coroutines.flow.StateFlow
interface IconTilesViewModel {
val largeTiles: StateFlow<Set<TileSpec>>
+ val largeTilesSpan: StateFlow<Int>
+
fun isIconTile(spec: TileSpec): Boolean
fun resize(spec: TileSpec, toIcon: Boolean)
@@ -34,6 +36,7 @@ interface IconTilesViewModel {
class IconTilesViewModelImpl @Inject constructor(private val interactor: IconTilesInteractor) :
IconTilesViewModel {
override val largeTiles = interactor.largeTilesSpecs
+ override val largeTilesSpan = interactor.largeTilesSpan
override fun isIconTile(spec: TileSpec): Boolean = interactor.isIconTile(spec)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
index d68710048e13..3327141d2bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.dialog.QSResetDialogDelegate
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -25,19 +24,15 @@ class InfiniteGridViewModel
@AssistedInject
constructor(
val dynamicIconTilesViewModelFactory: DynamicIconTilesViewModel.Factory,
- val gridSizeViewModel: QSColumnsViewModel,
+ val columnsWithMediaViewModelFactory: QSColumnsViewModel.Factory,
val squishinessViewModel: TileSquishinessViewModel,
private val resetDialogDelegate: QSResetDialogDelegate,
-) : ExclusiveActivatable() {
+) {
fun showResetDialog() {
resetDialogDelegate.showDialog()
}
- override suspend fun onActivated(): Nothing {
- gridSizeViewModel.activate()
- }
-
@AssistedFactory
interface Factory {
fun create(): InfiniteGridViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt
new file mode 100644
index 000000000000..2ed8fd20df8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModel.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import androidx.compose.runtime.getValue
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import javax.inject.Named
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Indicates whether a particular UMO in [LOCATION_QQS] or [LOCATION_QS] should currently show in a
+ * row with the tiles, based on its visibility and device configuration. If the player is not
+ * visible, it will never indicate that media should show in row.
+ */
+class MediaInRowInLandscapeViewModel
+@AssistedInject
+constructor(
+ @Main resources: Resources,
+ configurationInteractor: ConfigurationInteractor,
+ shadeModeInteractor: ShadeModeInteractor,
+ private val mediaHostStatesManager: MediaHostStatesManager,
+ @Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
+ @Assisted @MediaLocation private val inLocation: Int,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("MediaInRowInLanscapeViewModel - $inLocation")
+
+ val shouldMediaShowInRow: Boolean
+ get() = usingMedia && inSingleShade && isLandscapeAndLong && isMediaVisible
+
+ private val inSingleShade: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "inSingleShade",
+ initialValue = shadeModeInteractor.shadeMode.value == ShadeMode.Single,
+ source = shadeModeInteractor.shadeMode.map { it == ShadeMode.Single },
+ )
+
+ private val isLandscapeAndLong: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isLandscapeAndLong",
+ initialValue = resources.configuration.isLandscapeAndLong,
+ source = configurationInteractor.configurationValues.map { it.isLandscapeAndLong },
+ )
+
+ private val isMediaVisible by
+ hydrator.hydratedStateOf(
+ traceName = "isMediaVisible",
+ initialValue = false,
+ source =
+ conflatedCallbackFlow {
+ val callback =
+ object : MediaHostStatesManager.Callback {
+ override fun onHostStateChanged(
+ location: Int,
+ mediaHostState: MediaHostState,
+ ) {
+ if (location == inLocation) {
+ trySend(mediaHostState.visible)
+ }
+ }
+ }
+ mediaHostStatesManager.addCallback(callback)
+
+ awaitClose { mediaHostStatesManager.removeCallback(callback) }
+ }
+ .onStart {
+ emit(
+ mediaHostStatesManager.mediaHostStates.get(inLocation)?.visible ?: false
+ )
+ },
+ )
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(@MediaLocation inLocation: Int): MediaInRowInLandscapeViewModel
+ }
+}
+
+private val Configuration.isLandscapeAndLong: Boolean
+ get() =
+ orientation == Configuration.ORIENTATION_LANDSCAPE &&
+ (screenLayout and Configuration.SCREENLAYOUT_LONG_MASK) ==
+ Configuration.SCREENLAYOUT_LONG_YES
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index 8bd9ed05c12c..e5607eb6e620 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -16,10 +16,10 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -31,12 +31,13 @@ class PaginatedGridViewModel
@AssistedInject
constructor(
iconTilesViewModel: IconTilesViewModel,
- private val gridSizeViewModel: QSColumnsViewModel,
+ columnsWithMediaViewModelFactory: QSColumnsViewModel.Factory,
paginatedGridInteractor: PaginatedGridInteractor,
inFirstPageViewModel: InFirstPageViewModel,
) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
private val hydrator = Hydrator("PaginatedGridViewModel")
+ private val columnsWithMediaViewModel = columnsWithMediaViewModelFactory.create(LOCATION_QS)
val rows by
hydrator.hydratedStateOf(
@@ -47,13 +48,13 @@ constructor(
var inFirstPage by inFirstPageViewModel::inFirstPage
- val columns: State<Int>
- get() = gridSizeViewModel.columns
+ val columns: Int
+ get() = columnsWithMediaViewModel.columns
override suspend fun onActivated(): Nothing {
coroutineScope {
launch { hydrator.activate() }
- launch { gridSizeViewModel.activate() }
+ launch { columnsWithMediaViewModel.activate() }
awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
index 8926d2ff107e..85b7831fb270 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
@@ -16,25 +16,61 @@
package com.android.systemui.qs.panels.ui.viewmodel
-import androidx.compose.runtime.State
-import com.android.systemui.lifecycle.Activatable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.qs.panels.domain.interactor.QSColumnsInteractor
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
-interface QSColumnsViewModel : Activatable {
- val columns: State<Int>
-}
+/**
+ * View model for the number of columns that should be shown in a QS grid.
+ * * Create it with a [MediaLocation] to halve the number of columns when media should show in a row
+ * with the tiles.
+ * * Create it with a `null` [MediaLocation] to ignore media visibility (useful for edit mode).
+ */
+class QSColumnsViewModel
+@AssistedInject
+constructor(
+ interactor: QSColumnsInteractor,
+ mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
+ @Assisted @MediaLocation mediaLocation: Int?,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("QSColumnsViewModelWithMedia")
+
+ val columns by derivedStateOf {
+ if (mediaInRowInLandscapeViewModel?.shouldMediaShowInRow == true) {
+ columnsWithoutMedia / 2
+ } else {
+ columnsWithoutMedia
+ }
+ }
-class QSColumnsSizeViewModelImpl @Inject constructor(interactor: QSColumnsInteractor) :
- QSColumnsViewModel, ExclusiveActivatable() {
- private val hydrator = Hydrator("QSColumnsSizeViewModelImpl")
+ private val mediaInRowInLandscapeViewModel =
+ mediaLocation?.let { mediaInRowInLandscapeViewModelFactory.create(it) }
- override val columns =
- hydrator.hydratedStateOf(traceName = "columns", source = interactor.columns)
+ private val columnsWithoutMedia by
+ hydrator.hydratedStateOf(traceName = "columnsWithoutMedia", source = interactor.columns)
override suspend fun onActivated(): Nothing {
- hydrator.activate()
+ coroutineScope {
+ launch { hydrator.activate() }
+ launch { mediaInRowInLandscapeViewModel?.activate() }
+ awaitCancellation()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(mediaLocation: Int?): QSColumnsViewModel
+
+ fun createWithoutMediaTracking() = create(null)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
index 0859c86d74e1..adc4e4bf0870 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS
import com.android.systemui.qs.panels.domain.interactor.QuickQuickSettingsRowInteractor
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
@@ -36,43 +37,59 @@ class QuickQuickSettingsViewModel
@AssistedInject
constructor(
tilesInteractor: CurrentTilesInteractor,
- private val qsColumnsViewModel: QSColumnsViewModel,
+ qsColumnsViewModelFactory: QSColumnsViewModel.Factory,
quickQuickSettingsRowInteractor: QuickQuickSettingsRowInteractor,
+ mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory,
val squishinessViewModel: TileSquishinessViewModel,
iconTilesViewModel: IconTilesViewModel,
val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider,
) : ExclusiveActivatable() {
private val hydrator = Hydrator("QuickQuickSettingsViewModel")
+ private val qsColumnsViewModel = qsColumnsViewModelFactory.create(LOCATION_QQS)
+ private val mediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QQS)
- val columns by qsColumnsViewModel.columns
+ val columns: Int
+ get() = qsColumnsViewModel.columns
private val largeTiles by
hydrator.hydratedStateOf(traceName = "largeTiles", source = iconTilesViewModel.largeTiles)
- private val rows by
+ private val rows: Int
+ get() =
+ if (mediaInRowViewModel.shouldMediaShowInRow) {
+ rowsWithoutMedia * 2
+ } else {
+ rowsWithoutMedia
+ }
+
+ private val rowsWithoutMedia by
hydrator.hydratedStateOf(
- traceName = "rows",
+ traceName = "rowsWithoutMedia",
initialValue = quickQuickSettingsRowInteractor.defaultRows,
source = quickQuickSettingsRowInteractor.rows,
)
+ private val largeTilesSpan by
+ hydrator.hydratedStateOf(
+ traceName = "largeTilesSpan",
+ source = iconTilesViewModel.largeTilesSpan,
+ )
+
private val currentTiles by
hydrator.hydratedStateOf(traceName = "currentTiles", source = tilesInteractor.currentTiles)
val tileViewModels by derivedStateOf {
currentTiles
- .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
+ .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width()) }
.let { splitInRowsSequence(it, columns).take(rows).toList().flatten() }
}
- private val TileSpec.width: Int
- get() = if (largeTiles.contains(this)) 2 else 1
-
override suspend fun onActivated(): Nothing {
coroutineScope {
launch { hydrator.activate() }
launch { qsColumnsViewModel.activate() }
+ launch { mediaInRowViewModel.activate() }
awaitCancellation()
}
}
@@ -81,4 +98,6 @@ constructor(
interface Factory {
fun create(): QuickQuickSettingsViewModel
}
+
+ private fun TileSpec.width(): Int = if (largeTiles.contains(this)) largeTilesSpan else 1
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 56675e49d4e6..2fc7f0f9d67b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
import android.content.res.Resources
+import android.graphics.drawable.Drawable
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.widget.Switch
@@ -36,6 +37,7 @@ data class TileUiState(
val handlesLongClick: Boolean,
val handlesSecondaryClick: Boolean,
val icon: Supplier<QSTile.Icon?>,
+ val sideDrawable: Drawable?,
val accessibilityUiState: AccessibilityUiState,
)
@@ -90,6 +92,7 @@ fun QSTile.State.toUiState(resources: Resources): TileUiState {
handlesLongClick = handlesLongClick,
handlesSecondaryClick = handlesSecondaryClick,
icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
+ sideDrawable = sideViewCustomDrawable,
AccessibilityUiState(
contentDescription?.toString() ?: "",
stateDescription.toString(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
index f702da46717a..c9a0635021da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -54,6 +54,7 @@ object SubtitleArrayMapping {
subtitleIdsMap["dream"] = R.array.tile_states_dream
subtitleIdsMap["font_scaling"] = R.array.tile_states_font_scaling
subtitleIdsMap["hearing_devices"] = R.array.tile_states_hearing_devices
+ subtitleIdsMap["notes"] = R.array.tile_states_notes
}
/** Get the subtitle resource id of the given tile */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 1c9cb3d99480..fef5a745c1ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -25,6 +25,7 @@ import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.MetricsLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Background
@@ -48,7 +49,6 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.runBlocking
class ModesTile
@@ -120,8 +120,7 @@ constructor(
tileState = tileMapper.map(config, model)
state?.apply {
this.state = tileState.activationState.legacyState
- val tileStateIcon = tileState.icon()
- icon = tileStateIcon?.asQSTileIcon() ?: ResourceIcon.get(ICON_RES_ID)
+ icon = tileState.icon?.asQSTileIcon() ?: ResourceIcon.get(ICON_RES_ID)
label = tileLabel
secondaryLabel = tileState.secondaryLabel
contentDescription = tileState.contentDescription
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
new file mode 100644
index 000000000000..69df0961bf66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.service.quicksettings.Tile
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.runBlocking
+
+/** Quick settings tile: Notes */
+class NotesTile
+@Inject constructor(
+ private val host: QSHost,
+ private val uiEventLogger: QsEventLogger,
+ @Background private val backgroundLooper: Looper,
+ @Main private val mainHandler: Handler,
+ private val falsingManager: FalsingManager,
+ private val metricsLogger: MetricsLogger,
+ private val statusBarStateController: StatusBarStateController,
+ private val activityStarter: ActivityStarter,
+ private val qsLogger: QSLogger,
+ private val qsTileConfigProvider: QSTileConfigProvider,
+ private val dataInteractor: NotesTileDataInteractor,
+ private val tileMapper: NotesTileMapper,
+ private val userActionInteractor: NotesTileUserActionInteractor,
+) :
+ QSTileImpl<QSTile.State?>(
+ host,
+ uiEventLogger,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ ) {
+
+ private lateinit var tileState: QSTileState
+ private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
+
+ override fun getTileLabel(): CharSequence =
+ mContext.getString(config.uiConfig.labelRes)
+
+ override fun newTileState(): QSTile.State? {
+ return QSTile.State().apply { state = Tile.STATE_INACTIVE }
+ }
+
+ override fun handleClick(expandable: Expandable?) {
+ userActionInteractor.handleClick()
+ }
+
+ override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
+
+ override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
+ val model =
+ if (arg is NotesTileModel) arg else dataInteractor.getCurrentTileModel()
+ tileState = tileMapper.map(config, model)
+
+ state?.apply {
+ this.state = tileState.activationState.legacyState
+ icon = ResourceIcon.get(tileState.iconRes ?: R.drawable.ic_qs_notes)
+ label = tileState.label
+ contentDescription = tileState.contentDescription
+ expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
+ }
+ }
+
+ override fun isAvailable(): Boolean {
+ return dataInteractor.isAvailable()
+ }
+
+ companion object {
+ const val TILE_SPEC = "notes"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index f218d86a5aa1..37d24debe958 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -243,15 +243,15 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
}
mSelectedCard = cards.get(selectedIndex);
switch (mSelectedCard.getCardImage().getType()) {
+ case TYPE_BITMAP:
+ case TYPE_ADAPTIVE_BITMAP:
+ mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext);
+ break;
case TYPE_URI:
case TYPE_URI_ADAPTIVE_BITMAP:
- mCardViewDrawable = null;
- break;
case TYPE_RESOURCE:
- case TYPE_BITMAP:
- case TYPE_ADAPTIVE_BITMAP:
case TYPE_DATA:
- mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext);
+ mCardViewDrawable = null;
break;
default:
Log.e(TAG, "Unknown icon type: " + mSelectedCard.getCardImage().getType());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 284239ab1a78..f3be340f4951 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -191,8 +191,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
mPanelInteractor.collapsePanels();
};
- final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
- mDialogTransitionAnimator, mActivityStarter, onStartRecordingClicked);
+ final Dialog dialog = mController.createScreenRecordDialog(onStartRecordingClicked);
ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromExpandable) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index 9fb1d46c4241..d67057a2f476 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -30,10 +30,8 @@ import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
class AirplaneModeMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- val theme: Theme,
-) : QSTileDataToStateMapper<AirplaneModeTileModel> {
+constructor(@Main private val resources: Resources, val theme: Theme) :
+ QSTileDataToStateMapper<AirplaneModeTileModel> {
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
@@ -43,16 +41,7 @@ constructor(
} else {
R.drawable.qs_airplane_icon_off
}
-
- icon = {
- Icon.Loaded(
- resources.getDrawable(
- iconRes!!,
- theme,
- ),
- contentDescription = null
- )
- }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
@@ -62,9 +51,6 @@ constructor(
}
contentDescription = label
supportedActions =
- setOf(
- QSTileState.UserAction.CLICK,
- QSTileState.UserAction.LONG_CLICK,
- )
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index f0889433094a..7322b8d098fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -45,6 +45,7 @@ constructor(
val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E HH:mm")
val formatterDateOnly: DateTimeFormatter = DateTimeFormatter.ofPattern("E MMM d")
}
+
override fun map(config: QSTileConfig, data: AlarmTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
when (data) {
@@ -54,13 +55,13 @@ constructor(
val alarmDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(data.alarmClockInfo.triggerTime),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
val nowDateTime =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(clock.currentTimeMillis()),
- TimeZone.getDefault().toZoneId()
+ TimeZone.getDefault().toZoneId(),
)
// Edge case: If it's 8:00:30 right now and alarm is requested for next week at
@@ -84,7 +85,7 @@ constructor(
}
}
iconRes = R.drawable.ic_alarm
- icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
index bcf0935adf85..5b30e8d2c86b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
@@ -29,10 +29,8 @@ import javax.inject.Inject
/** Maps [BatterySaverTileModel] to [QSTileState]. */
open class BatterySaverTileMapper
@Inject
-constructor(
- @Main protected val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<BatterySaverTileModel> {
+constructor(@Main protected val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<BatterySaverTileModel> {
override fun map(config: QSTileConfig, data: BatterySaverTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
@@ -41,8 +39,7 @@ constructor(
iconRes =
if (data.isPowerSaving) R.drawable.qs_battery_saver_icon_on
else R.drawable.qs_battery_saver_icon_off
- icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
-
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
sideViewIcon = QSTileState.SideViewIcon.None
if (data.isPluggedIn) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
index cad7c65ad112..7c90b3d87958 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.colorcorrection.domain
import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
@@ -28,17 +29,14 @@ import javax.inject.Inject
/** Maps [ColorCorrectionTileModel] to [QSTileState]. */
class ColorCorrectionTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<ColorCorrectionTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<ColorCorrectionTileModel> {
override fun map(config: QSTileConfig, data: ColorCorrectionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction)
-
iconRes = R.drawable.ic_qs_color_correction
-
+ icon = Icon.Loaded(resources.getDrawable(R.drawable.ic_qs_color_correction)!!, null)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
index 984228d80b7f..60aa4ea4759f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
@@ -35,10 +35,8 @@ import javax.inject.Inject
@SysUISingleton
class CustomTileMapper
@Inject
-constructor(
- private val context: Context,
- private val uriGrantsManager: IUriGrantsManager,
-) : QSTileDataToStateMapper<CustomTileDataModel> {
+constructor(private val context: Context, private val uriGrantsManager: IUriGrantsManager) :
+ QSTileDataToStateMapper<CustomTileDataModel> {
override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
val userContext =
@@ -50,7 +48,7 @@ constructor(
val iconResult =
if (userContext != null) {
- getIconProvider(
+ getIcon(
userContext = userContext,
icon = data.tile.icon,
callingAppUid = data.callingAppUid,
@@ -58,16 +56,16 @@ constructor(
defaultIcon = data.defaultTileIcon,
)
} else {
- IconResult({ null }, true)
+ IconResult(null, true)
}
- return QSTileState.build(iconResult.iconProvider, data.tile.label) {
+ return QSTileState.build(iconResult.icon, data.tile.label) {
var tileState: Int = data.tile.state
if (data.hasPendingBind) {
tileState = Tile.STATE_UNAVAILABLE
}
- icon = iconResult.iconProvider
+ icon = iconResult.icon
activationState =
if (iconResult.failedToLoad) {
QSTileState.ActivationState.UNAVAILABLE
@@ -102,7 +100,7 @@ constructor(
}
@SuppressLint("MissingPermission") // android.permission.INTERACT_ACROSS_USERS_FULL
- private fun getIconProvider(
+ private fun getIcon(
userContext: Context,
icon: android.graphics.drawable.Icon?,
callingAppUid: Int,
@@ -123,17 +121,12 @@ constructor(
null
} ?: defaultIcon?.loadDrawable(userContext)
return IconResult(
- {
- drawable?.constantState?.newDrawable()?.let {
- Icon.Loaded(it, contentDescription = null)
- }
+ drawable?.constantState?.newDrawable()?.let {
+ Icon.Loaded(it, contentDescription = null)
},
failedToLoad,
)
}
- class IconResult(
- val iconProvider: () -> Icon?,
- val failedToLoad: Boolean,
- )
+ class IconResult(val icon: Icon?, val failedToLoad: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index d7d61241fc6c..7e557ebe4639 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -30,10 +30,8 @@ import javax.inject.Inject
/** Maps [FlashlightTileModel] to [QSTileState]. */
class FlashlightMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Theme,
-) : QSTileDataToStateMapper<FlashlightTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Theme) :
+ QSTileDataToStateMapper<FlashlightTileModel> {
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
@@ -43,15 +41,8 @@ constructor(
} else {
R.drawable.qs_flashlight_icon_off
}
- val icon =
- Icon.Loaded(
- resources.getDrawable(
- iconRes!!,
- theme,
- ),
- contentDescription = null
- )
- this.icon = { icon }
+
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
index 6b4dda13a5e6..9d44fc6ae25e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
@@ -29,23 +29,13 @@ import javax.inject.Inject
/** Maps [FontScalingTileModel] to [QSTileState]. */
class FontScalingTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<FontScalingTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<FontScalingTileModel> {
override fun map(config: QSTileConfig, data: FontScalingTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
iconRes = R.drawable.ic_qs_font_scaling
- val icon =
- Icon.Loaded(
- resources.getDrawable(
- iconRes!!,
- theme,
- ),
- contentDescription = null
- )
- this.icon = { icon }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
contentDescription = label
activationState = QSTileState.ActivationState.ACTIVE
sideViewIcon = QSTileState.SideViewIcon.Chevron
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
index 8dd611f9911a..c3ac1f8d9a72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -36,9 +36,7 @@ constructor(@Main private val resources: Resources, private val theme: Resources
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_hearing_devices_label)
iconRes = R.drawable.qs_hearing_devices_icon
- val loadedIcon =
- Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
- icon = { loadedIcon }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
if (data.isAnyActiveHearingDevice) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index bb0b9b7084fa..fc945851cdad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -61,28 +61,26 @@ constructor(
when (val dataIcon = data.icon) {
is InternetTileIconModel.ResourceId -> {
iconRes = dataIcon.resId
- icon = {
+ icon =
Icon.Loaded(
resources.getDrawable(dataIcon.resId, theme),
contentDescription = null,
)
- }
}
is InternetTileIconModel.Cellular -> {
val signalDrawable = SignalDrawable(context, handler)
signalDrawable.setLevel(dataIcon.level)
- icon = { Icon.Loaded(signalDrawable, contentDescription = null) }
+ icon = Icon.Loaded(signalDrawable, contentDescription = null)
}
is InternetTileIconModel.Satellite -> {
iconRes = dataIcon.resourceIcon.res // level is inferred from res
- icon = {
+ icon =
Icon.Loaded(
resources.getDrawable(dataIcon.resourceIcon.res, theme),
contentDescription = null,
)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
index 40aee65f41a7..3692c35472f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
@@ -30,10 +30,8 @@ import javax.inject.Inject
/** Maps [ColorInversionTileModel] to [QSTileState]. */
class ColorInversionTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Theme,
-) : QSTileDataToStateMapper<ColorInversionTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Theme) :
+ QSTileDataToStateMapper<ColorInversionTileModel> {
override fun map(config: QSTileConfig, data: ColorInversionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_inversion)
@@ -47,7 +45,7 @@ constructor(
secondaryLabel = subtitleArray[1]
iconRes = R.drawable.qs_invert_colors_icon_off
}
- icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
index ff931b35567f..3fe2a7734801 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
@@ -28,21 +28,26 @@ import javax.inject.Inject
class IssueRecordingMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Theme,
-) : QSTileDataToStateMapper<IssueRecordingModel> {
+constructor(@Main private val resources: Resources, private val theme: Theme) :
+ QSTileDataToStateMapper<IssueRecordingModel> {
override fun map(config: QSTileConfig, data: IssueRecordingModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- if (data.isRecording) {
- activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = resources.getString(R.string.qs_record_issue_stop)
- icon = { Icon.Resource(R.drawable.qs_record_issue_icon_on, null) }
- } else {
- icon = { Icon.Resource(R.drawable.qs_record_issue_icon_off, null) }
- activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = resources.getString(R.string.qs_record_issue_start)
- }
+ icon =
+ if (data.isRecording) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ secondaryLabel = resources.getString(R.string.qs_record_issue_stop)
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_record_issue_icon_on, theme),
+ null,
+ )
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = resources.getString(R.string.qs_record_issue_start)
+ Icon.Loaded(
+ resources.getDrawable(R.drawable.qs_record_issue_icon_off, theme),
+ null,
+ )
+ }
supportedActions = setOf(QSTileState.UserAction.CLICK)
contentDescription = "$label, $secondaryLabel"
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index d58f5abcd018..08432f685ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -30,10 +30,8 @@ import javax.inject.Inject
/** Maps [LocationTileModel] to [QSTileState]. */
class LocationTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Theme,
-) : QSTileDataToStateMapper<LocationTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Theme) :
+ QSTileDataToStateMapper<LocationTileModel> {
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
@@ -43,17 +41,9 @@ constructor(
} else {
R.drawable.qs_location_icon_off
}
- val icon =
- Icon.Loaded(
- resources.getDrawable(
- iconRes!!,
- theme,
- ),
- contentDescription = null
- )
- this.icon = { icon }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
- this.label = resources.getString(R.string.quick_settings_location_label)
+ label = resources.getString(R.string.quick_settings_location_label)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 69da3134314b..4a6431359ca2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -30,14 +30,12 @@ import javax.inject.Inject
class ModesTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- val theme: Resources.Theme,
-) : QSTileDataToStateMapper<ModesTileModel> {
+constructor(@Main private val resources: Resources, val theme: Resources.Theme) :
+ QSTileDataToStateMapper<ModesTileModel> {
override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
iconRes = data.iconResId
- icon = { data.icon }
+ icon = data.icon
activationState =
if (data.isActivated) {
QSTileState.ActivationState.ACTIVE
@@ -47,10 +45,7 @@ constructor(
secondaryLabel = getModesStatus(data, resources)
contentDescription = "$label. $secondaryLabel"
supportedActions =
- setOf(
- QSTileState.UserAction.CLICK,
- QSTileState.UserAction.LONG_CLICK,
- )
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
sideViewIcon = QSTileState.SideViewIcon.Chevron
expandedAccessibilityClass = Button::class
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
index bcf7cc763b9e..081a03c7ae67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
@@ -57,9 +57,8 @@ constructor(
activationState = QSTileState.ActivationState.INACTIVE
iconRes = R.drawable.qs_nightlight_icon_off
}
- val loadedIcon =
- Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
- icon = { loadedIcon }
+
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
secondaryLabel = getSecondaryLabel(data, resources)
@@ -70,7 +69,7 @@ constructor(
private fun getSecondaryLabel(
data: NightDisplayTileModel,
- resources: Resources
+ resources: Resources,
): CharSequence? {
when (data) {
is NightDisplayTileModel.AutoModeTwilight -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
new file mode 100644
index 000000000000..ee1b9e5171b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain
+
+import android.content.res.Resources
+import android.widget.Button
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class NotesTileMapper
+@Inject
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<NotesTileModel> {
+ override fun map(config: QSTileConfig, data: NotesTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ iconRes = R.drawable.ic_qs_notes
+ icon =
+ Icon.Loaded(
+ resources.getDrawable(
+ iconRes!!,
+ theme),
+ contentDescription = null
+ )
+ contentDescription = label
+ activationState = QSTileState.ActivationState.INACTIVE
+ sideViewIcon = QSTileState.SideViewIcon.Chevron
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ expandedAccessibilityClass = Button::class
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
new file mode 100644
index 000000000000..a501b8561330
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.notetask.NoteTaskEnabledKey
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+class NotesTileDataInteractor
+@Inject
+constructor(@NoteTaskEnabledKey private val isNoteTaskEnabled: Boolean) :
+ QSTileDataInteractor<NotesTileModel> {
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>,
+ ): Flow<NotesTileModel> = flowOf(NotesTileModel)
+
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(isAvailable())
+
+ fun isAvailable(): Boolean {
+ return Flags.notesRoleQsTile() && isNoteTaskEnabled
+ }
+
+ fun getCurrentTileModel(): NotesTileModel {
+ return NotesTileModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
new file mode 100644
index 000000000000..df01d99df0df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.interactor
+
+import com.android.systemui.animation.Expandable
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+class NotesTileUserActionInteractor
+@Inject constructor(
+ private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
+ private val panelInteractor: PanelInteractor,
+ private val noteTaskController: NoteTaskController,
+) : QSTileUserActionInteractor<NotesTileModel> {
+ val longClickIntent = NoteTaskController.createNotesRoleHolderSettingsIntent()
+
+ override suspend fun handleInput(input: QSTileInput<NotesTileModel>) {
+ when (input.action) {
+ is QSTileUserAction.Click -> handleClick()
+ is QSTileUserAction.LongClick -> handleLongClick(input.action.expandable)
+ is QSTileUserAction.ToggleClick -> {}
+ }
+ }
+
+ fun handleClick() {
+ noteTaskController.showNoteTask(NoteTaskEntryPoint.QS_NOTES_TILE)
+ panelInteractor.collapsePanels()
+ }
+
+ fun handleLongClick(expandable: Expandable?) {
+ qsTileIntentUserInputHandler.handle(expandable, longClickIntent)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt
new file mode 100644
index 000000000000..b72aa60e16f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/model/NotesTileModel.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes.domain.model
+
+/** NotesTileModel tile model. */
+data object NotesTileModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
index 40809960735f..8e5d0d4eb3dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
@@ -29,17 +29,15 @@ import javax.inject.Inject
/** Maps [OneHandedModeTileModel] to [QSTileState]. */
class OneHandedModeTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<OneHandedModeTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<OneHandedModeTileModel> {
override fun map(config: QSTileConfig, data: OneHandedModeTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_onehanded)
label = resources.getString(R.string.quick_settings_onehanded_label)
iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
- icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
index 823174234b13..5c6351e88494 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
@@ -29,17 +29,15 @@ import javax.inject.Inject
/** Maps [QRCodeScannerTileModel] to [QSTileState]. */
class QRCodeScannerTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<QRCodeScannerTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<QRCodeScannerTileModel> {
override fun map(config: QSTileConfig, data: QRCodeScannerTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.qr_code_scanner_title)
contentDescription = label
iconRes = R.drawable.ic_qr_code_scanner
- icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
sideViewIcon = QSTileState.SideViewIcon.Chevron
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
index 85ee02207ac6..fe77fe61b4bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
@@ -30,10 +30,8 @@ import javax.inject.Inject
/** Maps [ReduceBrightColorsTileModel] to [QSTileState]. */
class ReduceBrightColorsTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<ReduceBrightColorsTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<ReduceBrightColorsTileModel> {
override fun map(config: QSTileConfig, data: ReduceBrightColorsTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
@@ -50,12 +48,7 @@ constructor(
resources
.getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_INACTIVE]
}
- icon = {
- Icon.Loaded(
- drawable = resources.getDrawable(iconRes!!, theme),
- contentDescription = null
- )
- }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
label =
resources.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index 33dc6ed7a1e8..9a003ffdf7de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -36,36 +36,33 @@ constructor(
@Main private val resources: Resources,
private val theme: Resources.Theme,
private val devicePostureController: DevicePostureController,
- private val deviceStateManager: DeviceStateManager
+ private val deviceStateManager: DeviceStateManager,
) : QSTileDataToStateMapper<RotationLockTileModel> {
override fun map(config: QSTileConfig, data: RotationLockTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- this.label = resources.getString(R.string.quick_settings_rotation_unlocked_label)
- this.contentDescription =
- resources.getString(R.string.accessibility_quick_settings_rotation)
+ label = resources.getString(R.string.quick_settings_rotation_unlocked_label)
+ contentDescription = resources.getString(R.string.accessibility_quick_settings_rotation)
if (data.isRotationLocked) {
activationState = QSTileState.ActivationState.INACTIVE
- this.secondaryLabel = EMPTY_SECONDARY_STRING
+ secondaryLabel = EMPTY_SECONDARY_STRING
iconRes = R.drawable.qs_auto_rotate_icon_off
} else {
activationState = QSTileState.ActivationState.ACTIVE
- this.secondaryLabel =
+ secondaryLabel =
if (data.isCameraRotationEnabled) {
resources.getString(R.string.rotation_lock_camera_rotation_on)
} else {
EMPTY_SECONDARY_STRING
}
- this.iconRes = R.drawable.qs_auto_rotate_icon_on
- }
- this.icon = {
- Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ iconRes = R.drawable.qs_auto_rotate_icon_on
}
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
if (isDeviceFoldable(resources, deviceStateManager)) {
- this.secondaryLabel = getSecondaryLabelWithPosture(this.activationState)
+ secondaryLabel = getSecondaryLabelWithPosture(activationState)
}
- this.stateDescription = this.secondaryLabel
- this.sideViewIcon = QSTileState.SideViewIcon.None
+ stateDescription = secondaryLabel
+ sideViewIcon = QSTileState.SideViewIcon.None
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
}
@@ -86,7 +83,7 @@ constructor(
return resources.getString(
R.string.rotation_tile_with_posture_secondary_label_template,
stateName,
- posture
+ posture,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
index 888bba87a03a..08196bbfe2f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -29,10 +29,8 @@ import javax.inject.Inject
/** Maps [DataSaverTileModel] to [QSTileState]. */
class DataSaverTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<DataSaverTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<DataSaverTileModel> {
override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
with(data) {
@@ -45,9 +43,7 @@ constructor(
iconRes = R.drawable.qs_data_saver_icon_off
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
- val loadedIcon =
- Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
- icon = { loadedIcon }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 48b39ed25750..85aa6745e438 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -16,16 +16,13 @@
package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
-import android.content.Context
import android.util.Log
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter
@@ -45,7 +42,6 @@ import kotlinx.coroutines.withContext
class ScreenRecordTileUserActionInteractor
@Inject
constructor(
- @Application private val context: Context,
@Main private val mainContext: CoroutineContext,
@Background private val backgroundContext: CoroutineContext,
private val screenRecordRepository: ScreenRecordRepository,
@@ -55,8 +51,6 @@ constructor(
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val panelInteractor: PanelInteractor,
private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
- private val featureFlags: FeatureFlagsClassic,
- private val activityStarter: ActivityStarter,
) : QSTileUserActionInteractor<ScreenRecordModel> {
override suspend fun handleInput(input: QSTileInput<ScreenRecordModel>): Unit =
with(input) {
@@ -89,14 +83,7 @@ constructor(
panelInteractor.collapsePanels()
}
- val dialog =
- recordingController.createScreenRecordDialog(
- context,
- featureFlags,
- dialogTransitionAnimator,
- activityStarter,
- onStartRecordingClicked
- )
+ val dialog = recordingController.createScreenRecordDialog(onStartRecordingClicked)
if (dialog == null) {
Log.w(TAG, "showPrompt: dialog was null")
@@ -115,7 +102,7 @@ constructor(
expandable?.dialogTransitionController(
DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
- INTERACTION_JANK_TAG
+ INTERACTION_JANK_TAG,
)
)
controller?.let {
@@ -135,7 +122,7 @@ constructor(
keyguardDismissUtil.executeWhenUnlocked(
dismissAction,
false /* requiresShadeOpen */,
- true /* afterKeyguardDone */
+ true, /* afterKeyguardDone */
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
index e74e77f29007..ba06de966c10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
@@ -30,10 +30,8 @@ import javax.inject.Inject
/** Maps [ScreenRecordModel] to [QSTileState]. */
class ScreenRecordTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Resources.Theme,
-) : QSTileDataToStateMapper<ScreenRecordModel> {
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<ScreenRecordModel> {
override fun map(config: QSTileConfig, data: ScreenRecordModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_screen_record_label)
@@ -43,24 +41,12 @@ constructor(
is ScreenRecordModel.Recording -> {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_screen_record_icon_on
- val loadedIcon =
- Icon.Loaded(
- resources.getDrawable(iconRes!!, theme),
- contentDescription = null
- )
- icon = { loadedIcon }
sideViewIcon = QSTileState.SideViewIcon.None
secondaryLabel = resources.getString(R.string.quick_settings_screen_record_stop)
}
is ScreenRecordModel.Starting -> {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_screen_record_icon_on
- val loadedIcon =
- Icon.Loaded(
- resources.getDrawable(iconRes!!, theme),
- contentDescription = null
- )
- icon = { loadedIcon }
val countDown = data.countdownSeconds
sideViewIcon = QSTileState.SideViewIcon.None
secondaryLabel = String.format("%d...", countDown)
@@ -68,17 +54,13 @@ constructor(
is ScreenRecordModel.DoingNothing -> {
activationState = QSTileState.ActivationState.INACTIVE
iconRes = R.drawable.qs_screen_record_icon_off
- val loadedIcon =
- Icon.Loaded(
- resources.getDrawable(iconRes!!, theme),
- contentDescription = null
- )
- icon = { loadedIcon }
sideViewIcon = QSTileState.SideViewIcon.Chevron // tapping will open dialog
secondaryLabel =
resources.getString(R.string.quick_settings_screen_record_start)
}
}
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+
contentDescription =
if (TextUtils.isEmpty(secondaryLabel)) label
else TextUtils.concat(label, ", ", secondaryLabel)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
index 597cf274dcff..b4cfec48fb0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
@@ -51,8 +51,7 @@ constructor(
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
iconRes = sensorPrivacyTileResources.getIconRes(data.isBlocked)
- icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
-
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
sideViewIcon = QSTileState.SideViewIcon.None
if (data.isBlocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
index f29c745d8119..eda8e5ce8c43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
@@ -34,14 +34,13 @@ import javax.inject.Inject
/** Maps [UiModeNightTileModel] to [QSTileState]. */
class UiModeNightTileMapper
@Inject
-constructor(
- @Main private val resources: Resources,
- private val theme: Theme,
-) : QSTileDataToStateMapper<UiModeNightTileModel> {
+constructor(@Main private val resources: Resources, private val theme: Theme) :
+ QSTileDataToStateMapper<UiModeNightTileModel> {
companion object {
val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a")
val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
}
+
override fun map(config: QSTileConfig, data: UiModeNightTileModel): QSTileState =
with(data) {
QSTileState.build(resources, theme, config.uiConfig) {
@@ -76,7 +75,7 @@ constructor(
if (isNightMode)
R.string.quick_settings_dark_mode_secondary_label_until
else R.string.quick_settings_dark_mode_secondary_label_on_at,
- formatter.format(time)
+ formatter.format(time),
)
} else if (
nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME
@@ -121,9 +120,7 @@ constructor(
if (activationState == QSTileState.ActivationState.ACTIVE)
R.drawable.qs_light_dark_theme_icon_on
else R.drawable.qs_light_dark_theme_icon_off
- val loadedIcon =
- Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
- icon = { loadedIcon }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
supportedActions =
if (activationState == QSTileState.ActivationState.UNAVAILABLE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
index eee95b7311d3..a1bc8a889a1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
@@ -42,9 +42,7 @@ constructor(
label = getTileLabel()!!
contentDescription = label
iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
- icon = {
- Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
- }
+ icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
when (data) {
is WorkModeTileModel.HasActiveProfile -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 549f0a73908d..8394be5e0a38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -35,7 +35,7 @@ import kotlin.reflect.KClass
* // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
*/
data class QSTileState(
- val icon: () -> Icon?,
+ val icon: Icon?,
val iconRes: Int?,
val label: CharSequence,
val activationState: ActivationState,
@@ -54,21 +54,18 @@ data class QSTileState(
resources: Resources,
theme: Theme,
config: QSTileUIConfig,
- builder: Builder.() -> Unit
+ builder: Builder.() -> Unit,
): QSTileState {
val iconDrawable = resources.getDrawable(config.iconRes, theme)
return build(
- { Icon.Loaded(iconDrawable, null) },
+ Icon.Loaded(iconDrawable, null),
resources.getString(config.labelRes),
builder,
)
}
- fun build(
- icon: () -> Icon?,
- label: CharSequence,
- builder: Builder.() -> Unit
- ): QSTileState = Builder(icon, label).apply { builder() }.build()
+ fun build(icon: Icon?, label: CharSequence, builder: Builder.() -> Unit): QSTileState =
+ Builder(icon, label).apply { builder() }.build()
}
enum class ActivationState(val legacyState: Int) {
@@ -117,10 +114,7 @@ data class QSTileState(
data object None : SideViewIcon
}
- class Builder(
- var icon: () -> Icon?,
- var label: CharSequence,
- ) {
+ class Builder(var icon: Icon?, var label: CharSequence) {
var iconRes: Int? = null
var activationState: ActivationState = ActivationState.INACTIVE
var secondaryLabel: CharSequence? = null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index f89745f49cc8..35b1b9636263 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.viewmodel
import android.content.Context
import android.os.UserHandle
import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.InstanceId
import com.android.systemui.Dumpable
import com.android.systemui.animation.Expandable
@@ -42,7 +43,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.takeWhile
-import com.android.app.tracing.coroutines.launchTraced as launch
// TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
class QSTileViewModelAdapter
@@ -223,7 +223,7 @@ constructor(
fun mapState(
context: Context,
viewModelState: QSTileState,
- config: QSTileConfig
+ config: QSTileConfig,
): QSTile.State =
// we have to use QSTile.BooleanState to support different side icons
// which are bound to instanceof QSTile.BooleanState in QSTileView.
@@ -241,7 +241,7 @@ constructor(
viewModelState.supportedActions.contains(QSTileState.UserAction.TOGGLE_CLICK)
icon =
- when (val stateIcon = viewModelState.icon()) {
+ when (val stateIcon = viewModelState.icon) {
is Icon.Loaded ->
if (viewModelState.iconRes == null) DrawableIcon(stateIcon.drawable)
else DrawableIconWithRes(stateIcon.drawable, viewModelState.iconRes)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 6d5bf328d00b..d4adcdd49f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -22,6 +22,7 @@ import android.os.Bundle
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.applications.InterestingConfigChanges
import com.android.systemui.Dumpable
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
@@ -36,6 +37,7 @@ import com.android.systemui.qs.QSImpl
import com.android.systemui.qs.dagger.QSSceneComponent
import com.android.systemui.res.R
import com.android.systemui.settings.brightness.MirrorController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.util.kotlin.sample
@@ -57,7 +59,6 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
// TODO(307945185) Split View concerns into a ViewBinder
@@ -206,7 +207,7 @@ constructor(
dumpManager: DumpManager,
@Main private val mainDispatcher: CoroutineDispatcher,
@Application applicationScope: CoroutineScope,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val asyncLayoutInflaterFactory: (Context) -> AsyncLayoutInflater,
) : QSContainerController, QSSceneAdapter, Dumpable {
@@ -219,7 +220,7 @@ constructor(
dumpManager: DumpManager,
@Main dispatcher: CoroutineDispatcher,
@Application scope: CoroutineScope,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
) : this(
qsSceneComponentFactory,
qsImplProvider,
@@ -256,7 +257,7 @@ constructor(
.stateIn(
applicationScope,
SharingStarted.WhileSubscribed(),
- customizerState.value.isShowing
+ customizerState.value.isShowing,
)
override val customizerAnimationDuration: StateFlow<Int> =
customizerState
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 9a416d1d3b3b..000f7f8a7d31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.qs.ui.viewmodel
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
@@ -47,7 +46,7 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM
put(Back, HideOverlay(Overlays.QuickSettingsShade))
}
put(
- Swipe(SwipeDirection.Down, fromSource = SceneContainerEdge.TopLeft),
+ Swipe.Down(fromSource = SceneContainerEdge.TopLeft),
ReplaceByOverlay(Overlays.NotificationsShade),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
index 54e5caca107c..f59541545de4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
@@ -20,7 +20,6 @@ import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -44,10 +43,8 @@ import kotlinx.coroutines.flow.map
*/
class QuickSettingsUserActionsViewModel
@AssistedInject
-constructor(
- private val qsSceneAdapter: QSSceneAdapter,
- sceneBackInteractor: SceneBackInteractor,
-) : UserActionsViewModel() {
+constructor(private val qsSceneAdapter: QSSceneAdapter, sceneBackInteractor: SceneBackInteractor) :
+ UserActionsViewModel() {
private val backScene: Flow<SceneKey> =
sceneBackInteractor.backScene
@@ -55,10 +52,7 @@ constructor(
.map { it ?: Scenes.Shade }
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
- combine(
- qsSceneAdapter.isCustomizerShowing,
- backScene,
- ) { isCustomizing, backScene ->
+ combine(qsSceneAdapter.isCustomizerShowing, backScene) { isCustomizing, backScene ->
buildMap<UserAction, UserActionResult> {
if (isCustomizing) {
// TODO(b/332749288) Empty map so there are no back handlers and back can
@@ -69,9 +63,9 @@ constructor(
// while customizing
} else {
put(Back, UserActionResult(backScene))
- put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
+ put(Swipe.Up, UserActionResult(backScene))
put(
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
+ Swipe.Up(fromSource = Edge.Bottom),
UserActionResult(SceneFamilies.Home),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ce9c441654bf..a5eb92b10239 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -24,7 +24,10 @@ import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.window.BackEvent.EDGE_NONE;
+import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
@@ -41,6 +44,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_V
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_TRANSITION;
import android.annotation.FloatRange;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -114,6 +118,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;
@@ -174,6 +179,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private Region mActiveNavBarRegion;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final BackAnimation mBackAnimation;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
@@ -287,11 +293,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
@Override
- public void onBackPressed() {
- verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- });
+ public void onBackEvent(@Nullable KeyEvent keyEvent) throws RemoteException {
+ if (predictiveBackThreeButtonNav() && predictiveBackSwipeEdgeNoneApi()
+ && mBackAnimation != null && keyEvent != null) {
+ mBackAnimation.setTriggerBack(!keyEvent.isCanceled());
+ mBackAnimation.onBackMotion(/* touchX */ 0, /* touchY */ 0, keyEvent.getAction(),
+ EDGE_NONE);
+ } else {
+ verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ });
+ }
}
@Override
@@ -657,7 +670,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
AssistUtils assistUtils,
DumpManager dumpManager,
Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
- BroadcastDispatcher broadcastDispatcher
+ BroadcastDispatcher broadcastDispatcher,
+ Optional<BackAnimation> backAnimation
) {
// b/241601880: This component should only be running for primary users or
// secondaryUsers when visibleBackgroundUsers are supported.
@@ -695,6 +709,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
mBroadcastDispatcher = broadcastDispatcher;
+ mBackAnimation = backAnimation.orElse(null);
if (!KeyguardWmStateRefactor.isEnabled()) {
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 6758c3ba0767..02b2bb1585bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -35,7 +35,6 @@ import androidx.annotation.WorkerThread
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.SessionCreationSource
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
@@ -132,10 +131,9 @@ constructor(
@WorkerThread
private fun onScreenRecordSwitchClicked() {
if (
- flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
- devicePolicyResolver
- .get()
- .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
+ devicePolicyResolver
+ .get()
+ .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
) {
mainExecutor.execute {
screenCaptureDisabledDialogDelegate.createSysUIDialog().show()
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
index 6aaa27dd7387..eca405180075 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
@@ -69,7 +69,11 @@ open class UserAwareConnection(
@WorkerThread
fun doUnBind() {
if (shouldUnBind) {
- userContextProvider.userContext.unbindService(this)
+ try {
+ userContextProvider.userContext.unbindService(this)
+ } catch (e: IllegalArgumentException) {
+ Log.e(TAG, "Can't disconnect because service wasn't connected anyways.", e)
+ }
shouldUnBind = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index f3c6190d2f7b..daeaaa52fd94 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -46,7 +46,6 @@ import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
@@ -97,7 +96,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -138,7 +136,6 @@ constructor(
private val uiEventLogger: UiEventLogger,
private val sceneBackInteractor: SceneBackInteractor,
private val shadeSessionStorage: SessionStorage,
- private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val statusBarStateController: SysuiStatusBarStateController,
@@ -213,7 +210,6 @@ constructor(
/** Updates the visibility of the scene container. */
private fun hydrateVisibility() {
applicationScope.launch {
- // TODO(b/296114544): Combine with some global hun state to make it visible!
deviceProvisioningInteractor.isDeviceProvisioned
.flatMapLatest { isAllowedToBeVisible ->
if (isAllowedToBeVisible) {
@@ -271,27 +267,6 @@ constructor(
handleDeviceUnlockStatus()
handlePowerState()
handleShadeTouchability()
- handleSurfaceBehindKeyguardVisibility()
- }
-
- private fun handleSurfaceBehindKeyguardVisibility() {
- applicationScope.launch {
- sceneInteractor.currentScene.collectLatest { currentScene ->
- if (currentScene == Scenes.Lockscreen) {
- // Wait for the screen to be on
- powerInteractor.isAwake.first { it }
- // Wait for surface to become visible
- windowMgrLockscreenVisInteractor.surfaceBehindVisibility.first { it }
- // Make sure the device is actually unlocked before force-changing the scene
- deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked }
- // Override the current transition, if any, by forcing the scene to Gone
- sceneInteractor.changeScene(
- toScene = Scenes.Gone,
- loggingReason = "surface behind keyguard is visible",
- )
- }
- }
- }
}
private fun handleBouncerImeVisibility() {
@@ -398,6 +373,7 @@ constructor(
"device was unlocked with alternate bouncer showing" +
" and shade didn't need to be left open"
} else {
+ replaceLockscreenSceneOnBackStack()
null
}
}
@@ -416,16 +392,7 @@ constructor(
val prevScene = previousScene.value
val targetScene = prevScene ?: Scenes.Gone
if (targetScene != Scenes.Gone) {
- sceneBackInteractor.updateBackStack { stack ->
- val list = stack.asIterable().toMutableList()
- check(list.last() == Scenes.Lockscreen) {
- "The bottommost/last SceneKey of the back stack isn't" +
- " the Lockscreen scene like expected. The back" +
- " stack is $stack."
- }
- list[list.size - 1] = Scenes.Gone
- sceneStackOf(*list.toTypedArray())
- }
+ replaceLockscreenSceneOnBackStack()
}
targetScene to
"device was unlocked with primary bouncer showing," +
@@ -460,6 +427,20 @@ constructor(
}
}
+ /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */
+ private fun replaceLockscreenSceneOnBackStack() {
+ sceneBackInteractor.updateBackStack { stack ->
+ val list = stack.asIterable().toMutableList()
+ check(list.last() == Scenes.Lockscreen) {
+ "The bottommost/last SceneKey of the back stack isn't" +
+ " the Lockscreen scene like expected. The back" +
+ " stack is $stack."
+ }
+ list[list.size - 1] = Scenes.Gone
+ sceneStackOf(*list.toTypedArray())
+ }
+ }
+
private fun handlePowerState() {
applicationScope.launch {
powerInteractor.detailedWakefulness.collect { wakefulness ->
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index a8a78a91097c..d7463f8f0c36 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -32,17 +32,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -66,12 +62,10 @@ public class RecordingController
private CountDownTimer mCountDownTimer = null;
private final Executor mMainExecutor;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final FeatureFlags mFlags;
private final UserTracker mUserTracker;
private final RecordingControllerLogger mRecordingControllerLogger;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private final ScreenCaptureDisabledDialogDelegate mScreenCaptureDisabledDialogDelegate;
- private final ScreenRecordDialogDelegate.Factory mScreenRecordDialogFactory;
private final ScreenRecordPermissionDialogDelegate.Factory
mScreenRecordPermissionDialogDelegateFactory;
@@ -116,24 +110,20 @@ public class RecordingController
public RecordingController(
@Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher,
- FeatureFlags flags,
Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
UserTracker userTracker,
RecordingControllerLogger recordingControllerLogger,
MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
ScreenCaptureDisabledDialogDelegate screenCaptureDisabledDialogDelegate,
- ScreenRecordDialogDelegate.Factory screenRecordDialogFactory,
ScreenRecordPermissionDialogDelegate.Factory
screenRecordPermissionDialogDelegateFactory) {
mMainExecutor = mainExecutor;
- mFlags = flags;
mDevicePolicyResolver = devicePolicyResolver;
mBroadcastDispatcher = broadcastDispatcher;
mUserTracker = userTracker;
mRecordingControllerLogger = recordingControllerLogger;
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
mScreenCaptureDisabledDialogDelegate = screenCaptureDisabledDialogDelegate;
- mScreenRecordDialogFactory = screenRecordDialogFactory;
mScreenRecordPermissionDialogDelegateFactory = screenRecordPermissionDialogDelegateFactory;
BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -158,12 +148,8 @@ public class RecordingController
/** Create a dialog to show screen recording options to the user.
* If screen capturing is currently not allowed it will return a dialog
* that warns users about it. */
- public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
- DialogTransitionAnimator dialogTransitionAnimator,
- ActivityStarter activityStarter,
- @Nullable Runnable onStartRecordingClicked) {
- if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)
- && mDevicePolicyResolver.get()
+ public Dialog createScreenRecordDialog(@Nullable Runnable onStartRecordingClicked) {
+ if (mDevicePolicyResolver.get()
.isScreenCaptureCompletelyDisabled(getHostUserHandle())) {
return mScreenCaptureDisabledDialogDelegate.createSysUIDialog();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java
deleted file mode 100644
index 9f1447b1f509..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialogDelegate.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2018 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.screenrecord;
-
-import static android.app.Activity.RESULT_OK;
-
-import static com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
-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 static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.view.Gravity;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-
-import dagger.assisted.Assisted;
-import dagger.assisted.AssistedFactory;
-import dagger.assisted.AssistedInject;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Dialog to select screen recording options
- */
-public class ScreenRecordDialogDelegate implements SystemUIDialog.Delegate {
- private static final List<ScreenRecordingAudioSource> MODES = Arrays.asList(INTERNAL, MIC,
- MIC_AND_INTERNAL);
- private static final long DELAY_MS = 3000;
- private static final long INTERVAL_MS = 1000;
-
- private final SystemUIDialog.Factory mSystemUIDialogFactory;
- private final UserContextProvider mUserContextProvider;
- private final RecordingController mController;
- private final Runnable mOnStartRecordingClicked;
- private Switch mTapsSwitch;
- private Switch mAudioSwitch;
- private Spinner mOptions;
-
- @AssistedFactory
- public interface Factory {
- ScreenRecordDialogDelegate create(
- RecordingController recordingController,
- @Nullable Runnable onStartRecordingClicked
- );
- }
-
- @AssistedInject
- public ScreenRecordDialogDelegate(
- SystemUIDialog.Factory systemUIDialogFactory,
- UserContextProvider userContextProvider,
- @Assisted RecordingController controller,
- @Assisted @Nullable Runnable onStartRecordingClicked) {
- mSystemUIDialogFactory = systemUIDialogFactory;
- mUserContextProvider = userContextProvider;
- mController = controller;
- mOnStartRecordingClicked = onStartRecordingClicked;
- }
-
- @Override
- public SystemUIDialog createDialog() {
- return mSystemUIDialogFactory.create(this);
- }
-
- @Override
- public void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {
- Window window = dialog.getWindow();
-
- window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
-
- window.setGravity(Gravity.CENTER);
- dialog.setTitle(R.string.screenrecord_title);
-
- dialog.setContentView(R.layout.screen_record_dialog);
-
- TextView cancelBtn = dialog.findViewById(R.id.button_cancel);
- cancelBtn.setOnClickListener(v -> dialog.dismiss());
- TextView startBtn = dialog.findViewById(R.id.button_start);
- startBtn.setOnClickListener(v -> {
- if (mOnStartRecordingClicked != null) {
- // Note that it is important to run this callback before dismissing, so that the
- // callback can disable the dialog exit animation if it wants to.
- mOnStartRecordingClicked.run();
- }
-
- // Start full-screen recording
- requestScreenCapture(/* captureTarget= */ null);
- dialog.dismiss();
- });
-
- mAudioSwitch = dialog.findViewById(R.id.screenrecord_audio_switch);
- mTapsSwitch = dialog.findViewById(R.id.screenrecord_taps_switch);
- mOptions = dialog.findViewById(R.id.screen_recording_options);
- ArrayAdapter a = new ScreenRecordingAdapter(dialog.getContext().getApplicationContext(),
- android.R.layout.simple_spinner_dropdown_item,
- MODES);
- a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mOptions.setAdapter(a);
- mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
- mAudioSwitch.setChecked(true);
- });
-
- // disable redundant Touch & Hold accessibility action for Switch Access
- mOptions.setAccessibilityDelegate(new View.AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(@NonNull View host,
- @NonNull AccessibilityNodeInfo info) {
- info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
- super.onInitializeAccessibilityNodeInfo(host, info);
- }
- });
- mOptions.setLongClickable(false);
- }
-
- /**
- * Starts screen capture after some countdown
- * @param captureTarget target to capture (could be e.g. a task) or
- * null to record the whole screen
- */
- private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) {
- Context userContext = mUserContextProvider.getUserContext();
- boolean showTaps = mTapsSwitch.isChecked();
- ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
- ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
- : NONE;
- PendingIntent startIntent = PendingIntent.getForegroundService(userContext,
- RecordingService.REQUEST_CODE,
- RecordingService.getStartIntent(
- userContext, Activity.RESULT_OK,
- audioMode.ordinal(), showTaps, captureTarget),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- PendingIntent stopIntent = PendingIntent.getService(userContext,
- RecordingService.REQUEST_CODE,
- RecordingService.getStopIntent(userContext),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
- }
-
- private class CaptureTargetResultReceiver extends ResultReceiver {
-
- CaptureTargetResultReceiver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == RESULT_OK) {
- MediaProjectionCaptureTarget captureTarget = resultData
- .getParcelable(KEY_CAPTURE_TARGET, MediaProjectionCaptureTarget.class);
-
- // Start recording of the selected target
- requestScreenCapture(captureTarget);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt
index 2048b7c0c142..137b4fdf89bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot.data.model
import android.app.ActivityTaskManager.RootTaskInfo
+import com.android.systemui.screenshot.policy.childTasksTopDown
/** Information about the tasks on a display. */
data class DisplayContentModel(
@@ -27,3 +28,5 @@ data class DisplayContentModel(
/** A list of root tasks on the display, ordered from top to bottom along the z-axis */
val rootTasks: List<RootTaskInfo>,
)
+
+fun DisplayContentModel.allTasks() = rootTasks.asSequence().flatMap { it.childTasksTopDown() }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt
index 5e2b57651de7..2a4fe3ebfb92 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt
@@ -16,15 +16,17 @@
package com.android.systemui.screenshot.policy
-import android.content.ComponentName
import android.os.UserHandle
-/** The parameters dictated by a [CapturePolicy], used to adjust alter screenshot request. */
data class CaptureParameters(
- /** How should the content be captured? */
+ /** Describes how the image should be obtained. */
val type: CaptureType,
- /** The focused or top component at the time of the screenshot. */
- val component: ComponentName?,
- /** Which user should receive the screenshot file? */
+ /** Which user to receive the image. */
val owner: UserHandle,
+ /**
+ * The task which represents the main content or focal point of the screenshot. This is the task
+ * used for retrieval of [AssistContent][android.app.assist.AssistContent] as well as
+ * [Scroll Capture][android.view.IWindowManager.requestScrollCapture].
+ */
+ val contentTask: TaskReference,
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt
index 0fb536636f1c..73ff566b7306 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt
@@ -22,7 +22,7 @@ import com.android.systemui.screenshot.data.model.DisplayContentModel
fun interface CapturePolicy {
/**
* Test the policy against the current display task state. If the policy applies, Returns a
- * [PolicyResult.Matched] containing [CaptureParameters] used to alter the request.
+ * [PolicyResult.Matched] containing [LegacyCaptureParameters] used to alter the request.
*/
suspend fun check(content: DisplayContentModel): PolicyResult
@@ -35,7 +35,7 @@ fun interface CapturePolicy {
/** Why the policy matched. */
val reason: String,
/** Details on how to modify the screen capture request. */
- val parameters: CaptureParameters,
+ val parameters: LegacyCaptureParameters,
) : PolicyResult
/** The policy rules do not match the given display content and do not apply. */
@@ -43,7 +43,7 @@ fun interface CapturePolicy {
/** The name of the policy rule which matched. */
val policy: String,
/** Why the policy did not match. */
- val reason: String
+ val reason: String,
) : PolicyResult
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
index 945520126474..34c851100d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
@@ -20,12 +20,10 @@ import android.graphics.Rect
/** What to capture */
sealed interface CaptureType {
+
/** Capture the entire screen contents. */
data class FullScreen(val displayId: Int) : CaptureType
/** Capture the contents of the task only. */
data class IsolatedTask(val taskId: Int, val taskBounds: Rect?) : CaptureType
-
- data class RootTask(val parentTaskId: Int, val taskBounds: Rect?, val childTaskIds: List<Int>) :
- CaptureType
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/LegacyCaptureParameters.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/LegacyCaptureParameters.kt
new file mode 100644
index 000000000000..4b697b201cee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/LegacyCaptureParameters.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.content.ComponentName
+import android.os.UserHandle
+
+/** The parameters dictated by a [CapturePolicy], used to adjust alter screenshot request. */
+data class LegacyCaptureParameters(
+ /** How should the content be captured? */
+ val type: CaptureType,
+ /** The focused or top component at the time of the screenshot. */
+ val component: ComponentName?,
+ /** Which user should receive the screenshot file? */
+ val owner: UserHandle,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
index e840668688a0..a84cdf40ca69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
@@ -80,6 +80,7 @@ class PolicyRequestProcessor(
Log.i(TAG, "$result")
return modify(original, result.parameters)
}
+
is NotMatched -> Log.i(TAG, "$result")
}
}
@@ -89,7 +90,45 @@ class PolicyRequestProcessor(
}
/** Produce a new [ScreenshotData] using [CaptureParameters] */
- suspend fun modify(original: ScreenshotData, updates: CaptureParameters): ScreenshotData {
+ suspend fun modify(original: ScreenshotData, params: CaptureParameters): ScreenshotData {
+ Log.d(TAG, "[modify] CaptureParameters = $params")
+ // Update and apply bitmap capture depending on the parameters.
+ when (val type = params.type) {
+ is IsolatedTask -> {
+ Log.i(TAG, "Capturing task snapshot: $params")
+
+ val taskSnapshot =
+ capture.captureTask(type.taskId) ?: error("Failed to capture task")
+
+ return original.copy(
+ type = TAKE_SCREENSHOT_PROVIDED_IMAGE,
+ bitmap = taskSnapshot,
+ userHandle = params.owner,
+ taskId = params.contentTask.taskId,
+ topComponent = params.contentTask.component,
+ originalScreenBounds = type.taskBounds,
+ )
+ }
+
+ is FullScreen -> {
+ Log.i(TAG, "Capturing screenshot: $params")
+
+ val screenshot =
+ captureDisplay(type.displayId) ?: error("Failed to capture screenshot")
+ return original.copy(
+ type = TAKE_SCREENSHOT_FULLSCREEN,
+ bitmap = screenshot,
+ userHandle = params.owner,
+ topComponent = params.contentTask.component,
+ originalScreenBounds = Rect(0, 0, screenshot.width, screenshot.height),
+ taskId = params.contentTask.taskId,
+ )
+ }
+ }
+ }
+
+ /** Produce a new [ScreenshotData] using [LegacyCaptureParameters] */
+ suspend fun modify(original: ScreenshotData, updates: LegacyCaptureParameters): ScreenshotData {
Log.d(TAG, "[modify] CaptureParameters = $updates")
// Update and apply bitmap capture depending on the parameters.
val updated =
@@ -102,14 +141,7 @@ class PolicyRequestProcessor(
type.taskId,
type.taskBounds,
)
- is CaptureType.RootTask ->
- replaceWithTaskSnapshot(
- original,
- updates.component,
- updates.owner,
- type.parentTaskId,
- type.taskBounds,
- )
+
is FullScreen ->
replaceWithScreenshot(
original,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt
index 1945c2575655..3857ba305ad0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt
@@ -31,11 +31,8 @@ import javax.inject.Inject
*
* Parameters: Capture the whole screen, owned by the private user.
*/
-class PrivateProfilePolicy
-@Inject
-constructor(
- private val profileTypes: ProfileTypeRepository,
-) : CapturePolicy {
+class PrivateProfilePolicy @Inject constructor(private val profileTypes: ProfileTypeRepository) :
+ CapturePolicy {
override suspend fun check(content: DisplayContentModel): PolicyResult {
// The systemUI notification shade isn't a private profile app, skip.
if (content.systemUiState.shadeExpanded) {
@@ -47,25 +44,23 @@ constructor(
content.rootTasks
.filter { it.isVisible }
.firstNotNullOfOrNull { root ->
- root
- .childTasksTopDown()
- .firstOrNull {
- profileTypes.getProfileType(it.userId) == ProfileType.PRIVATE
- }
- }
- ?: return NotMatched(policy = NAME, reason = NO_VISIBLE_TASKS)
+ root.childTasksTopDown().firstOrNull {
+ profileTypes.getProfileType(it.userId) == ProfileType.PRIVATE
+ }
+ } ?: return NotMatched(policy = NAME, reason = NO_VISIBLE_TASKS)
// If matched, return parameters needed to modify the request.
return Matched(
policy = NAME,
reason = PRIVATE_TASK_VISIBLE,
- CaptureParameters(
+ LegacyCaptureParameters(
type = FullScreen(content.displayId),
component = content.rootTasks.first { it.isVisible }.topActivity,
owner = UserHandle.of(childTask.userId),
- )
+ ),
)
}
+
companion object {
const val NAME = "PrivateProfile"
const val SHADE_EXPANDED = "Notification shade is expanded"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
index dd39f92643ce..c43e929295e1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
@@ -20,7 +20,7 @@ import android.app.ActivityTaskManager.RootTaskInfo
import com.android.systemui.screenshot.data.model.ChildTaskModel
/** The child tasks of A RootTaskInfo as [ChildTaskModel] in top-down (z-index ascending) order. */
-internal fun RootTaskInfo.childTasksTopDown(): Sequence<ChildTaskModel> {
+fun RootTaskInfo.childTasksTopDown(): Sequence<ChildTaskModel> {
return ((childTaskIds.size - 1) downTo 0).asSequence().map { index ->
ChildTaskModel(
childTaskIds[index],
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt
index 9967afffb6a0..5213579d5ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt
@@ -20,18 +20,16 @@ import android.app.ActivityTaskManager.RootTaskInfo
import android.app.WindowConfiguration
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.content.ComponentName
+import android.graphics.Rect
import android.os.UserHandle
import android.util.Log
import com.android.systemui.screenshot.data.model.DisplayContentModel
-import com.android.systemui.screenshot.data.model.ProfileType
import com.android.systemui.screenshot.data.model.ProfileType.PRIVATE
import com.android.systemui.screenshot.data.model.ProfileType.WORK
import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
import com.android.systemui.screenshot.policy.CaptureType.FullScreen
import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
-import com.android.systemui.screenshot.policy.CaptureType.RootTask
import javax.inject.Inject
private const val TAG = "ScreenshotPolicy"
@@ -39,7 +37,7 @@ private const val TAG = "ScreenshotPolicy"
/** Determines what to capture and which user owns the output. */
class ScreenshotPolicy @Inject constructor(private val profileTypes: ProfileTypeRepository) {
/**
- * Apply the policy to the content, resulting in [CaptureParameters].
+ * Apply the policy to the content, resulting in [LegacyCaptureParameters].
*
* @param content the content of the display
* @param defaultComponent the component associated with the screenshot by default
@@ -53,7 +51,7 @@ class ScreenshotPolicy @Inject constructor(private val profileTypes: ProfileType
val defaultFullScreen by lazy {
CaptureParameters(
type = FullScreen(displayId = content.displayId),
- component = defaultComponent,
+ contentTask = TaskReference(-1, defaultComponent, defaultOwner, Rect()),
owner = defaultOwner,
)
}
@@ -70,32 +68,47 @@ class ScreenshotPolicy @Inject constructor(private val profileTypes: ProfileType
} ?: return defaultFullScreen
Log.d(TAG, "topRootTask: $topRootTask")
- val rootTaskOwners = topRootTask.childTaskUserIds.distinct()
- // Special case: Only WORK in top root task which is full-screen or maximized freeform
+ // When:
+ // * there is one or more child task
+ // * all owned by the same user
+ // * this user is a work profile
+ // * the root task is fullscreen or freeform-maximized
+ //
+ // Then:
+ // the result will be a task snapshot instead of a full screen capture. If there is more
+ // than one child task, the root task will be snapshot to include any/all child tasks. This
+ // is intended to cover split-screen mode.
+ val rootTaskOwners = topRootTask.childTaskUserIds.distinct()
if (
rootTaskOwners.size == 1 &&
profileTypes.getProfileType(rootTaskOwners.single()) == WORK &&
(topRootTask.isFullScreen() || topRootTask.isMaximizedFreeform())
) {
+ val topChildTask = topRootTask.childTasksTopDown().first()
+
+ // If there is more than one task, capture the parent to include both.
val type =
if (topRootTask.childTaskCount() > 1) {
- RootTask(
- parentTaskId = topRootTask.taskId,
- taskBounds = topRootTask.bounds,
- childTaskIds = topRootTask.childTasksTopDown().map { it.id }.toList(),
- )
+ IsolatedTask(taskId = topRootTask.taskId, taskBounds = topRootTask.bounds)
} else {
- IsolatedTask(
- taskId = topRootTask.childTasksTopDown().first().id,
- taskBounds = topRootTask.bounds,
- )
+ // Otherwise capture the single task, and use its bounds.
+ IsolatedTask(taskId = topChildTask.id, taskBounds = topChildTask.bounds)
}
- // Capture the RootTask (and all children)
+
+ // The content task (the focus of the screenshot) must represent a single task
+ // containing an activity, so always reference the top child task here. The owner
+ // of the screenshot here is always the same as well.
return CaptureParameters(
type = type,
- component = topRootTask.topActivity,
- owner = UserHandle.of(rootTaskOwners.single()),
+ contentTask =
+ TaskReference(
+ taskId = topChildTask.id,
+ component = topRootTask.topActivity ?: defaultComponent,
+ owner = UserHandle.of(topChildTask.userId),
+ bounds = topChildTask.bounds,
+ ),
+ owner = UserHandle.of(topChildTask.userId),
)
}
@@ -105,26 +118,36 @@ class ScreenshotPolicy @Inject constructor(private val profileTypes: ProfileType
val visibleChildTasks =
content.rootTasks.filter { it.isVisible }.flatMap { it.childTasksTopDown() }
+ // Don't target a PIP window as the screenshot "content", it should only be used
+ // to determine ownership (above).
+ val contentTask =
+ content.rootTasks
+ .filter {
+ it.isVisible && it.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED
+ }
+ .flatMap { it.childTasksTopDown() }
+ .first()
+
val allVisibleProfileTypes =
visibleChildTasks
.map { it.userId }
.distinct()
.associate { profileTypes.getProfileType(it) to UserHandle.of(it) }
- // If any visible content belongs to the private profile user -> private profile
- // otherwise the personal user (including partial screen work content).
- val ownerHandle =
- allVisibleProfileTypes[PRIVATE]
- ?: allVisibleProfileTypes[ProfileType.NONE]
- ?: defaultOwner
-
- // Attribute to the component of top-most task owned by this user (or fallback to default)
- val topComponent =
- visibleChildTasks.firstOrNull { it.userId == ownerHandle.identifier }?.componentName
+ // If any task is visible and owned by a PRIVATE profile user, the screenshot is assigned
+ // to that user. Work profile has been handled above so it is not considered here. Fallback
+ // to the default user which is the primary "current" user ('aka' personal "profile").
+ val ownerHandle = allVisibleProfileTypes[PRIVATE] ?: defaultOwner
return CaptureParameters(
type = FullScreen(content.displayId),
- component = topComponent ?: topRootTask.topActivity ?: defaultComponent,
+ contentTask =
+ TaskReference(
+ taskId = contentTask.id,
+ component = contentTask.componentName,
+ owner = UserHandle.of(contentTask.userId),
+ bounds = contentTask.bounds,
+ ),
owner = ownerHandle,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/TaskReference.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/TaskReference.kt
new file mode 100644
index 000000000000..04f5b1ef2885
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/TaskReference.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.content.ComponentName
+import android.graphics.Rect
+import android.os.UserHandle
+
+data class TaskReference(
+ /** The id of the task. */
+ val taskId: Int,
+ /** The component name of the task. */
+ val component: ComponentName?,
+ /** The owner of the task. */
+ val owner: UserHandle,
+ /** The bounds of the task. */
+ val bounds: Rect,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
index cf90c0a58e94..109c1cbf7e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
@@ -68,7 +68,7 @@ constructor(private val profileTypes: ProfileTypeRepository, private val context
return PolicyResult.Matched(
policy = NAME,
reason = WORK_TASK_IS_TOP,
- CaptureParameters(
+ LegacyCaptureParameters(
type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds),
component = childTask.componentName ?: rootTask.topActivity,
owner = UserHandle.of(childTask.userId),
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
index 2ef27a8df117..60ed2de5c532 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt
@@ -17,7 +17,7 @@
package com.android.systemui.settings
import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.hardware.display.DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS
import android.os.Handler
import android.view.Display
import androidx.annotation.GuardedBy
@@ -32,7 +32,7 @@ import java.util.concurrent.Executor
class DisplayTrackerImpl
internal constructor(
val displayManager: DisplayManager,
- @Background val backgroundHandler: Handler
+ @Background val backgroundHandler: Handler,
) : DisplayTracker {
override val defaultDisplayId: Int = Display.DEFAULT_DISPLAY
override val allDisplays: Array<Display>
@@ -47,27 +47,21 @@ internal constructor(
val displayChangedListener: DisplayManager.DisplayListener =
object : DisplayManager.DisplayListener {
override fun onDisplayAdded(displayId: Int) {
- traceSection(
- "DisplayTrackerImpl.displayChangedDisplayListener#onDisplayAdded",
- ) {
+ traceSection("DisplayTrackerImpl.displayChangedDisplayListener#onDisplayAdded") {
val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
onDisplayAdded(displayId, list)
}
}
override fun onDisplayRemoved(displayId: Int) {
- traceSection(
- "DisplayTrackerImpl.displayChangedDisplayListener#onDisplayRemoved",
- ) {
+ traceSection("DisplayTrackerImpl.displayChangedDisplayListener#onDisplayRemoved") {
val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
onDisplayRemoved(displayId, list)
}
}
override fun onDisplayChanged(displayId: Int) {
- traceSection(
- "DisplayTrackerImpl.displayChangedDisplayListener#onDisplayChanged",
- ) {
+ traceSection("DisplayTrackerImpl.displayChangedDisplayListener#onDisplayChanged") {
val list = synchronized(displayCallbacks) { displayCallbacks.toList() }
onDisplayChanged(displayId, list)
}
@@ -83,7 +77,7 @@ internal constructor(
override fun onDisplayChanged(displayId: Int) {
traceSection(
- "DisplayTrackerImpl.displayBrightnessChangedDisplayListener#onDisplayChanged",
+ "DisplayTrackerImpl.displayBrightnessChangedDisplayListener#onDisplayChanged"
) {
val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() }
onDisplayChanged(displayId, list)
@@ -102,14 +96,15 @@ internal constructor(
override fun addBrightnessChangeCallback(
callback: DisplayTracker.Callback,
- executor: Executor
+ executor: Executor,
) {
synchronized(brightnessCallbacks) {
if (brightnessCallbacks.isEmpty()) {
displayManager.registerDisplayListener(
displayBrightnessChangedListener,
backgroundHandler,
- EVENT_FLAG_DISPLAY_BRIGHTNESS
+ /* eventFlags */ 0,
+ PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS,
)
}
brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor))
@@ -159,7 +154,7 @@ internal constructor(
private inline fun notifySubscribers(
crossinline action: DisplayTracker.Callback.() -> Unit,
- list: List<DisplayTrackerDataItem>
+ list: List<DisplayTrackerDataItem>,
) {
list.forEach {
if (it.callback.get() != null) {
@@ -170,7 +165,7 @@ internal constructor(
private data class DisplayTrackerDataItem(
val callback: WeakReference<DisplayTracker.Callback>,
- val executor: Executor
+ val executor: Executor,
) {
fun sameOrEmpty(other: DisplayTracker.Callback): Boolean {
return callback.get()?.equals(other) ?: true
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt
deleted file mode 100644
index 6199a83f9075..000000000000
--- a/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.settings
-
-import android.content.ContentResolver
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
-import com.android.systemui.shared.settings.data.repository.SecureSettingsRepositoryImpl
-import dagger.Module
-import dagger.Provides
-import kotlinx.coroutines.CoroutineDispatcher
-
-@Module
-object SecureSettingsRepositoryModule {
- @JvmStatic
- @Provides
- @SysUISingleton
- fun provideSecureSettingsRepository(
- contentResolver: ContentResolver,
- @Background backgroundDispatcher: CoroutineDispatcher,
- ): SecureSettingsRepository =
- SecureSettingsRepositoryImpl(contentResolver, backgroundDispatcher)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt
deleted file mode 100644
index 02ce74a94de6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.settings
-
-import android.content.ContentResolver
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
-import com.android.systemui.shared.settings.data.repository.SystemSettingsRepositoryImpl
-import dagger.Module
-import dagger.Provides
-import kotlinx.coroutines.CoroutineDispatcher
-
-@Module
-object SystemSettingsRepositoryModule {
- @JvmStatic
- @Provides
- @SysUISingleton
- fun provideSystemSettingsRepository(
- contentResolver: ContentResolver,
- @Background backgroundDispatcher: CoroutineDispatcher,
- ): SystemSettingsRepository =
- SystemSettingsRepositoryImpl(contentResolver, backgroundDispatcher)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/settings/UserSettingsRepositoryModule.kt
new file mode 100644
index 000000000000..3d7b2ea2bc6e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserSettingsRepositoryModule.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.ContentResolver
+import com.android.systemui.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepositoryImpl
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepositoryImpl
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SystemSettings
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
+import com.android.systemui.util.settings.repository.UserAwareSystemSettingsRepository
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
+
+@Module
+object UserSettingsRepositoryModule {
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ fun provideSecureSettingsRepository(
+ secureSettings: Lazy<SecureSettings>,
+ userRepository: Lazy<UserRepository>,
+ contentResolver: Lazy<ContentResolver>,
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ @Background backgroundContext: CoroutineContext,
+ ): SecureSettingsRepository {
+ return if (Flags.userAwareSettingsRepositories()) {
+ UserAwareSecureSettingsRepository(
+ secureSettings.get(),
+ userRepository.get(),
+ backgroundDispatcher,
+ backgroundContext,
+ )
+ } else {
+ SecureSettingsRepositoryImpl(contentResolver.get(), backgroundDispatcher)
+ }
+ }
+
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ fun provideSystemSettingsRepository(
+ systemSettings: Lazy<SystemSettings>,
+ userRepository: Lazy<UserRepository>,
+ contentResolver: Lazy<ContentResolver>,
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ @Background backgroundContext: CoroutineContext,
+ ): SystemSettingsRepository {
+ return if (Flags.userAwareSettingsRepositories()) {
+ UserAwareSystemSettingsRepository(
+ systemSettings.get(),
+ userRepository.get(),
+ backgroundDispatcher,
+ backgroundContext,
+ )
+ } else {
+ SystemSettingsRepositoryImpl(contentResolver.get(), backgroundDispatcher)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 649f8db89bc0..90d27f4b33e9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -44,6 +44,7 @@ import android.util.MathUtils;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -56,6 +57,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.core.LogMessage;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
@@ -111,6 +113,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
private boolean mControlValueInitialized;
private float mBrightnessMin = PowerManager.BRIGHTNESS_MIN;
private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX;
+ private boolean mIsBrightnessOverriddenByWindow = false;
private ValueAnimator mSliderAnimator;
@@ -246,12 +249,14 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
@Override
public void run() {
final boolean inVrMode = mIsVrModeEnabled;
- final BrightnessInfo info = mContext.getDisplay().getBrightnessInfo();
+ final BrightnessInfo info = getBrightnessInfo();
if (info == null) {
return;
}
mBrightnessMax = info.brightnessMaximum;
mBrightnessMin = info.brightnessMinimum;
+ mIsBrightnessOverriddenByWindow = info.isBrightnessOverrideByWindow;
+
// Value is passed as intbits, since this is what the message takes.
final int valueAsIntBits = Float.floatToIntBits(info.brightness);
mMainHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
@@ -353,7 +358,19 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
public void onChanged(boolean tracking, int value, boolean stopTracking) {
boolean starting = !mTrackingTouch && tracking;
mTrackingTouch = tracking;
- if (mExternalChange) return;
+ if (starting) {
+ if (Flags.showToastWhenAppControlBrightness()) {
+ // Showing the warning toast if the current running app window has
+ // controlled the brightness value.
+ if (mIsBrightnessOverriddenByWindow) {
+ mControl.showToast(R.string.quick_settings_brightness_unable_adjust_msg);
+ }
+ }
+ }
+ if (mExternalChange
+ || (Flags.showToastWhenAppControlBrightness() && mIsBrightnessOverriddenByWindow)) {
+ return;
+ }
if (mSliderAnimator != null) {
mSliderAnimator.cancel();
@@ -424,6 +441,11 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig
mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
}
+ @VisibleForTesting
+ BrightnessInfo getBrightnessInfo() {
+ return mContext.getDisplay().getBrightnessInfo();
+ }
+
private void updateVrMode(boolean isEnabled) {
if (mIsVrModeEnabled != isEnabled) {
mIsVrModeEnabled = isEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 2f7df21893b7..3a90d2b9df7b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -16,6 +16,7 @@
package com.android.systemui.settings.brightness;
+import android.annotation.StringRes;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
@@ -36,6 +37,7 @@ import com.android.systemui.haptics.slider.HapticSliderViewBinder;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.res.R;
+import com.android.systemui.settings.brightness.ui.BrightnessWarningToast;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -68,6 +70,8 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
private final HapticSliderPlugin mBrightnessSliderHapticPlugin;
private final ActivityStarter mActivityStarter;
+ private final BrightnessWarningToast mBrightnessWarningToast;
+
private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -90,12 +94,14 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
FalsingManager falsingManager,
UiEventLogger uiEventLogger,
HapticSliderPlugin brightnessSliderHapticPlugin,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ BrightnessWarningToast brightnessWarningToast) {
super(brightnessSliderView);
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mBrightnessSliderHapticPlugin = brightnessSliderHapticPlugin;
mActivityStarter = activityStarter;
+ mBrightnessWarningToast = brightnessWarningToast;
}
/**
@@ -225,6 +231,15 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
}
@Override
+ public void showToast(@StringRes int resId) {
+ if (mBrightnessWarningToast.isToastActive()) {
+ return;
+ }
+ mBrightnessWarningToast.show(mView.getContext(),
+ R.string.quick_settings_brightness_unable_adjust_msg);
+ }
+
+ @Override
public boolean isVisible() {
// this should be called rarely - once or twice per slider's value change, but not for
// every value change when user slides finger - only the final one.
@@ -286,6 +301,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
private final SystemClock mSystemClock;
private final ActivityStarter mActivityStarter;
private final MSDLPlayer mMSDLPlayer;
+ private final BrightnessWarningToast mBrightnessWarningToast;
@Inject
public Factory(
@@ -294,7 +310,8 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
VibratorHelper vibratorHelper,
MSDLPlayer msdlPlayer,
SystemClock clock,
- ActivityStarter activityStarter
+ ActivityStarter activityStarter,
+ BrightnessWarningToast brightnessWarningToast
) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
@@ -302,6 +319,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
mSystemClock = clock;
mActivityStarter = activityStarter;
mMSDLPlayer = msdlPlayer;
+ mBrightnessWarningToast = brightnessWarningToast;
}
/**
@@ -323,8 +341,8 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
mSystemClock,
new HapticSlider.SeekBar(root.requireViewById(R.id.slider)));
HapticSliderViewBinder.bind(viewRoot, plugin);
- return new BrightnessSliderController(
- root, mFalsingManager, mUiEventLogger, plugin, mActivityStarter);
+ return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger, plugin,
+ mActivityStarter, mBrightnessWarningToast);
}
/** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
index 24bc67047a47..ed69d35e6053 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
@@ -16,6 +16,7 @@
package com.android.systemui.settings.brightness;
+import android.annotation.StringRes;
import android.view.MotionEvent;
import com.android.settingslib.RestrictedLockUtils;
@@ -37,5 +38,6 @@ public interface ToggleSlider {
void showView();
void hideView();
+ void showToast(@StringRes int resId);
boolean isVisible();
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
new file mode 100644
index 000000000000..dfbdaa62ec44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/BrightnessWarningToast.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.settings.brightness.ui
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.annotation.StringRes
+import android.content.Context
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.toast.ToastFactory
+import javax.inject.Inject
+
+@SysUISingleton
+class BrightnessWarningToast
+@Inject
+constructor(
+ private val toastFactory: ToastFactory,
+ private val windowManager: WindowManager,
+) {
+ private var toastView: View? = null
+
+ fun show(viewContext: Context, @StringRes resId: Int) {
+ val res = viewContext.resources
+ // Show the brightness warning toast with passing the toast inflation required context,
+ // userId and resId from SystemUI package.
+ val systemUIToast = toastFactory.createToast(
+ viewContext,
+ res.getString(resId), viewContext.packageName, viewContext.getUserId(),
+ res.configuration.orientation
+ )
+ if (systemUIToast == null) {
+ return
+ }
+
+ toastView = systemUIToast.view
+
+ val params = WindowManager.LayoutParams()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ params.width = WindowManager.LayoutParams.WRAP_CONTENT
+ params.format = PixelFormat.TRANSLUCENT
+ params.title = "Brightness warning toast"
+ params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
+ params.flags = (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+ params.y = systemUIToast.yOffset
+
+ val absGravity = Gravity.getAbsoluteGravity(
+ systemUIToast.gravity,
+ res.configuration.layoutDirection
+ )
+ params.gravity = absGravity
+ if ((absGravity and Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT
+ }
+ if ((absGravity and Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT
+ }
+
+ windowManager.addView(toastView, params)
+
+ val inAnimator = systemUIToast.inAnimation
+ inAnimator?.start()
+
+ toastView!!.postDelayed({
+ val outAnimator = systemUIToast.outAnimation
+ if (outAnimator != null) {
+ outAnimator.start()
+ outAnimator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animator: Animator) {
+ windowManager.removeViewImmediate(toastView)
+ toastView = null
+ }
+ })
+ }
+ }, TOAST_DURATION_MS)
+ }
+
+ fun isToastActive(): Boolean {
+ return toastView != null && toastView!!.isAttachedToWindow
+ }
+
+ companion object {
+ private const val TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f
+ private const val TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f
+ private const val TOAST_DURATION_MS: Long = 3000
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index 6e63446d88d8..1776a2c4a439 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -18,14 +18,14 @@ package com.android.systemui.shade
import android.content.Context
import android.view.ViewGroup
-import com.android.systemui.res.R
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState.SHADE
-import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
+import com.android.systemui.res.R
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import javax.inject.Inject
@@ -39,8 +39,10 @@ constructor(
progressProvider: NaturalRotationUnfoldProgressProvider,
) {
- private val filterShade: () -> Boolean = { statusBarStateController.getState() == SHADE ||
- statusBarStateController.getState() == SHADE_LOCKED }
+ private val filterShade: () -> Boolean = {
+ statusBarStateController.getState() == SHADE ||
+ statusBarStateController.getState() == SHADE_LOCKED
+ }
private val translateAnimator by lazy {
UnfoldConstantTranslateAnimator(
@@ -48,21 +50,23 @@ constructor(
setOf(
ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade),
ViewIdToTranslate(R.id.qs_footer_actions, START, filterShade),
- ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)),
- progressProvider = progressProvider)
+ ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade),
+ ),
+ progressProvider = progressProvider,
+ )
}
private val translateAnimatorStatusBar by lazy {
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
- setOf(
- ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade),
- ViewIdToTranslate(R.id.privacy_container, END, filterShade),
- ViewIdToTranslate(R.id.carrier_group, END, filterShade),
- ViewIdToTranslate(R.id.clock, START, filterShade),
- ViewIdToTranslate(R.id.date, START, filterShade)
- ),
- progressProvider = progressProvider
+ setOf(
+ ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade),
+ ViewIdToTranslate(R.id.privacy_container, END, filterShade),
+ ViewIdToTranslate(R.id.carrier_group, END, filterShade),
+ ViewIdToTranslate(R.id.clock, START, filterShade),
+ ViewIdToTranslate(R.id.date, START, filterShade),
+ ),
+ progressProvider = progressProvider,
)
}
@@ -73,10 +77,7 @@ constructor(
val splitShadeStatusBarViewGroup: ViewGroup? =
root.findViewById(R.id.split_shade_status_bar)
if (splitShadeStatusBarViewGroup != null) {
- translateAnimatorStatusBar.init(
- splitShadeStatusBarViewGroup,
- translationMax
- )
+ translateAnimatorStatusBar.init(splitShadeStatusBarViewGroup, translationMax)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 0e82bf82fdf9..c15c8f946855 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2053,6 +2053,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
if (mQsController.getExpanded()) {
mQsController.flingQs(0, FLING_COLLAPSE);
+ } else if (mBarState == KEYGUARD) {
+ mLockscreenShadeTransitionController.goToLockedShade(
+ /* expandedView= */null, /* needsQSAnimation= */false);
} else {
expand(true /* animate */);
}
@@ -3109,7 +3112,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
if (isTracking()) {
onTrackingStopped(true);
}
- if (isExpanded() && !mQsController.getExpanded()) {
+ if (isExpanded() && mBarState != KEYGUARD && !mQsController.getExpanded()) {
mShadeLog.d("Status Bar was long pressed. Expanding to QS.");
expandToQs();
} else {
@@ -5091,13 +5094,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
boolean handled = mHeadsUpTouchHelper.onTouchEvent(event);
- if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && mQsController.handleTouch(
- event, isFullyCollapsed(), isShadeOrQsHeightAnimationRunning())) {
- if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
- mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event");
- }
- return true;
- }
// This touch session has already resulted in shade expansion. Ignore everything else.
if (ShadeExpandsOnStatusBarLongPress.isEnabled()
&& event.getActionMasked() != MotionEvent.ACTION_DOWN
@@ -5105,6 +5101,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mShadeLog.d("Touch has same down time as Status Bar long press. Ignoring.");
return false;
}
+ if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && mQsController.handleTouch(
+ event, isFullyCollapsed(), isShadeOrQsHeightAnimationRunning())) {
+ if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
+ mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event");
+ }
+ return true;
+ }
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
handled = true;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 24dba59a1d54..4d77e3ecea7b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -17,8 +17,6 @@
package com.android.systemui.shade;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -27,16 +25,13 @@ import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.view.Display;
-import android.view.Gravity;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.View;
@@ -271,33 +266,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
// Now that the notification shade encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
- mLp = new LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- LayoutParams.TYPE_NOTIFICATION_SHADE,
- LayoutParams.FLAG_NOT_FOCUSABLE
- | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | LayoutParams.FLAG_SPLIT_TOUCH
- | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- PixelFormat.TRANSLUCENT);
- mLp.token = new Binder();
- mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(0 /* types */);
- mLp.setTitle("NotificationShade");
- mLp.packageName = mContext.getPackageName();
- mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;
-
- if (SceneContainerFlag.isEnabled()) {
- // This prevents the appearance and disappearance of the software keyboard (also known
- // as the "IME") from scrolling/panning the window to make room for the keyboard.
- //
- // The scene container logic does its own adjustment and animation when the IME appears
- // or disappears.
- mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- }
-
+ mLp = ShadeWindowLayoutParams.INSTANCE.create(mContext);
mWindowManager.addView(mWindowRootView, mLp);
// We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index be2bf822e6b3..f2c39063c867 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -88,6 +88,7 @@ import java.util.Optional;
import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Controller for {@link NotificationShadeWindowView}.
@@ -193,7 +194,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor,
BouncerViewBinder bouncerViewBinder,
- @ShadeDisplayAware ConfigurationForwarder configurationForwarder,
+ @ShadeDisplayAware Provider<ConfigurationForwarder> configurationForwarder,
BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -257,7 +258,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
if (ShadeWindowGoesAround.isEnabled()) {
- mView.setConfigurationForwarder(configurationForwarder);
+ mView.setConfigurationForwarder(configurationForwarder.get());
}
dumpManager.registerDumpable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index 51f1f81ab1f0..e15830eb22eb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -20,15 +20,26 @@ import android.content.Context
import android.content.res.Resources
import android.view.LayoutInflater
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.common.ui.ConfigurationStateImpl
import com.android.systemui.common.ui.GlobalConfig
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.ShadePositionRepository
+import com.android.systemui.shade.data.repository.ShadePositionRepositoryImpl
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.phone.ConfigurationForwarder
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
/**
* Module responsible for managing display-specific components and resources for the notification
@@ -79,12 +90,12 @@ object ShadeDisplayAwareModule {
fun provideShadeWindowConfigurationController(
@ShadeDisplayAware shadeContext: Context,
factory: ConfigurationControllerImpl.Factory,
- @GlobalConfig globalConfigConfigController: ConfigurationController,
+ @GlobalConfig globalConfigController: ConfigurationController,
): ConfigurationController {
return if (ShadeWindowGoesAround.isEnabled) {
factory.create(shadeContext)
} else {
- globalConfigConfigController
+ globalConfigController
}
}
@@ -92,13 +103,75 @@ object ShadeDisplayAwareModule {
@ShadeDisplayAware
@SysUISingleton
fun provideShadeWindowConfigurationForwarder(
- @ShadeDisplayAware shadeConfigurationController: ConfigurationController,
- @GlobalConfig globalConfigController: ConfigurationController,
+ @ShadeDisplayAware shadeConfigurationController: ConfigurationController
): ConfigurationForwarder {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ return shadeConfigurationController
+ }
+
+ @SysUISingleton
+ @Provides
+ @ShadeDisplayAware
+ fun provideShadeDisplayAwareConfigurationState(
+ factory: ConfigurationStateImpl.Factory,
+ @ShadeDisplayAware configurationController: ConfigurationController,
+ @ShadeDisplayAware context: Context,
+ @GlobalConfig configurationState: ConfigurationState,
+ ): ConfigurationState {
return if (ShadeWindowGoesAround.isEnabled) {
- shadeConfigurationController
+ factory.create(context, configurationController)
} else {
- globalConfigController
+ configurationState
+ }
+ }
+
+ @SysUISingleton
+ @Provides
+ @ShadeDisplayAware
+ fun provideShadeDisplayAwareConfigurationRepository(
+ factory: ConfigurationRepositoryImpl.Factory,
+ @ShadeDisplayAware configurationController: ConfigurationController,
+ @ShadeDisplayAware context: Context,
+ @GlobalConfig globalConfigurationRepository: ConfigurationRepository,
+ ): ConfigurationRepository {
+ return if (ShadeWindowGoesAround.isEnabled) {
+ factory.create(context, configurationController)
+ } else {
+ globalConfigurationRepository
+ }
+ }
+
+ @SysUISingleton
+ @Provides
+ @ShadeDisplayAware
+ fun provideShadeAwareConfigurationInteractor(
+ @ShadeDisplayAware configurationRepository: ConfigurationRepository,
+ @GlobalConfig configurationInteractor: ConfigurationInteractor,
+ ): ConfigurationInteractor {
+ return if (ShadeWindowGoesAround.isEnabled) {
+ ConfigurationInteractorImpl(configurationRepository)
+ } else {
+ configurationInteractor
+ }
+ }
+
+ @SysUISingleton
+ @Provides
+ fun provideShadePositionRepository(impl: ShadePositionRepositoryImpl): ShadePositionRepository {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ return impl
+ }
+
+ @Provides
+ @IntoMap
+ @ClassKey(ShadePositionRepositoryImpl::class)
+ fun provideShadePositionRepositoryAsCoreStartable(
+ impl: ShadePositionRepositoryImpl
+ ): CoreStartable {
+ return if (ShadeWindowGoesAround.isEnabled) {
+ impl
+ } else {
+ CoreStartable.NOP
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 72a465030bf5..2348a110eb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -49,10 +49,7 @@ import dagger.Provides
import javax.inject.Provider
/** Module for classes related to the notification shade. */
-@Module(
- includes =
- [StartShadeModule::class, ShadeViewProviderModule::class]
-)
+@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class])
abstract class ShadeModule {
companion object {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
new file mode 100644
index 000000000000..802fc0edb533
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.view.Display
+import com.android.systemui.shade.data.repository.ShadePositionRepository
+import com.android.systemui.statusbar.commandline.Command
+import java.io.PrintWriter
+
+class ShadePrimaryDisplayCommand(private val positionRepository: ShadePositionRepository) :
+ Command {
+
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ if (args[0].lowercase() == "reset") {
+ positionRepository.resetDisplayId()
+ pw.println("Reset shade primary display id to ${Display.DEFAULT_DISPLAY}")
+ return
+ }
+
+ val displayId: Int =
+ try {
+ args[0].toInt()
+ } catch (e: NumberFormatException) {
+ pw.println("Error: task id should be an integer")
+ return
+ }
+
+ if (displayId < 0) {
+ pw.println("Error: display id should be positive integer")
+ }
+
+ positionRepository.setDisplayId(displayId)
+ pw.println("New shade primary display id is $displayId")
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("shade_display_override <displayId> ")
+ pw.println("Set the display which is holding the shade.")
+ pw.println("shade_display_override reset ")
+ pw.println("Reset the display which is holding the shade.")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt
new file mode 100644
index 000000000000..6bb50f99b5e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Binder
+import android.view.Gravity
+import android.view.ViewGroup
+import android.view.WindowManager.LayoutParams
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+
+object ShadeWindowLayoutParams {
+ /**
+ * Creates [LayoutParams] for the shade window.
+ *
+ * This is extracted to a single place as those layout params will be used by several places:
+ * - When sysui starts, and the shade is added the first time
+ * - When the shade moves to a different window (e.g. while an external display is connected)
+ */
+ fun create(context: Context): LayoutParams {
+ return LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_NOTIFICATION_SHADE,
+ LayoutParams.FLAG_NOT_FOCUSABLE or
+ LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING or
+ LayoutParams.FLAG_SPLIT_TOUCH or
+ LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+ LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ // Now that the notification shade encompasses the sliding panel and its
+ // translucent backdrop, the entire thing is made TRANSLUCENT and is
+ // hardware-accelerated.
+ PixelFormat.TRANSLUCENT,
+ )
+ .apply {
+ token = Binder()
+ gravity = Gravity.TOP
+ fitInsetsTypes = 0
+ title = "NotificationShade"
+ packageName = context.packageName
+ layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags = privateFlags or LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE
+ if (SceneContainerFlag.isEnabled) {
+ // This prevents the appearance and disappearance of the software keyboard (also
+ // known as the "IME") from scrolling/panning the window to make room for the
+ // keyboard.
+ //
+ // The scene container logic does its own adjustment and animation when the IME
+ // appears or disappears.
+ softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt
index 6fb3ca5f86d2..ae36e81c7b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/StatusBarLongPressGestureDetector.kt
@@ -25,7 +25,7 @@ import javax.inject.Inject
/** Accepts touch events, detects long press, and calls ShadeViewController#onStatusBarLongPress. */
@SysUISingleton
-class LongPressGestureDetector
+class StatusBarLongPressGestureDetector
@Inject
constructor(context: Context, val shadeViewController: ShadeViewController) {
val gestureDetector =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt
new file mode 100644
index 000000000000..37210b90ee78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.data.repository
+
+import android.view.Display
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeShadePositionRepository : ShadePositionRepository {
+ private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+
+ override fun setDisplayId(displayId: Int) {
+ _displayId.value = displayId
+ }
+
+ override val displayId: StateFlow<Int>
+ get() = _displayId
+
+ override fun resetDisplayId() {
+ _displayId.value = Display.DEFAULT_DISPLAY
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt
new file mode 100644
index 000000000000..24c067ae2371
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.data.repository
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.ShadePrimaryDisplayCommand
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+interface ShadePositionRepository {
+ /** ID of the display which currently hosts the shade */
+ val displayId: StateFlow<Int>
+
+ /**
+ * Updates the value of the shade display id stored, emitting to the new display id to every
+ * component dependent on the shade display id
+ */
+ fun setDisplayId(displayId: Int)
+
+ /** Resets value of shade primary display to the default display */
+ fun resetDisplayId()
+}
+
+/** Source of truth for the display currently holding the shade. */
+@SysUISingleton
+class ShadePositionRepositoryImpl
+@Inject
+constructor(private val commandRegistry: CommandRegistry) : ShadePositionRepository, CoreStartable {
+ private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+
+ override val displayId: StateFlow<Int>
+ get() = _displayId
+
+ override fun setDisplayId(displayId: Int) {
+ _displayId.value = displayId
+ }
+
+ override fun resetDisplayId() {
+ _displayId.value = Display.DEFAULT_DISPLAY
+ }
+
+ override fun start() {
+ commandRegistry.registerCommand("shade_display_override") {
+ ShadePrimaryDisplayCommand(this)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 5629938deeb0..ef62d2da9589 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -15,9 +15,7 @@
*/
package com.android.systemui.shade.data.repository
-import android.content.Context
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -182,8 +180,7 @@ interface ShadeRepository {
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl @Inject constructor() :
- ShadeRepository {
+class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
@Deprecated("Use ShadeInteractor.qsExpansion instead")
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
index e5d08a0ac977..44f29119a1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade.domain.startable
import android.content.Context
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
@@ -42,7 +43,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class ShadeStartable
@@ -51,7 +51,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
@ShadeDisplayAware private val context: Context,
@ShadeTouchLog private val touchLog: LogBuffer,
- private val configurationRepository: ConfigurationRepository,
+ @ShadeDisplayAware private val configurationRepository: ConfigurationRepository,
private val shadeRepository: ShadeRepository,
private val splitShadeStateController: SplitShadeStateController,
private val scrimShadeTransitionController: ScrimShadeTransitionController,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
index 6f492cfaa6c3..c23ff5302b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt
@@ -32,7 +32,7 @@ object ShadeWindowGoesAround {
/** Is the refactor enabled */
@JvmStatic
- inline val isEnabled
+ inline val isEnabled: Boolean
get() = Flags.shadeWindowGoesAround()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
index e5f684635ac7..b0777c9e20b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
@@ -18,7 +18,6 @@ package com.android.systemui.shade.ui.viewmodel
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.Overlays
@@ -35,7 +34,7 @@ fun singleShadeActions(
return arrayOf(
// Swiping down, not from the edge, always goes to shade.
Swipe.Down to shadeUserActionResult,
- swipeDown(pointerCount = 2) to shadeUserActionResult,
+ Swipe.Down(pointerCount = 2) to shadeUserActionResult,
// Swiping down from the top edge.
swipeDownFromTop(pointerCount = 1) to
@@ -54,7 +53,7 @@ fun splitShadeActions(): Array<Pair<UserAction, UserActionResult>> {
return arrayOf(
// Swiping down, not from the edge, always goes to shade.
Swipe.Down to shadeUserActionResult,
- swipeDown(pointerCount = 2) to shadeUserActionResult,
+ Swipe.Down(pointerCount = 2) to shadeUserActionResult,
// Swiping down from the top edge goes to QS.
swipeDownFromTop(pointerCount = 1) to shadeUserActionResult,
swipeDownFromTop(pointerCount = 2) to shadeUserActionResult,
@@ -69,15 +68,10 @@ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
UserActionResult.ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true)
return arrayOf(
Swipe.Down to notifShadeUserActionResult,
- Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to
- qsShadeuserActionResult,
+ Swipe.Down(fromSource = SceneContainerEdge.TopRight) to qsShadeuserActionResult,
)
}
private fun swipeDownFromTop(pointerCount: Int): Swipe {
- return Swipe(SwipeDirection.Down, fromSource = Edge.Top, pointerCount = pointerCount)
-}
-
-private fun swipeDown(pointerCount: Int): Swipe {
- return Swipe(SwipeDirection.Down, pointerCount = pointerCount)
+ return Swipe.Down(fromSource = Edge.Top, pointerCount = pointerCount)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
index 4bdd36773655..7d6b1a3126dc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.ui.viewmodel
import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -58,7 +57,7 @@ constructor(
buildMap<UserAction, UserActionResult> {
if (!isCustomizerShowing) {
set(
- Swipe(SwipeDirection.Up),
+ Swipe.Up,
UserActionResult(
backScene,
ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
@@ -69,7 +68,7 @@ constructor(
// TODO(b/330200163) Add an else to be able to collapse the shade while
// customizing
if (shadeMode is ShadeMode.Single) {
- set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
+ set(Swipe.Down, UserActionResult(Scenes.QuickSettings))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 520cbf9d80d9..a5595edcbb95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -116,6 +116,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.UserLogoutInteractor;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.wakelock.SettableWakeLock;
@@ -162,6 +163,7 @@ public class KeyguardIndicationController {
private final KeyguardLogger mKeyguardLogger;
private final UserTracker mUserTracker;
private final BouncerMessageInteractor mBouncerMessageInteractor;
+
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -187,6 +189,7 @@ public class KeyguardIndicationController {
private final BiometricMessageInteractor mBiometricMessageInteractor;
private DeviceEntryFingerprintAuthInteractor mDeviceEntryFingerprintAuthInteractor;
private DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
+ private final UserLogoutInteractor mUserLogoutInteractor;
private String mPersistentUnlockMessage;
private String mAlignmentIndication;
private boolean mForceIsDismissible;
@@ -237,6 +240,13 @@ public class KeyguardIndicationController {
showTrustAgentErrorMessage(mTrustAgentErrorMessage);
}
};
+ @VisibleForTesting
+ final Consumer<Boolean> mIsLogoutEnabledCallback =
+ (Boolean isLogoutEnabled) -> {
+ if (mVisible) {
+ updateDeviceEntryIndication(false);
+ }
+ };
private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
@@ -299,7 +309,8 @@ public class KeyguardIndicationController {
KeyguardInteractor keyguardInteractor,
BiometricMessageInteractor biometricMessageInteractor,
DeviceEntryFingerprintAuthInteractor deviceEntryFingerprintAuthInteractor,
- DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor
+ DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
+ UserLogoutInteractor userLogoutInteractor
) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
@@ -331,6 +342,8 @@ public class KeyguardIndicationController {
mBiometricMessageInteractor = biometricMessageInteractor;
mDeviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor;
mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
+ mUserLogoutInteractor = userLogoutInteractor;
+
mFaceAcquiredMessageDeferral = faceHelpMessageDeferral.create();
@@ -418,6 +431,9 @@ public class KeyguardIndicationController {
mCoExAcquisitionMsgIdsToShowCallback);
collectFlow(mIndicationArea, mDeviceEntryFingerprintAuthInteractor.isEngaged(),
mIsFingerprintEngagedCallback);
+ collectFlow(mIndicationArea,
+ mUserLogoutInteractor.isLogoutEnabled(),
+ mIsLogoutEnabledCallback);
}
/**
@@ -619,10 +635,11 @@ public class KeyguardIndicationController {
}
private void updateLockScreenUserLockedMsg(int userId) {
- boolean userUnlocked = mKeyguardUpdateMonitor.isUserUnlocked(userId);
+ boolean userStorageUnlocked = mKeyguardUpdateMonitor.isUserUnlocked(userId);
boolean encryptedOrLockdown = mKeyguardUpdateMonitor.isEncryptedOrLockdown(userId);
- mKeyguardLogger.logUpdateLockScreenUserLockedMsg(userId, userUnlocked, encryptedOrLockdown);
- if (!userUnlocked || encryptedOrLockdown) {
+ mKeyguardLogger.logUpdateLockScreenUserLockedMsg(userId, userStorageUnlocked,
+ encryptedOrLockdown);
+ if (!userStorageUnlocked || encryptedOrLockdown) {
mRotateTextViewController.updateIndication(
INDICATION_TYPE_USER_LOCKED,
new KeyguardIndication.Builder()
@@ -743,9 +760,7 @@ public class KeyguardIndicationController {
}
private void updateLockScreenLogoutView() {
- final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled()
- && getCurrentUser() != UserHandle.USER_SYSTEM;
- if (shouldShowLogout) {
+ if (mUserLogoutInteractor.isLogoutEnabled().getValue()) {
mRotateTextViewController.updateIndication(
INDICATION_TYPE_LOGOUT,
new KeyguardIndication.Builder()
@@ -759,7 +774,7 @@ public class KeyguardIndicationController {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) {
return;
}
- mDevicePolicyManager.logoutUser();
+ mUserLogoutInteractor.logOut();
})
.build(),
false);
@@ -1514,13 +1529,6 @@ public class KeyguardIndicationController {
}
@Override
- public void onLogoutEnabledChanged() {
- if (mVisible) {
- updateDeviceEntryIndication(false);
- }
- }
-
- @Override
public void onRequireUnlockForNfc() {
showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc));
hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 3a24ec9408ad..c1b8d9d123b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -383,34 +383,20 @@ public class NotificationGroupingUtil {
}
protected boolean hasSameIcon(Object parentData, Object childData) {
- Icon parentIcon = getIcon((Notification) parentData);
- Icon childIcon = getIcon((Notification) childData);
+ Icon parentIcon = ((Notification) parentData).getSmallIcon();
+ Icon childIcon = ((Notification) childData).getSmallIcon();
return parentIcon.sameAs(childIcon);
}
- private static Icon getIcon(Notification notification) {
- if (notification.shouldUseAppIcon()) {
- return notification.getAppIcon();
- }
- return notification.getSmallIcon();
- }
-
/**
* @return whether two ImageViews have the same colorFilterSet or none at all
*/
protected boolean hasSameColor(Object parentData, Object childData) {
- int parentColor = getColor((Notification) parentData);
- int childColor = getColor((Notification) childData);
+ int parentColor = ((Notification) parentData).color;
+ int childColor = ((Notification) childData).color;
return parentColor == childColor;
}
- private static int getColor(Notification notification) {
- if (notification.shouldUseAppIcon()) {
- return 0; // the color filter isn't applied if using the app icon
- }
- return notification.color;
- }
-
@Override
public boolean isEmpty(View view) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index 0d789c703d48..f5d443443838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -119,7 +119,6 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
/** Factory for constructing an {@link OperatorNameViewController}. */
public static class Factory {
- private final DarkIconDispatcher mDarkIconDispatcher;
private final TunerService mTunerService;
private final TelephonyManager mTelephonyManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -129,7 +128,7 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
private final JavaAdapter mJavaAdapter;
@Inject
- public Factory(DarkIconDispatcher darkIconDispatcher,
+ public Factory(
TunerService tunerService,
TelephonyManager telephonyManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -137,7 +136,6 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
AirplaneModeInteractor airplaneModeInteractor,
SubscriptionManagerProxy subscriptionManagerProxy,
JavaAdapter javaAdapter) {
- mDarkIconDispatcher = darkIconDispatcher;
mTunerService = tunerService;
mTelephonyManager = telephonyManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -148,9 +146,11 @@ public class OperatorNameViewController extends ViewController<OperatorNameView>
}
/** Create an {@link OperatorNameViewController}. */
- public OperatorNameViewController create(OperatorNameView view) {
- return new OperatorNameViewController(view,
- mDarkIconDispatcher,
+ public OperatorNameViewController create(
+ OperatorNameView view, DarkIconDispatcher darkIconDispatcher) {
+ return new OperatorNameViewController(
+ view,
+ darkIconDispatcher,
mTunerService,
mTelephonyManager,
mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index ad3afd4d1756..33f0c64269cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,7 +27,6 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -36,7 +35,6 @@ import android.graphics.Color;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Trace;
@@ -520,34 +518,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
userId = UserHandle.USER_SYSTEM;
}
- // Try to load the monochrome app icon if applicable
- Drawable icon = maybeGetMonochromeAppIcon(context, statusBarIcon);
- // Otherwise, just use the icon normally
- if (icon == null) {
- icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
- }
- return icon;
- }
- }
-
- @Nullable
- private Drawable maybeGetMonochromeAppIcon(Context context,
- StatusBarIcon statusBarIcon) {
- if (android.app.Flags.notificationsUseMonochromeAppIcon()
- && statusBarIcon.type == StatusBarIcon.Type.MaybeMonochromeAppIcon) {
- // Check if we have a monochrome app icon
- PackageManager pm = context.getPackageManager();
- Drawable appIcon = context.getApplicationInfo().loadIcon(pm);
- if (appIcon instanceof AdaptiveIconDrawable) {
- Drawable monochrome = ((AdaptiveIconDrawable) appIcon).getMonochrome();
- if (monochrome != null) {
- setCropToPadding(true);
- setScaleType(ScaleType.CENTER);
- return new ScalingDrawableWrapper(monochrome, APP_ICON_SCALE);
- }
- }
+ return statusBarIcon.icon.loadDrawableAsUser(context, userId);
}
- return null;
}
public StatusBarIcon getStatusBarIcon() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index c8d3f339b3e9..752674854e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -68,13 +68,8 @@ constructor(
notifChipsInteractor.onPromotedNotificationChipTapped(this@toChipModel.key)
}
}
- return OngoingActivityChipModel.Shown.ShortTimeDelta(
- icon,
- colors,
- time = this.whenTime,
- onClickListener,
- )
- // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta.
+ return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ // TODO(b/364653005): Use Notification.showWhen to determine if we should show the time.
// TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text.
// TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
// TODO(b/364653005): If the app that posted the notification is in the foreground, don't
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 9c53cc13f702..e3dc70af5fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -28,14 +29,19 @@ import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.transformLatest
+import kotlinx.coroutines.launch
/** Interactor for the screen recording chip shown in the status bar. */
@SysUISingleton
+@OptIn(ExperimentalCoroutinesApi::class)
class ScreenRecordChipInteractor
@Inject
constructor(
@@ -44,6 +50,32 @@ constructor(
private val mediaProjectionRepository: MediaProjectionRepository,
@StatusBarChipsLog private val logger: LogBuffer,
) {
+ /**
+ * Emits true if we should assume that we're currently screen recording, even if
+ * [ScreenRecordRepository.screenRecordState] hasn't emitted [ScreenRecordModel.Recording] yet.
+ */
+ private val shouldAssumeIsRecording: Flow<Boolean> =
+ screenRecordRepository.screenRecordState
+ .transformLatest {
+ when (it) {
+ is ScreenRecordModel.DoingNothing -> {
+ emit(false)
+ }
+ is ScreenRecordModel.Starting -> {
+ // If we're told that the recording will start in [it.millisUntilStarted],
+ // optimistically assume the recording did indeed start after that time even
+ // if [ScreenRecordRepository.screenRecordState] hasn't emitted
+ // [ScreenRecordModel.Recording] yet. Start 50ms early so that the chip
+ // timer will definitely be showing by the time the recording actually
+ // starts - see b/366448907.
+ delay(it.millisUntilStarted - 50)
+ emit(true)
+ }
+ is ScreenRecordModel.Recording -> {}
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
val screenRecordState: StateFlow<ScreenRecordChipModel> =
// ScreenRecordRepository has the main "is the screen being recorded?" state, and
// MediaProjectionRepository has information about what specifically is being recorded (a
@@ -51,37 +83,55 @@ constructor(
combine(
screenRecordRepository.screenRecordState,
mediaProjectionRepository.mediaProjectionState,
- ) { screenRecordState, mediaProjectionState ->
- when (screenRecordState) {
- is ScreenRecordModel.DoingNothing -> {
- logger.log(TAG, LogLevel.INFO, {}, { "State: DoingNothing" })
- ScreenRecordChipModel.DoingNothing
- }
- is ScreenRecordModel.Starting -> {
- logger.log(
- TAG,
- LogLevel.INFO,
- { long1 = screenRecordState.millisUntilStarted },
- { "State: Starting($long1)" }
- )
- ScreenRecordChipModel.Starting(screenRecordState.millisUntilStarted)
- }
- is ScreenRecordModel.Recording -> {
- val recordedTask =
- if (
- mediaProjectionState is MediaProjectionState.Projecting.SingleTask
- ) {
- mediaProjectionState.task
- } else {
- null
- }
- logger.log(
- TAG,
- LogLevel.INFO,
- { str1 = recordedTask?.baseIntent?.component?.packageName },
- { "State: Recording(taskPackage=$str1)" }
- )
- ScreenRecordChipModel.Recording(recordedTask)
+ shouldAssumeIsRecording,
+ ) { screenRecordState, mediaProjectionState, shouldAssumeIsRecording ->
+ if (
+ Flags.statusBarAutoStartScreenRecordChip() &&
+ shouldAssumeIsRecording &&
+ screenRecordState is ScreenRecordModel.Starting
+ ) {
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "State: Recording(taskPackage=null) due to force-start" },
+ )
+ ScreenRecordChipModel.Recording(recordedTask = null)
+ } else {
+ when (screenRecordState) {
+ is ScreenRecordModel.DoingNothing -> {
+ logger.log(TAG, LogLevel.INFO, {}, { "State: DoingNothing" })
+ ScreenRecordChipModel.DoingNothing
+ }
+
+ is ScreenRecordModel.Starting -> {
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ { long1 = screenRecordState.millisUntilStarted },
+ { "State: Starting($long1)" },
+ )
+ ScreenRecordChipModel.Starting(screenRecordState.millisUntilStarted)
+ }
+
+ is ScreenRecordModel.Recording -> {
+ val recordedTask =
+ if (
+ mediaProjectionState
+ is MediaProjectionState.Projecting.SingleTask
+ ) {
+ mediaProjectionState.task
+ } else {
+ null
+ }
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = recordedTask?.baseIntent?.component?.packageName },
+ { "State: Recording(taskPackage=$str1)" },
+ )
+ ScreenRecordChipModel.Recording(recordedTask)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 3a31851bcb4f..52a79d39bb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.connectivity;
+import static com.android.systemui.Flags.multiuserWifiPickerTrackerSupport;
+
+import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
@@ -65,13 +68,16 @@ public class AccessPointControllerImpl implements AccessPointController,
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
private int mCurrentUser;
+ private Context mContext;
public AccessPointControllerImpl(
+ Context context,
UserManager userManager,
UserTracker userTracker,
Executor mainExecutor,
WifiPickerTrackerFactory wifiPickerTrackerFactory
) {
+ mContext = context;
mUserManager = userManager;
mUserTracker = userTracker;
mCurrentUser = userTracker.getUserId();
@@ -87,7 +93,11 @@ public class AccessPointControllerImpl implements AccessPointController,
*/
public void init() {
if (mWifiPickerTracker == null) {
- mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this, TAG);
+ // We are creating the WifiPickerTracker during init to make sure we have one
+ // available at all times however we expect this to be recreated very quickly
+ // with a user-specific context in onUserSwitched.
+ mWifiPickerTracker =
+ mWifiPickerTrackerFactory.create(mContext, this.getLifecycle(), this, TAG);
}
}
@@ -116,6 +126,19 @@ public class AccessPointControllerImpl implements AccessPointController,
void onUserSwitched(int newUserId) {
mCurrentUser = newUserId;
+ // Return early if multiuser support is not enabled.
+ if (!multiuserWifiPickerTrackerSupport()) {
+ return;
+ }
+
+ if (mWifiPickerTracker != null) {
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED));
+ }
+ Context context = mContext.createContextAsUser(UserHandle.of(newUserId), /* flags= */ 0);
+ mWifiPickerTracker = mWifiPickerTrackerFactory.create(context, mLifecycle, this, TAG);
+ if (!mCallbacks.isEmpty()) {
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED));
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt
index dc2ebe55fb73..947ce33274d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt
@@ -22,6 +22,7 @@ import android.net.wifi.WifiManager
import android.os.Handler
import android.os.SimpleClock
import androidx.lifecycle.Lifecycle
+import com.android.systemui.Flags.multiuserWifiPickerTrackerSupport
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.concurrency.ThreadFactory
@@ -41,7 +42,7 @@ import javax.inject.Inject
class WifiPickerTrackerFactory
@Inject
constructor(
- private val context: Context,
+ private val applicationContext: Context,
private val wifiManager: WifiManager?,
private val connectivityManager: ConnectivityManager,
private val systemClock: SystemClock,
@@ -64,16 +65,23 @@ constructor(
* @return a new [WifiPickerTracker] or null if [WifiManager] is null.
*/
fun create(
+ userContext: Context,
lifecycle: Lifecycle,
listener: WifiPickerTrackerCallback,
name: String,
): WifiPickerTracker? {
return if (wifiManager == null) {
null
- } else
+ } else {
+ val contextToUse =
+ if (multiuserWifiPickerTrackerSupport()) {
+ userContext
+ } else {
+ applicationContext
+ }
WifiPickerTracker(
lifecycle,
- context,
+ contextToUse,
wifiManager,
connectivityManager,
mainHandler,
@@ -86,6 +94,7 @@ constructor(
SCAN_INTERVAL_MILLIS,
listener,
)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
index e1159220e366..84c7ab200fad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -17,11 +17,14 @@
package com.android.systemui.statusbar.core
import android.view.Display
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.statusbar.data.repository.LightBarControllerStore
+import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStore
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
@@ -29,7 +32,6 @@ import com.android.systemui.util.kotlin.pairwiseBy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Responsible for creating and starting the status bar components for each display. Also does it
@@ -48,6 +50,8 @@ constructor(
private val initializerStore: StatusBarInitializerStore,
private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
private val statusBarInitializerStore: StatusBarInitializerStore,
+ private val privacyDotWindowControllerStore: PrivacyDotWindowControllerStore,
+ private val lightBarControllerStore: LightBarControllerStore,
) : CoreStartable {
init {
@@ -71,6 +75,15 @@ constructor(
val displayId = display.displayId
createAndStartOrchestratorForDisplay(displayId)
createAndStartInitializerForDisplay(displayId)
+ startPrivacyDotForDisplay(displayId)
+ createLightBarControllerForDisplay(displayId)
+ }
+
+ private fun createLightBarControllerForDisplay(displayId: Int) {
+ // Explicitly not calling `start()`, because the store is already calling `start()`.
+ // This is to maintain the legacy behavior with NavigationBar, that was already expecting
+ // LightBarController to start at construction time.
+ lightBarControllerStore.forDisplay(displayId)
}
private fun createAndStartOrchestratorForDisplay(displayId: Int) {
@@ -89,4 +102,12 @@ constructor(
private fun createAndStartInitializerForDisplay(displayId: Int) {
statusBarInitializerStore.forDisplay(displayId).start()
}
+
+ private fun startPrivacyDotForDisplay(displayId: Int) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // For the default display, privacy dot is started via ScreenDecorations
+ return
+ }
+ privacyDotWindowControllerStore.forDisplay(displayId).start()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 3abbc6e0d1cb..4c54fc49e536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -23,6 +23,7 @@ import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarView
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
@@ -71,7 +72,10 @@ interface StatusBarInitializer : CoreStartable {
}
interface Factory {
- fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer
+ fun create(
+ statusBarWindowController: StatusBarWindowController,
+ statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
+ ): StatusBarInitializer
}
}
@@ -79,6 +83,7 @@ class StatusBarInitializerImpl
@AssistedInject
constructor(
@Assisted private val statusBarWindowController: StatusBarWindowController,
+ @Assisted private val statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
private val statusBarRootFactory: StatusBarRootFactory,
private val componentFactory: HomeStatusBarComponent.Factory,
@@ -107,12 +112,12 @@ constructor(
}
override fun initializeStatusBar() {
- StatusBarSimpleFragment.assertInLegacyMode()
+ StatusBarRootModernization.assertInLegacyMode()
doStart()
}
private fun doStart() {
- if (StatusBarSimpleFragment.isEnabled) doComposeStart() else doLegacyStart()
+ if (StatusBarRootModernization.isEnabled) doComposeStart() else doLegacyStart()
}
/**
@@ -127,7 +132,7 @@ constructor(
val phoneStatusBarView = cv.findViewById<PhoneStatusBarView>(R.id.status_bar)
component =
componentFactory.create(phoneStatusBarView).also { component ->
- // CollapsedStatusBarFragment used to be responsible initializting
+ // CollapsedStatusBarFragment used to be responsible initializing
component.init()
statusBarViewUpdatedListener?.onStatusBarViewUpdated(
@@ -135,8 +140,12 @@ constructor(
component.phoneStatusBarTransitions,
)
- creationListeners.forEach { listener ->
- listener.onStatusBarViewInitialized(component)
+ if (StatusBarConnectedDisplays.isEnabled) {
+ statusBarModePerDisplayRepository.onStatusBarViewInitialized(component)
+ } else {
+ creationListeners.forEach { listener ->
+ listener.onStatusBarViewInitialized(component)
+ }
}
}
}
@@ -184,7 +193,8 @@ constructor(
@AssistedFactory
interface Factory : StatusBarInitializer.Factory {
override fun create(
- statusBarWindowController: StatusBarWindowController
+ statusBarWindowController: StatusBarWindowController,
+ statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
): StatusBarInitializerImpl
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
index 041f0b0fdf93..4f815c1f0b31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -22,6 +22,7 @@ import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.PerDisplayStore
import com.android.systemui.display.data.repository.PerDisplayStoreImpl
import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -37,6 +38,7 @@ constructor(
displayRepository: DisplayRepository,
private val factory: StatusBarInitializer.Factory,
private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
) :
StatusBarInitializerStore,
PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) {
@@ -47,7 +49,8 @@ constructor(
override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer {
return factory.create(
- statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId)
+ statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId),
+ statusBarModePerDisplayRepository = statusBarModeRepositoryStore.forDisplay(displayId),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index f33b76b17f96..ff4760fd2837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.core
import android.view.Display
import android.view.View
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.DarkIconDispatcher
@@ -46,12 +48,12 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.io.PrintWriter
import java.util.Optional
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Class responsible for managing the lifecycle and state of the status bar.
@@ -68,6 +70,7 @@ constructor(
@Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository,
@Assisted private val statusBarInitializer: StatusBarInitializer,
@Assisted private val statusBarWindowController: StatusBarWindowController,
+ @Main private val mainContext: CoroutineContext,
private val demoModeController: DemoModeController,
private val pluginDependencyProvider: PluginDependencyProvider,
private val autoHideController: AutoHideController,
@@ -141,7 +144,8 @@ constructor(
override fun start() {
StatusBarConnectedDisplays.assertInNewMode()
coroutineScope
- .launch {
+ // Perform animations on the main thread to prevent crashes.
+ .launch(context = mainContext) {
dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator)
launch {
controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
index 214151383dc6..057213fa4b18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt
@@ -21,9 +21,9 @@ import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
/** Helper for reading and using the status bar simple fragment flag state */
-object StatusBarSimpleFragment {
+object StatusBarRootModernization {
/** Aconfig flag for removing the fragment */
- const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_ROOT_MODERNIZATION
/** A token used for dependency declaration */
val token: FlagToken
@@ -32,7 +32,7 @@ object StatusBarSimpleFragment {
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.statusBarSimpleFragment()
+ get() = Flags.statusBarRootModernization()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index f65ae67efbf1..434120051039 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -26,6 +26,7 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.StatusBarDataLayerModule
+import com.android.systemui.statusbar.data.repository.LightBarControllerStore
import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl
@@ -55,26 +56,26 @@ import dagger.multibindings.IntoMap
* [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.).
*/
@Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class])
-abstract class StatusBarModule {
+interface StatusBarModule {
@Binds
@IntoMap
@ClassKey(OngoingCallController::class)
- abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
+ fun bindOngoingCallController(impl: OngoingCallController): CoreStartable
@Binds
@IntoMap
@ClassKey(LightBarController::class)
- abstract fun bindLightBarController(impl: LightBarController): CoreStartable
+ fun lightBarControllerAsCoreStartable(controller: LightBarController): CoreStartable
@Binds
@IntoMap
@ClassKey(StatusBarSignalPolicy::class)
- abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
+ fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
@Binds
@SysUISingleton
- abstract fun statusBarWindowControllerFactory(
+ fun statusBarWindowControllerFactory(
implFactory: StatusBarWindowControllerImpl.Factory
): StatusBarWindowController.Factory
@@ -82,6 +83,12 @@ abstract class StatusBarModule {
@Provides
@SysUISingleton
+ fun lightBarController(store: LightBarControllerStore): LightBarController {
+ return store.defaultDisplay
+ }
+
+ @Provides
+ @SysUISingleton
fun windowControllerStore(
multiDisplayImplLazy: Lazy<MultiDisplayStatusBarWindowControllerStore>,
singleDisplayImplLazy: Lazy<SingleDisplayStatusBarWindowControllerStore>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index c416bf7b4f92..27d815190d85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -15,23 +15,29 @@
*/
package com.android.systemui.statusbar.data
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStoreModule
import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
+import com.android.systemui.statusbar.data.repository.LightBarControllerStoreModule
import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStoreModule
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule
+import com.android.systemui.statusbar.data.repository.SystemEventChipAnimationControllerStoreModule
import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule
import dagger.Module
@Module(
includes =
[
+ DarkIconDispatcherStoreModule::class,
KeyguardStatusBarRepositoryModule::class,
+ LightBarControllerStoreModule::class,
RemoteInputRepositoryModule::class,
StatusBarConfigurationControllerModule::class,
StatusBarContentInsetsProviderStoreModule::class,
StatusBarModeRepositoryModule::class,
StatusBarPhoneDataLayerModule::class,
+ SystemEventChipAnimationControllerStoreModule::class,
]
)
object StatusBarDataLayerModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
new file mode 100644
index 000000000000..8183a487cee2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.content.Context
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl
+import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
+import dagger.Binds
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Provides per display instances of [DarkIconDispatcher]. */
+interface DarkIconDispatcherStore : PerDisplayStore<DarkIconDispatcher>
+
+/** Provides per display instances of [SysuiDarkIconDispatcher]. */
+interface SysuiDarkIconDispatcherStore : PerDisplayStore<SysuiDarkIconDispatcher>
+
+/**
+ * Multi display implementation that should be used when the [StatusBarConnectedDisplays] flag is
+ * enabled.
+ */
+@SysUISingleton
+class MultiDisplayDarkIconDispatcherStore
+@Inject
+constructor(
+ @Background backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
+ private val factory: DarkIconDispatcherImpl.Factory,
+ private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+) :
+ SysuiDarkIconDispatcherStore,
+ PerDisplayStoreImpl<SysuiDarkIconDispatcher>(backgroundApplicationScope, displayRepository) {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher {
+ val properties = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR)
+ return factory.create(displayId, properties.context)
+ }
+
+ override suspend fun onDisplayRemovalAction(instance: SysuiDarkIconDispatcher) {
+ instance.stop()
+ }
+
+ override val instanceClass = SysuiDarkIconDispatcher::class.java
+}
+
+/**
+ * Single display implementation that should be used when the [StatusBarConnectedDisplays] flag is
+ * disabled.
+ */
+@SysUISingleton
+class SingleDisplayDarkIconDispatcherStore
+@Inject
+constructor(factory: DarkIconDispatcherImpl.Factory, context: Context) :
+ SysuiDarkIconDispatcherStore,
+ PerDisplayStore<SysuiDarkIconDispatcher> by SingleDisplayStore(
+ defaultInstance = factory.create(context.displayId, context)
+ ) {
+
+ init {
+ StatusBarConnectedDisplays.assertInLegacyMode()
+ }
+}
+
+/** Extra implementation that simply implements the [DarkIconDispatcherStore] interface. */
+@SysUISingleton
+class DarkIconDispatcherStoreImpl
+@Inject
+constructor(private val store: SysuiDarkIconDispatcherStore) : DarkIconDispatcherStore {
+ override val defaultDisplay: DarkIconDispatcher
+ get() = store.defaultDisplay
+
+ override fun forDisplay(displayId: Int): DarkIconDispatcher = store.forDisplay(displayId)
+}
+
+@Module
+interface DarkIconDispatcherStoreModule {
+
+ @Binds fun store(impl: DarkIconDispatcherStoreImpl): DarkIconDispatcherStore
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun sysUiStore(
+ singleDisplayLazy: Lazy<SingleDisplayDarkIconDispatcherStore>,
+ multiDisplayLazy: Lazy<MultiDisplayDarkIconDispatcherStore>,
+ ): SysuiDarkIconDispatcherStore {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ singleDisplayLazy.get()
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(DarkIconDispatcherStore::class)
+ fun storeAsCoreStartable(
+ multiDisplayLazy: Lazy<MultiDisplayDarkIconDispatcherStore>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
new file mode 100644
index 000000000000..e4987555833b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.statusbar.phone.LightBarController
+import com.android.systemui.statusbar.phone.LightBarControllerImpl
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Provides per display instances of [LightBarController]. */
+interface LightBarControllerStore : PerDisplayStore<LightBarController>
+
+@SysUISingleton
+class LightBarControllerStoreImpl
+@Inject
+constructor(
+ @Background backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
+ private val factory: LightBarControllerImpl.Factory,
+ private val displayScopeRepository: DisplayScopeRepository,
+ private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ private val darkIconDispatcherStore: DarkIconDispatcherStore,
+) :
+ LightBarControllerStore,
+ PerDisplayStoreImpl<LightBarController>(backgroundApplicationScope, displayRepository) {
+
+ override fun createInstanceForDisplay(displayId: Int): LightBarController {
+ return factory
+ .create(
+ displayId,
+ displayScopeRepository.scopeForDisplay(displayId),
+ darkIconDispatcherStore.forDisplay(displayId),
+ statusBarModeRepositoryStore.forDisplay(displayId),
+ )
+ .also { it.start() }
+ }
+
+ override suspend fun onDisplayRemovalAction(instance: LightBarController) {
+ instance.stop()
+ }
+
+ override val instanceClass = LightBarController::class.java
+}
+
+@Module
+interface LightBarControllerStoreModule {
+
+ @Binds fun store(impl: LightBarControllerStoreImpl): LightBarControllerStore
+
+ @Binds
+ @IntoMap
+ @ClassKey(LightBarControllerStore::class)
+ fun storeAsCoreStartable(impl: LightBarControllerStoreImpl): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
new file mode 100644
index 000000000000..a1f56552629b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.events.PrivacyDotWindowController
+import dagger.Binds
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Providers per display instances of [PrivacyDotWindowController]. */
+interface PrivacyDotWindowControllerStore : PerDisplayStore<PrivacyDotWindowController>
+
+@SysUISingleton
+class PrivacyDotWindowControllerStoreImpl
+@Inject
+constructor(
+ @Background backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
+ private val windowControllerFactory: PrivacyDotWindowController.Factory,
+ private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+ private val privacyDotViewControllerStore: PrivacyDotViewControllerStore,
+ private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
+) :
+ PrivacyDotWindowControllerStore,
+ PerDisplayStoreImpl<PrivacyDotWindowController>(backgroundApplicationScope, displayRepository) {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ throw IllegalArgumentException("This class should only be used for connected displays")
+ }
+ val displayWindowProperties =
+ displayWindowPropertiesRepository.get(displayId, TYPE_NAVIGATION_BAR_PANEL)
+ return windowControllerFactory.create(
+ displayId = displayId,
+ privacyDotViewController = privacyDotViewControllerStore.forDisplay(displayId),
+ viewCaptureAwareWindowManager =
+ viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager),
+ inflater = displayWindowProperties.layoutInflater,
+ )
+ }
+
+ override val instanceClass = PrivacyDotWindowController::class.java
+}
+
+@Module
+interface PrivacyDotWindowControllerStoreModule {
+
+ @Binds fun store(impl: PrivacyDotWindowControllerStoreImpl): PrivacyDotWindowControllerStore
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(PrivacyDotWindowControllerStore::class)
+ fun storeAsCoreStartable(
+ storeLazy: Lazy<PrivacyDotWindowControllerStoreImpl>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ storeLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 44bee1d784fc..cc91e2dc3a25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -27,7 +27,7 @@ import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BA
import android.view.WindowInsetsController.Appearance
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
-import com.android.systemui.Dumpable
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
@@ -59,7 +59,7 @@ import kotlinx.coroutines.flow.stateIn
* Note: These status bar modes are status bar *window* states that are sent to us from
* WindowManager, not determined internally.
*/
-interface StatusBarModePerDisplayRepository {
+interface StatusBarModePerDisplayRepository : OnStatusBarViewInitializedListener, CoreStartable {
/**
* True if the status bar window is showing transiently and will disappear soon, and false
* otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
@@ -104,6 +104,12 @@ interface StatusBarModePerDisplayRepository {
* determined internally instead.
*/
fun clearTransient()
+
+ /**
+ * Called when the [StatusBarModePerDisplayRepository] should stop doing any work and clean up
+ * if needed.
+ */
+ fun stop()
}
class StatusBarModePerDisplayRepositoryImpl
@@ -114,7 +120,7 @@ constructor(
private val commandQueue: CommandQueue,
private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
ongoingCallRepository: OngoingCallRepository,
-) : StatusBarModePerDisplayRepository, OnStatusBarViewInitializedListener, Dumpable {
+) : StatusBarModePerDisplayRepository {
private val commandQueueCallback =
object : CommandQueue.Callbacks {
@@ -163,10 +169,14 @@ constructor(
}
}
- fun start() {
+ override fun start() {
commandQueue.addCallback(commandQueueCallback)
}
+ override fun stop() {
+ commandQueue.removeCallback(commandQueueCallback)
+ }
+
private val _isTransientShown = MutableStateFlow(false)
override val isTransientShown: StateFlow<Boolean> = _isTransientShown.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
index 2c9fa25d8535..143e99823860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
@@ -18,21 +18,54 @@ package com.android.systemui.statusbar.data.repository
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
import dagger.Binds
+import dagger.Lazy
import dagger.Module
+import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet
import java.io.PrintWriter
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
-interface StatusBarModeRepositoryStore {
- val defaultDisplay: StatusBarModePerDisplayRepository
+interface StatusBarModeRepositoryStore : PerDisplayStore<StatusBarModePerDisplayRepository>
- fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository
+@SysUISingleton
+class MultiDisplayStatusBarModeRepositoryStore
+@Inject
+constructor(
+ @Background backgroundApplicationScope: CoroutineScope,
+ private val factory: StatusBarModePerDisplayRepositoryFactory,
+ displayRepository: DisplayRepository,
+) :
+ StatusBarModeRepositoryStore,
+ PerDisplayStoreImpl<StatusBarModePerDisplayRepository>(
+ backgroundApplicationScope,
+ displayRepository,
+ ) {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun createInstanceForDisplay(displayId: Int): StatusBarModePerDisplayRepository {
+ return factory.create(displayId).also { it.start() }
+ }
+
+ override suspend fun onDisplayRemovalAction(instance: StatusBarModePerDisplayRepository) {
+ instance.stop()
+ }
+
+ override val instanceClass = StatusBarModePerDisplayRepository::class.java
}
@SysUISingleton
@@ -47,10 +80,7 @@ constructor(
StatusBarInitializer.OnStatusBarViewInitializedListener {
override val defaultDisplay = factory.create(displayId)
- override fun forDisplay(displayId: Int) =
- // TODO(b/369337087): implement per display status bar modes.
- // For now just use default display instance.
- defaultDisplay
+ override fun forDisplay(displayId: Int) = defaultDisplay
override fun start() {
defaultDisplay.start()
@@ -66,17 +96,40 @@ constructor(
}
@Module
-interface StatusBarModeRepositoryModule {
- @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepositoryStore
-
- @Binds
- @IntoMap
- @ClassKey(StatusBarModeRepositoryStore::class)
- fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable
-
+abstract class StatusBarModeRepositoryModule {
@Binds
@IntoSet
- fun bindViewInitListener(
+ abstract fun bindViewInitListener(
impl: StatusBarModeRepositoryImpl
): StatusBarInitializer.OnStatusBarViewInitializedListener
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(StatusBarModeRepositoryStore::class)
+ fun storeAsCoreStartable(
+ singleDisplayLazy: Lazy<StatusBarModeRepositoryImpl>,
+ multiDisplayLazy: Lazy<MultiDisplayStatusBarModeRepositoryStore>,
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ singleDisplayLazy.get()
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ fun store(
+ singleDisplayLazy: Lazy<StatusBarModeRepositoryImpl>,
+ multiDisplayLazy: Lazy<MultiDisplayStatusBarModeRepositoryStore>,
+ ): StatusBarModeRepositoryStore {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ singleDisplayLazy.get()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt
new file mode 100644
index 000000000000..7760f58805c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.events.SystemEventChipAnimationController
+import com.android.systemui.statusbar.events.SystemEventChipAnimationControllerImpl
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import dagger.Binds
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Provides per display instances of [SystemEventChipAnimationController]. */
+interface SystemEventChipAnimationControllerStore :
+ PerDisplayStore<SystemEventChipAnimationController>
+
+@SysUISingleton
+class SystemEventChipAnimationControllerStoreImpl
+@Inject
+constructor(
+ @Background backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
+ private val factory: SystemEventChipAnimationControllerImpl.Factory,
+ private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
+) :
+ SystemEventChipAnimationControllerStore,
+ PerDisplayStoreImpl<SystemEventChipAnimationController>(
+ backgroundApplicationScope,
+ displayRepository,
+ ) {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController {
+ return factory.create(
+ displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context,
+ statusBarWindowControllerStore.forDisplay(displayId),
+ statusBarContentInsetsProviderStore.forDisplay(displayId),
+ )
+ }
+
+ override suspend fun onDisplayRemovalAction(instance: SystemEventChipAnimationController) {
+ instance.stop()
+ }
+
+ override val instanceClass = SystemEventChipAnimationController::class.java
+}
+
+@Module
+interface SystemEventChipAnimationControllerStoreModule {
+
+ @Binds
+ @SysUISingleton
+ fun store(
+ impl: SystemEventChipAnimationControllerStoreImpl
+ ): SystemEventChipAnimationControllerStore
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(SystemEventChipAnimationControllerStore::class)
+ fun storeAsCoreStartable(
+ implLazy: Lazy<SystemEventChipAnimationControllerStoreImpl>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ implLazy.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt
new file mode 100644
index 000000000000..f2bb7b16439d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import androidx.core.animation.Animator
+import androidx.core.animation.AnimatorSet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.data.repository.SystemEventChipAnimationControllerStore
+import javax.inject.Inject
+
+/**
+ * A [SystemEventChipAnimationController] that handles animations for multiple displays. It
+ * delegates the animation tasks to individual controllers for each display.
+ */
+@SysUISingleton
+class MultiDisplaySystemEventChipAnimationController
+@Inject
+constructor(
+ private val displayRepository: DisplayRepository,
+ private val controllerStore: SystemEventChipAnimationControllerStore,
+) : SystemEventChipAnimationController {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun prepareChipAnimation(viewCreator: ViewCreator) {
+ forEachController { it.prepareChipAnimation(viewCreator) }
+ }
+
+ override fun init() {
+ forEachController { it.init() }
+ }
+
+ override fun stop() {
+ forEachController { it.stop() }
+ }
+
+ override fun announceForAccessibility(contentDescriptions: String) {
+ forEachController { it.announceForAccessibility(contentDescriptions) }
+ }
+
+ override fun onSystemEventAnimationBegin(): Animator {
+ val animators = controllersForAllDisplays().map { it.onSystemEventAnimationBegin() }
+ return AnimatorSet().apply { playTogether(animators) }
+ }
+
+ override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
+ val animators =
+ controllersForAllDisplays().map { it.onSystemEventAnimationFinish(hasPersistentDot) }
+ return AnimatorSet().apply { playTogether(animators) }
+ }
+
+ private fun forEachController(consumer: (SystemEventChipAnimationController) -> Unit) {
+ controllersForAllDisplays().forEach { consumer(it) }
+ }
+
+ private fun controllersForAllDisplays() =
+ displayRepository.displays.value.map { controllerStore.forDisplay(it.displayId) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
new file mode 100644
index 000000000000..9928ac67f185
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import android.view.Display
+import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM
+import android.view.DisplayCutout.BOUNDS_POSITION_LEFT
+import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT
+import android.view.DisplayCutout.BOUNDS_POSITION_TOP
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.ScreenDecorations
+import com.android.systemui.ScreenDecorationsThread
+import com.android.systemui.decor.DecorProvider
+import com.android.systemui.decor.PrivacyDotCornerDecorProviderImpl
+import com.android.systemui.decor.PrivacyDotDecorProviderFactory
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
+import com.android.systemui.util.containsExactly
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
+
+/**
+ * Responsible for adding the privacy dot to a window.
+ *
+ * It will create one window per corner (top left, top right, bottom left, bottom right), which are
+ * used dependant on the display's rotation.
+ */
+class PrivacyDotWindowController
+@AssistedInject
+constructor(
+ @Assisted private val displayId: Int,
+ @Assisted private val privacyDotViewController: PrivacyDotViewController,
+ @Assisted private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ @Assisted private val inflater: LayoutInflater,
+ @ScreenDecorationsThread private val uiExecutor: Executor,
+ private val dotFactory: PrivacyDotDecorProviderFactory,
+) {
+
+ fun start() {
+ uiExecutor.execute { startOnUiThread() }
+ }
+
+ private fun startOnUiThread() {
+ val providers = dotFactory.providers
+
+ val topLeft = providers.inflate(BOUNDS_POSITION_TOP, BOUNDS_POSITION_LEFT)
+ val topRight = providers.inflate(BOUNDS_POSITION_TOP, BOUNDS_POSITION_RIGHT)
+ val bottomLeft = providers.inflate(BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_LEFT)
+ val bottomRight = providers.inflate(BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_RIGHT)
+
+ topLeft.addToWindow(TopLeft)
+ topRight.addToWindow(TopRight)
+ bottomLeft.addToWindow(BottomLeft)
+ bottomRight.addToWindow(BottomRight)
+
+ privacyDotViewController.initialize(topLeft, topRight, bottomLeft, bottomRight)
+ }
+
+ private fun List<DecorProvider>.inflate(alignedBound1: Int, alignedBound2: Int): View {
+ val provider =
+ first { it.alignedBounds.containsExactly(alignedBound1, alignedBound2) }
+ as PrivacyDotCornerDecorProviderImpl
+ return inflater.inflate(/* resource= */ provider.layoutId, /* root= */ null)
+ }
+
+ private fun View.addToWindow(corner: PrivacyDotCorner) {
+ val excludeFromScreenshots = displayId == Display.DEFAULT_DISPLAY
+ val params =
+ ScreenDecorations.getWindowLayoutBaseParams(excludeFromScreenshots).apply {
+ width = WRAP_CONTENT
+ height = WRAP_CONTENT
+ gravity = corner.rotatedCorner(context.display.rotation).gravity
+ title = "PrivacyDot${corner.title}$displayId"
+ }
+ // PrivacyDotViewController expects the dot view to have a FrameLayout parent.
+ val rootView = FrameLayout(context)
+ rootView.addView(this)
+ viewCaptureAwareWindowManager.addView(rootView, params)
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(
+ displayId: Int,
+ privacyDotViewController: PrivacyDotViewController,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ inflater: LayoutInflater,
+ ): PrivacyDotWindowController
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index b28660590ad0..1038ad4124ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -32,13 +32,16 @@ import androidx.core.animation.AnimatorSet
import androidx.core.animation.ValueAnimator
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Default
import com.android.systemui.res.R
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
+import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
@@ -57,6 +60,8 @@ interface SystemEventChipAnimationController : SystemStatusAnimationCallback {
fun init()
+ fun stop()
+
/** Announces [contentDescriptions] for accessibility. */
fun announceForAccessibility(contentDescriptions: String)
@@ -287,6 +292,26 @@ constructor(
return animSet
}
+ private val statusBarContentInsetsChangedListener =
+ object : StatusBarContentInsetsChangedListener {
+ override fun onStatusBarContentInsetsChanged() {
+ val newContentArea =
+ contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
+ updateDimens(newContentArea)
+
+ // If we are currently animating, we have to re-solve for the chip bounds. If
+ // we're not animating then [prepareChipAnimation] will take care of it for us.
+ currentAnimatedView?.let {
+ updateChipBounds(it, newContentArea)
+ // Since updateCurrentAnimatedView can only be called during an animation,
+ // we have to create a no-op animator here to apply the new chip bounds.
+ val animator = ValueAnimator.ofInt(0, 1).setDuration(0)
+ animator.addUpdateListener { updateCurrentAnimatedView() }
+ animator.start()
+ }
+ }
+ }
+
override fun init() {
initialized = true
themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
@@ -303,28 +328,11 @@ constructor(
// Use contentInsetsProvider rather than configuration controller, since we only care
// about status bar dimens
- contentInsetsProvider.addCallback(
- object : StatusBarContentInsetsChangedListener {
- override fun onStatusBarContentInsetsChanged() {
- val newContentArea =
- contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()
- updateDimens(newContentArea)
-
- // If we are currently animating, we have to re-solve for the chip bounds. If
- // we're
- // not animating then [prepareChipAnimation] will take care of it for us
- currentAnimatedView?.let {
- updateChipBounds(it, newContentArea)
- // Since updateCurrentAnimatedView can only be called during an animation,
- // we
- // have to create a dummy animator here to apply the new chip bounds
- val animator = ValueAnimator.ofInt(0, 1).setDuration(0)
- animator.addUpdateListener { updateCurrentAnimatedView() }
- animator.start()
- }
- }
- }
- )
+ contentInsetsProvider.addCallback(statusBarContentInsetsChangedListener)
+ }
+
+ override fun stop() {
+ contentInsetsProvider.removeCallback(statusBarContentInsetsChangedListener)
}
override fun announceForAccessibility(contentDescriptions: String) {
@@ -418,7 +426,7 @@ constructor(
}
@AssistedFactory
- interface Factory {
+ fun interface Factory {
fun create(
context: Context,
statusBarWindowController: StatusBarWindowController,
@@ -446,20 +454,36 @@ private const val LEFT = 1
private const val RIGHT = 2
@Module
-object SystemEventChipAnimationControllerModule {
-
- @Provides
- @SysUISingleton
- fun controller(
- factory: SystemEventChipAnimationControllerImpl.Factory,
- context: Context,
- statusBarWindowControllerStore: StatusBarWindowControllerStore,
- contentInsetsProviderStore: StatusBarContentInsetsProviderStore,
- ): SystemEventChipAnimationController {
- return factory.create(
- context,
- statusBarWindowControllerStore.defaultDisplay,
- contentInsetsProviderStore.defaultDisplay,
- )
+interface SystemEventChipAnimationControllerModule {
+
+ companion object {
+ @Provides
+ @Default
+ @SysUISingleton
+ fun defaultController(
+ factory: SystemEventChipAnimationControllerImpl.Factory,
+ context: Context,
+ statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ contentInsetsProviderStore: StatusBarContentInsetsProviderStore,
+ ): SystemEventChipAnimationController {
+ return factory.create(
+ context,
+ statusBarWindowControllerStore.defaultDisplay,
+ contentInsetsProviderStore.defaultDisplay,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ fun controller(
+ @Default defaultLazy: Lazy<SystemEventChipAnimationController>,
+ multiDisplayLazy: Lazy<MultiDisplaySystemEventChipAnimationController>,
+ ): SystemEventChipAnimationController {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayLazy.get()
+ } else {
+ defaultLazy.get()
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 564d52a62cde..1cb4c44d10e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -27,7 +27,10 @@ import kotlinx.coroutines.flow.StateFlow
interface SystemStatusAnimationScheduler :
CallbackController<SystemStatusAnimationCallback>, Dumpable {
- /** StateFlow holding the current [SystemEventAnimationState] at any time. */
+ /**
+ * The current state of the animation. This can be used from compose functions to coordinate
+ * their animations with the chip
+ */
val animationState: StateFlow<SystemEventAnimationState>
fun onStatusEvent(event: StatusEvent)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt
new file mode 100644
index 000000000000..971f5d1b04f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** Repository to expose the [SystemStatusAnimationScheduler] state via flows */
+interface SystemStatusEventAnimationRepository {
+ val animationState: StateFlow<SystemEventAnimationState>
+}
+
+@SysUISingleton
+class SystemStatusEventAnimationRepositoryImpl
+@Inject
+constructor(scheduler: SystemStatusAnimationScheduler) : SystemStatusEventAnimationRepository {
+ override val animationState = scheduler.animationState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt
new file mode 100644
index 000000000000..3e3064223595
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events.domain.interactor
+
+import android.view.View
+import androidx.core.animation.Animator
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepository
+import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Interactor for dealing with system status event animations. This class can be used to monitor the
+ * current [animationState], and defines some common animation functions that an handle hiding
+ * system chrome in order to make space for the event chips
+ */
+@SysUISingleton
+class SystemStatusEventAnimationInteractor
+@Inject
+constructor(
+ repo: SystemStatusEventAnimationRepository,
+ configurationInteractor: ConfigurationInteractor,
+ @Application scope: CoroutineScope,
+) {
+ private val chipAnimateInTranslationX =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
+ .stateIn(scope, SharingStarted.Eagerly, 0)
+
+ private val chipAnimateOutTranslationX =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
+ .stateIn(scope, SharingStarted.Eagerly, 0)
+
+ val animationState = repo.animationState
+
+ private fun getDefaultStatusBarAnimationForChipEnter(
+ setX: (Float) -> Unit,
+ setAlpha: (Float) -> Unit,
+ ): Animator {
+ return StatusBarSystemEventDefaultAnimator.getDefaultStatusBarAnimationForChipEnter(
+ chipAnimateInTranslationX.value,
+ setX,
+ setAlpha,
+ )
+ }
+
+ private fun getDefaultStatusBarAnimationForChipExit(
+ setX: (Float) -> Unit,
+ setAlpha: (Float) -> Unit,
+ ): Animator {
+ return StatusBarSystemEventDefaultAnimator.getDefaultStatusBarAnimationForChipExit(
+ chipAnimateOutTranslationX.value,
+ setX,
+ setAlpha,
+ )
+ }
+
+ fun animateStatusBarContentForChipEnter(v: View) {
+ getDefaultStatusBarAnimationForChipEnter(setX = v::setTranslationX, setAlpha = v::setAlpha)
+ .start()
+ }
+
+ fun animateStatusBarContentForChipExit(v: View) {
+ v.translationX = chipAnimateOutTranslationX.value.toFloat()
+ getDefaultStatusBarAnimationForChipExit(setX = v::setTranslationX, setAlpha = v::setAlpha)
+ .start()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 77ec65bfad6f..9a779300de97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -49,12 +49,12 @@ class ConversationNotificationProcessor
@Inject
constructor(
private val launcherApps: LauncherApps,
- private val conversationNotificationManager: ConversationNotificationManager
+ private val conversationNotificationManager: ConversationNotificationManager,
) {
fun processNotification(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): Notification.MessagingStyle? {
val messagingStyle = recoveredBuilder.style as? Notification.MessagingStyle ?: return null
messagingStyle.conversationType =
@@ -83,7 +83,7 @@ constructor(
private val notifCollection: CommonNotifCollection,
private val bindEventManager: BindEventManager,
private val headsUpManager: HeadsUpManager,
- private val statusBarStateController: StatusBarStateController
+ private val statusBarStateController: StatusBarStateController,
) {
private var isStatusBarExpanded = false
@@ -118,7 +118,8 @@ constructor(
.flatMap { layout -> layout.allViews.asSequence() }
.flatMap { view ->
(view as? ConversationLayout)?.messagingGroups?.asSequence()
- ?: (view as? MessagingLayout)?.messagingGroups?.asSequence() ?: emptySequence()
+ ?: (view as? MessagingLayout)?.messagingGroups?.asSequence()
+ ?: emptySequence()
}
.flatMap { messagingGroup -> messagingGroup.messageContainer.children }
.mapNotNull { view ->
@@ -144,7 +145,7 @@ constructor(
bindEventManager: BindEventManager,
@ShadeDisplayAware private val context: Context,
private val notifCollection: CommonNotifCollection,
- @Main private val mainHandler: Handler
+ @Main private val mainHandler: Handler,
) {
// Need this state to be thread safe, since it's accessed from the ui thread
// (NotificationEntryListener) and a bg thread (NotificationRowContentBinder)
@@ -175,7 +176,7 @@ constructor(
// the notif has been moved in the shade
mainHandler.postDelayed(
{ layout.setIsImportantConversation(important, true) },
- IMPORTANCE_ANIMATION_DELAY.toLong()
+ IMPORTANCE_ANIMATION_DELAY.toLong(),
)
} else {
layout.setIsImportantConversation(important, false)
@@ -233,8 +234,7 @@ constructor(
state?.run {
if (shouldIncrementUnread(recoveredBuilder)) unreadCount + 1
else unreadCount
- }
- ?: 1
+ } ?: 1
ConversationState(newCount, entry.sbn.notification)
}!!
.unreadCount
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
index bdd9fd032800..6b6920a3621a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
@@ -28,14 +28,5 @@ import com.android.systemui.res.R
@MainThread
fun contentDescForNotification(c: Context, n: Notification): CharSequence {
val appName = n.loadHeaderAppName(c) ?: ""
- val title = n.extras?.getCharSequence(Notification.EXTRA_TITLE)
- val text = n.extras?.getCharSequence(Notification.EXTRA_TEXT)
- val ticker = n.tickerText
-
- // Some apps just put the app name into the title
- val titleOrText = if (TextUtils.equals(title, appName)) text else title
- val desc =
- if (!TextUtils.isEmpty(titleOrText)) titleOrText
- else if (!TextUtils.isEmpty(ticker)) ticker else ""
- return c.getString(R.string.accessibility_desc_notification_icon, appName, desc)
+ return c.getString(R.string.accessibility_desc_notification_icon, appName, "")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
index 958001625a07..1f8d365cfdad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
@@ -102,6 +102,10 @@ class NotifCollectionCache<V>(
return --lives <= 0
}
}
+
+ override fun toString(): String {
+ return "$key = $value"
+ }
}
/**
@@ -174,7 +178,10 @@ class NotifCollectionCache<V>(
pw.println("$TAG(retainCount = $retainCount, purgeTimeoutMillis = $purgeTimeoutMillis)")
pw.withIncreasedIndent {
- pw.printCollection("keys present in cache", cache.keys.stream().sorted().toList())
+ pw.printCollection(
+ "entries present in cache",
+ cache.values.stream().map { it.toString() }.sorted().toList(),
+ )
val misses = misses.get()
val hits = hits.get()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index e74ed8d27ec2..c487ff5d35bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -49,6 +49,7 @@ import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import android.view.ContentInfo;
import androidx.annotation.NonNull;
@@ -65,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -194,6 +196,10 @@ public final class NotificationEntry extends ListEntry {
*/
private boolean mIsDemoted = false;
+ // TODO(b/377565433): Move into NotificationContentModel during/after
+ // NotificationRowContentBinderRefactor.
+ private PromotedNotificationContentModel mPromotedNotificationContentModel;
+
/**
* True if both
* 1) app provided full screen intent but does not have the permission to send it
@@ -1061,6 +1067,32 @@ public final class NotificationEntry extends ListEntry {
this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
}
+ /**
+ * Gets the content needed to render this notification as a promoted notification on various
+ * surfaces (like status bar chips and AOD).
+ */
+ public PromotedNotificationContentModel getPromotedNotificationContentModel() {
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ return mPromotedNotificationContentModel;
+ } else {
+ Log.wtf(TAG, "getting promoted content without feature flag enabled");
+ return null;
+ }
+ }
+
+ /**
+ * Sets the content needed to render this notification as a promoted notification on various
+ * surfaces (like status bar chips and AOD).
+ */
+ public void setPromotedNotificationContentModel(
+ @Nullable PromotedNotificationContentModel promotedNotificationContentModel) {
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ this.mPromotedNotificationContentModel = promotedNotificationContentModel;
+ } else {
+ Log.wtf(TAG, "setting promoted content without feature flag enabled");
+ }
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
@@ -1101,4 +1133,6 @@ public final class NotificationEntry extends ListEntry {
private static final long INITIALIZATION_DELAY = 400;
private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
private static final int COLOR_INVALID = 1;
+
+ private static final String TAG = "NotificationEntry";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
index 2fded34a56a0..e2328497d9ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.annotation.SuppressLint
import android.app.NotificationManager
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
@@ -50,7 +51,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* If the setting is enabled, this will track seen notifications and ensure that they only show in
@@ -74,7 +74,7 @@ constructor(
private val unseenNotifications = mutableSetOf<NotificationEntry>()
private var isShadeVisible = false
- private var unseenFilterEnabled = false
+ private var minimalismEnabled = false
override fun attach(pipeline: NotifPipeline) {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) {
@@ -83,7 +83,7 @@ constructor(
pipeline.addPromoter(unseenNotifPromoter)
pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotifs)
pipeline.addCollectionListener(collectionListener)
- scope.launch { trackUnseenFilterSettingChanges() }
+ scope.launch { trackLockScreenNotificationMinimalismSettingChanges() }
dumpManager.registerDumpable(this)
}
@@ -136,12 +136,12 @@ constructor(
return seenNotificationsInteractor.isLockScreenNotificationMinimalismEnabled()
}
- private suspend fun trackUnseenFilterSettingChanges() {
+ private suspend fun trackLockScreenNotificationMinimalismSettingChanges() {
// Only filter the seen notifs when the lock screen minimalism feature settings is on.
minimalismFeatureSettingEnabled().collectLatest { isMinimalismSettingEnabled ->
// update local field and invalidate if necessary
- if (isMinimalismSettingEnabled != unseenFilterEnabled) {
- unseenFilterEnabled = isMinimalismSettingEnabled
+ if (isMinimalismSettingEnabled != minimalismEnabled) {
+ minimalismEnabled = isMinimalismSettingEnabled
unseenNotifications.clear()
unseenNotifPromoter.invalidateList("unseen setting changed")
}
@@ -156,21 +156,21 @@ constructor(
private val collectionListener =
object : NotifCollectionListener {
override fun onEntryAdded(entry: NotificationEntry) {
- if (unseenFilterEnabled && !isShadeVisible) {
+ if (minimalismEnabled && !isShadeVisible) {
logger.logUnseenAdded(entry.key)
unseenNotifications.add(entry)
}
}
override fun onEntryUpdated(entry: NotificationEntry) {
- if (unseenFilterEnabled && !isShadeVisible) {
+ if (minimalismEnabled && !isShadeVisible) {
logger.logUnseenUpdated(entry.key)
unseenNotifications.add(entry)
}
}
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- if (unseenFilterEnabled && unseenNotifications.remove(entry)) {
+ if (minimalismEnabled && unseenNotifications.remove(entry)) {
logger.logUnseenRemoved(entry.key)
}
}
@@ -178,7 +178,7 @@ constructor(
private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
- if (!unseenFilterEnabled) return
+ if (!minimalismEnabled) return
// Only ever elevate a top unseen notification on keyguard, not even locked shade
if (statusBarStateController.state != StatusBarState.KEYGUARD) {
seenNotificationsInteractor.setTopOngoingNotification(null)
@@ -215,6 +215,7 @@ constructor(
override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
when {
NotificationMinimalism.isUnexpectedlyInLegacyMode() -> false
+ !minimalismEnabled -> false
seenNotificationsInteractor.isTopOngoingNotification(child) -> true
!NotificationMinimalism.ungroupTopUnseen -> false
else -> seenNotificationsInteractor.isTopUnseenNotification(child)
@@ -225,6 +226,7 @@ constructor(
object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) {
override fun isInSection(entry: ListEntry): Boolean {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
+ if (!minimalismEnabled) return false
return entry.anyEntry { notificationEntry ->
seenNotificationsInteractor.isTopOngoingNotification(notificationEntry)
}
@@ -235,6 +237,7 @@ constructor(
object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) {
override fun isInSection(entry: ListEntry): Boolean {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
+ if (!minimalismEnabled) return false
return entry.anyEntry { notificationEntry ->
seenNotificationsInteractor.isTopUnseenNotification(notificationEntry)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index de868d45e64f..df8e56eb4102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -33,29 +33,30 @@ import javax.inject.Inject
* they are fully attached.
*/
@CoordinatorScope
-class RowAppearanceCoordinator @Inject internal constructor(
+class RowAppearanceCoordinator
+@Inject
+internal constructor(
@ShadeDisplayAware context: Context,
private var mAssistantFeedbackController: AssistantFeedbackController,
- private var mSectionStyleProvider: SectionStyleProvider
+ private var mSectionStyleProvider: SectionStyleProvider,
) : Coordinator {
private var entryToExpand: NotificationEntry? = null
/**
- * `true` if notifications not part of a group should by default be rendered in their
- * expanded state. If `false`, then only the first notification will be expanded if
- * possible.
+ * `true` if notifications not part of a group should by default be rendered in their expanded
+ * state. If `false`, then only the first notification will be expanded if possible.
*/
private val mAlwaysExpandNonGroupedNotification =
context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications)
/**
- * `true` if the first non-group expandable notification should be expanded automatically
- * when possible. If `false`, then the first non-group expandable notification should not
- * be expanded.
+ * `true` if the first non-group expandable notification should be expanded automatically when
+ * possible. If `false`, then the first non-group expandable notification should not be
+ * expanded.
*/
private val mAutoExpandFirstNotification =
- context.resources.getBoolean(R.bool.config_autoExpandFirstNotification)
+ context.resources.getBoolean(R.bool.config_autoExpandFirstNotification)
override fun attach(pipeline: NotifPipeline) {
pipeline.addOnBeforeRenderListListener(::onBeforeRenderList)
@@ -63,17 +64,20 @@ class RowAppearanceCoordinator @Inject internal constructor(
}
private fun onBeforeRenderList(list: List<ListEntry>) {
- entryToExpand = list.firstOrNull()?.representativeEntry?.takeIf { entry ->
- !mSectionStyleProvider.isMinimizedSection(entry.section!!)
- }
+ entryToExpand =
+ list.firstOrNull()?.representativeEntry?.takeIf { entry ->
+ !mSectionStyleProvider.isMinimizedSection(entry.section!!)
+ }
}
private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
// If mAlwaysExpandNonGroupedNotification is false, then only expand the
// very first notification if it's not a child of grouped notifications and when
// mAutoExpandFirstNotification is true.
- controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification ||
- (mAutoExpandFirstNotification && entry == entryToExpand))
+ controller.setSystemExpanded(
+ mAlwaysExpandNonGroupedNotification ||
+ (mAutoExpandFirstNotification && entry == entryToExpand)
+ )
// Show/hide the feedback icon
controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index db778b801dbc..2d1eccdf1abd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -17,10 +17,12 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.Log
+import com.android.app.tracing.traceSection
import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
import com.android.systemui.statusbar.notification.ColorUpdateLogger
@@ -29,17 +31,17 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Compile
-import com.android.app.tracing.traceSection
-import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
/**
- * A coordinator which ensures that notifications within the new pipeline are correctly inflated
- * for the current uiMode and screen properties; additionally deferring those changes when a user
- * change is in progress until that process has completed.
+ * A coordinator which ensures that notifications within the new pipeline are correctly inflated for
+ * the current uiMode and screen properties; additionally deferring those changes when a user change
+ * is in progress until that process has completed.
*/
@CoordinatorScope
-class ViewConfigCoordinator @Inject internal constructor(
+class ViewConfigCoordinator
+@Inject
+internal constructor(
@ShadeDisplayAware private val mConfigurationController: ConfigurationController,
private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
@@ -52,28 +54,32 @@ class ViewConfigCoordinator @Inject internal constructor(
private var mDispatchUiModeChangeOnUserSwitched = false
private var mPipeline: NotifPipeline? = null
- private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
- override fun onUserSwitching(userId: Int) {
- colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitching()")
- log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
- mIsSwitchingUser = true
- }
+ private val mKeyguardUpdateCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onUserSwitching(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitching()")
+ log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
+ mIsSwitchingUser = true
+ }
- override fun onUserSwitchComplete(userId: Int) {
- colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitchComplete()")
- log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
- mIsSwitchingUser = false
- applyChangesOnUserSwitched()
+ override fun onUserSwitchComplete(userId: Int) {
+ colorUpdateLogger.logTriggerEvent(
+ "VCC.mKeyguardUpdateCallback.onUserSwitchComplete()"
+ )
+ log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
+ mIsSwitchingUser = false
+ applyChangesOnUserSwitched()
+ }
}
- }
- private val mUserChangedListener = object : UserChangedListener {
- override fun onUserChanged(userId: Int) {
- colorUpdateLogger.logTriggerEvent("VCC.mUserChangedListener.onUserChanged()")
- log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
- applyChangesOnUserSwitched()
+ private val mUserChangedListener =
+ object : UserChangedListener {
+ override fun onUserChanged(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mUserChangedListener.onUserChanged()")
+ log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
+ applyChangesOnUserSwitched()
+ }
}
- }
override fun attach(pipeline: NotifPipeline) {
mPipeline = pipeline
@@ -87,8 +93,8 @@ class ViewConfigCoordinator @Inject internal constructor(
log {
val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
"ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
- " isSwitchingUser=$mIsSwitchingUser" +
- " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
+ " isSwitchingUser=$mIsSwitchingUser" +
+ " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
}
MessagingMessage.dropCache()
MessagingGroup.dropCache()
@@ -104,8 +110,8 @@ class ViewConfigCoordinator @Inject internal constructor(
log {
val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
"ViewConfigCoordinator.onUiModeChanged()" +
- " isSwitchingUser=$mIsSwitchingUser" +
- " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
+ " isSwitchingUser=$mIsSwitchingUser" +
+ " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
}
if (!mIsSwitchingUser) {
updateNotificationsOnUiModeChanged()
@@ -132,13 +138,13 @@ class ViewConfigCoordinator @Inject internal constructor(
}
private fun updateNotificationsOnUiModeChanged() {
- colorUpdateLogger.logEvent("VCC.updateNotificationsOnUiModeChanged()",
- "mode=" + mConfigurationController.nightModeName)
+ colorUpdateLogger.logEvent(
+ "VCC.updateNotificationsOnUiModeChanged()",
+ "mode=" + mConfigurationController.nightModeName,
+ )
log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
traceSection("updateNotifOnUiModeChanged") {
- mPipeline?.allNotifs?.forEach { entry ->
- entry.row?.onUiModeChanged()
- }
+ mPipeline?.allNotifs?.forEach { entry -> entry.row?.onUiModeChanged() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index cab4c1c88b2c..3c838e5b707e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection.render
import android.content.Context
import android.view.View
+import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -26,8 +28,6 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumpable
import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.app.tracing.traceSection
-import com.android.systemui.shade.ShadeDisplayAware
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -36,7 +36,9 @@ import dagger.assisted.AssistedInject
* Responsible for building and applying the "shade node spec": the list (tree) of things that
* currently populate the notification shade.
*/
-class ShadeViewManager @AssistedInject constructor(
+class ShadeViewManager
+@AssistedInject
+constructor(
@ShadeDisplayAware context: Context,
@Assisted listContainer: NotificationListContainer,
@Assisted private val stackController: NotifStackController,
@@ -45,13 +47,19 @@ class ShadeViewManager @AssistedInject constructor(
sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
nodeSpecBuilderLogger: NodeSpecBuilderLogger,
shadeViewDifferLogger: ShadeViewDifferLogger,
- private val viewBarn: NotifViewBarn
+ private val viewBarn: NotifViewBarn,
) : PipelineDumpable {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
private val rootController = RootNodeController(listContainer, View(context))
- private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager,
- sectionHeaderVisibilityProvider, viewBarn, nodeSpecBuilderLogger)
+ private val specBuilder =
+ NodeSpecBuilder(
+ mediaContainerController,
+ featureManager,
+ sectionHeaderVisibilityProvider,
+ viewBarn,
+ nodeSpecBuilderLogger,
+ )
private val viewDiffer = ShadeViewDiffer(rootController, shadeViewDifferLogger)
/** Method for attaching this manager to the pipeline. */
@@ -59,34 +67,36 @@ class ShadeViewManager @AssistedInject constructor(
renderStageManager.setViewRenderer(viewRenderer)
}
- override fun dumpPipeline(d: PipelineDumper) = with(d) {
- dump("rootController", rootController)
- dump("specBuilder", specBuilder)
- dump("viewDiffer", viewDiffer)
- }
+ override fun dumpPipeline(d: PipelineDumper) =
+ with(d) {
+ dump("rootController", rootController)
+ dump("specBuilder", specBuilder)
+ dump("viewDiffer", viewDiffer)
+ }
- private val viewRenderer = object : NotifViewRenderer {
+ private val viewRenderer =
+ object : NotifViewRenderer {
- override fun onRenderList(notifList: List<ListEntry>) {
- traceSection("ShadeViewManager.onRenderList") {
- viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
+ override fun onRenderList(notifList: List<ListEntry>) {
+ traceSection("ShadeViewManager.onRenderList") {
+ viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
+ }
}
- }
- override fun getStackController(): NotifStackController = stackController
+ override fun getStackController(): NotifStackController = stackController
- override fun getGroupController(group: GroupEntry): NotifGroupController =
- viewBarn.requireGroupController(group.requireSummary)
+ override fun getGroupController(group: GroupEntry): NotifGroupController =
+ viewBarn.requireGroupController(group.requireSummary)
- override fun getRowController(entry: NotificationEntry): NotifRowController =
- viewBarn.requireRowController(entry)
- }
+ override fun getRowController(entry: NotificationEntry): NotifRowController =
+ viewBarn.requireRowController(entry)
+ }
}
@AssistedFactory
interface ShadeViewManagerFactory {
fun create(
listContainer: NotificationListContainer,
- stackController: NotifStackController
+ stackController: NotifStackController,
): ShadeViewManager
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
index 925d4a588f09..4c2512988f4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/ReferenceNotificationsModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.dagger
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsModule
import com.android.systemui.statusbar.notification.row.NotificationRowModule
import dagger.Module
@@ -23,5 +24,12 @@ import dagger.Module
* A module that includes the standard notifications classes that most SysUI variants need. Variants
* are free to not include this module and instead write a custom notifications module.
*/
-@Module(includes = [NotificationsModule::class, NotificationRowModule::class])
+@Module(
+ includes =
+ [
+ NotificationsModule::class,
+ NotificationRowModule::class,
+ PromotedNotificationsModule::class,
+ ]
+)
object ReferenceNotificationsModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
index 7d374b051b76..dbe58337de11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.notification.data
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.settings.SecureSettingsRepositoryModule
-import com.android.systemui.settings.SystemSettingsRepositoryModule
+import com.android.systemui.settings.UserSettingsRepositoryModule
import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
@@ -32,9 +32,8 @@ import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
-@Module(includes = [SecureSettingsRepositoryModule::class, SystemSettingsRepositoryModule::class])
+@Module(includes = [UserSettingsRepositoryModule::class])
object NotificationSettingsRepositoryModule {
@Provides
@SysUISingleton
@@ -48,7 +47,8 @@ object NotificationSettingsRepositoryModule {
backgroundScope,
backgroundDispatcher,
secureSettingsRepository,
- systemSettingsRepository)
+ systemSettingsRepository,
+ )
@Provides
@IntoMap
@@ -57,7 +57,7 @@ object NotificationSettingsRepositoryModule {
fun provideCoreStartable(
@Application applicationScope: CoroutineScope,
repository: NotificationSettingsRepository,
- logger: VisualInterruptionDecisionLogger
+ logger: VisualInterruptionDecisionLogger,
) = CoreStartable {
applicationScope.launch {
repository.isCooldownEnabled.collect { value -> logger.logCooldownSetting(value) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 697a6ce52ba9..cff5bef9fe69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -83,6 +83,9 @@ constructor(
// TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
// instead of being separate.
topLevelRepresentativeNotifications
+ .map { notifs -> notifs.filter { it.isPromoted } }
+ .distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
} else {
flowOf(emptyList())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 10084517ec19..8edbc5e8e4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -33,6 +33,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -50,6 +52,7 @@ class RenderNotificationListInteractor
constructor(
private val repository: ActiveNotificationListRepository,
private val sectionStyleProvider: SectionStyleProvider,
+ private val promotedNotificationsProvider: PromotedNotificationsProvider,
) {
/**
* Sets the current list of rendered notification entries as displayed in the notification list.
@@ -57,7 +60,11 @@ constructor(
fun setRenderedList(entries: List<ListEntry>) {
traceSection("RenderNotificationListInteractor.setRenderedList") {
repository.activeNotifications.update { existingModels ->
- buildActiveNotificationsStore(existingModels, sectionStyleProvider) {
+ buildActiveNotificationsStore(
+ existingModels,
+ sectionStyleProvider,
+ promotedNotificationsProvider,
+ ) {
entries.forEach(::addListEntry)
setRankingsMap(entries)
}
@@ -69,13 +76,21 @@ constructor(
private fun buildActiveNotificationsStore(
existingModels: ActiveNotificationsStore,
sectionStyleProvider: SectionStyleProvider,
- block: ActiveNotificationsStoreBuilder.() -> Unit
+ promotedNotificationsProvider: PromotedNotificationsProvider,
+ block: ActiveNotificationsStoreBuilder.() -> Unit,
): ActiveNotificationsStore =
- ActiveNotificationsStoreBuilder(existingModels, sectionStyleProvider).apply(block).build()
+ ActiveNotificationsStoreBuilder(
+ existingModels,
+ sectionStyleProvider,
+ promotedNotificationsProvider,
+ )
+ .apply(block)
+ .build()
private class ActiveNotificationsStoreBuilder(
private val existingModels: ActiveNotificationsStore,
private val sectionStyleProvider: SectionStyleProvider,
+ private val promotedNotificationsProvider: PromotedNotificationsProvider,
) {
private val builder = ActiveNotificationsStore.Builder()
@@ -96,7 +111,7 @@ private class ActiveNotificationsStoreBuilder(
existingModels.createOrReuse(
key = entry.key,
summary = summaryModel,
- children = childModels
+ children = childModels,
)
)
}
@@ -141,6 +156,7 @@ private class ActiveNotificationsStoreBuilder(
key = key,
groupKey = sbn.groupKey,
whenTime = sbn.notification.`when`,
+ isPromoted = promotedNotificationsProvider.shouldPromote(this),
isAmbient = sectionStyleProvider.isMinimized(this),
isRowDismissed = isRowDismissed,
isSilent = sectionStyleProvider.isSilent(this),
@@ -158,6 +174,7 @@ private class ActiveNotificationsStoreBuilder(
isGroupSummary = sbn.notification.isGroupSummary,
bucket = bucket,
callType = sbn.toCallType(),
+ promotedContent = promotedNotificationContentModel,
)
}
}
@@ -166,6 +183,7 @@ private fun ActiveNotificationsStore.createOrReuse(
key: String,
groupKey: String?,
whenTime: Long,
+ isPromoted: Boolean,
isAmbient: Boolean,
isRowDismissed: Boolean,
isSilent: Boolean,
@@ -183,12 +201,14 @@ private fun ActiveNotificationsStore.createOrReuse(
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
+ promotedContent: PromotedNotificationContentModel?,
): ActiveNotificationModel {
return individuals[key]?.takeIf {
it.isCurrent(
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isPromoted = isPromoted,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -206,12 +226,14 @@ private fun ActiveNotificationsStore.createOrReuse(
contentIntent = contentIntent,
bucket = bucket,
callType = callType,
+ promotedContent = promotedContent,
)
}
?: ActiveNotificationModel(
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isPromoted = isPromoted,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -229,6 +251,7 @@ private fun ActiveNotificationsStore.createOrReuse(
contentIntent = contentIntent,
bucket = bucket,
callType = callType,
+ promotedContent = promotedContent,
)
}
@@ -236,6 +259,7 @@ private fun ActiveNotificationModel.isCurrent(
key: String,
groupKey: String?,
whenTime: Long,
+ isPromoted: Boolean,
isAmbient: Boolean,
isRowDismissed: Boolean,
isSilent: Boolean,
@@ -253,11 +277,13 @@ private fun ActiveNotificationModel.isCurrent(
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
+ promotedContent: PromotedNotificationContentModel?,
): Boolean {
return when {
key != this.key -> false
groupKey != this.groupKey -> false
whenTime != this.whenTime -> false
+ isPromoted != this.isPromoted -> false
isAmbient != this.isAmbient -> false
isRowDismissed != this.isRowDismissed -> false
isSilent != this.isSilent -> false
@@ -275,6 +301,9 @@ private fun ActiveNotificationModel.isCurrent(
contentIntent != this.contentIntent -> false
bucket != this.bucket -> false
callType != this.callType -> false
+ // QQQ: Do we need to do the same `isCurrent` thing within the content model to avoid
+ // recreating the active notification model constantly?
+ promotedContent != this.promotedContent -> false
else -> true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 96f47e5fdd52..a0515ca92cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -459,8 +459,12 @@ public class FooterView extends StackScrollerDecorView {
Resources.Theme theme = mContext.getTheme();
final @ColorInt int onSurface = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.materialColorOnSurface);
+ // Same resource, separate drawables to prevent touch effects from showing on the wrong
+ // button.
final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
- final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+ final Drawable settingsBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+ final Drawable historyBg = NotifRedesignFooter.isEnabled()
+ ? theme.getDrawable(R.drawable.notif_footer_btn_background) : null;
final @ColorInt int scHigh;
if (!notificationFooterBackgroundTintOptimization()) {
scHigh = Utils.getColorAttrDefaultColor(mContext,
@@ -468,7 +472,10 @@ public class FooterView extends StackScrollerDecorView {
if (scHigh != 0) {
final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
clearAllBg.setColorFilter(bgColorFilter);
- manageBg.setColorFilter(bgColorFilter);
+ settingsBg.setColorFilter(bgColorFilter);
+ if (NotifRedesignFooter.isEnabled()) {
+ historyBg.setColorFilter(bgColorFilter);
+ }
}
} else {
scHigh = 0;
@@ -476,13 +483,13 @@ public class FooterView extends StackScrollerDecorView {
mClearAllButton.setBackground(clearAllBg);
mClearAllButton.setTextColor(onSurface);
if (NotifRedesignFooter.isEnabled()) {
- mSettingsButton.setBackground(manageBg);
+ mSettingsButton.setBackground(settingsBg);
mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
- mHistoryButton.setBackground(manageBg);
+ mHistoryButton.setBackground(historyBg);
mHistoryButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
} else {
- mManageOrHistoryButton.setBackground(manageBg);
+ mManageOrHistoryButton.setBackground(settingsBg);
mManageOrHistoryButton.setTextColor(onSurface);
}
mSeenNotifsFooterTextView.setTextColor(onSurface);
@@ -492,7 +499,7 @@ public class FooterView extends StackScrollerDecorView {
colorUpdateLogger.logEvent("Footer.updateColors()",
"textColor(onSurface)=" + hexColorString(onSurface)
+ " backgroundTint(surfaceContainerHigh)=" + hexColorString(scHigh)
- + " background=" + DrawableDumpKt.dumpToString(manageBg));
+ + " background=" + DrawableDumpKt.dumpToString(settingsBg));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
index 16d0cc42db7f..3c8c42f6b29d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.icon
import android.app.Notification
import android.content.Context
-import android.graphics.drawable.Drawable
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.contentDescForNotification
@@ -30,15 +29,11 @@ class IconBuilder @Inject constructor(private val context: Context) {
return StatusBarIconView(
context,
"${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
- entry.sbn
+ entry.sbn,
)
}
fun getIconContentDescription(n: Notification): CharSequence {
return contentDescForNotification(context, n)
}
-
- fun getAppIcon(n: Notification): Drawable {
- return n.loadHeaderAppIcon(context)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index db804835f260..47171948f395 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -26,6 +26,7 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.Flags
@@ -44,7 +45,6 @@ import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/**
@@ -152,13 +152,7 @@ constructor(
setIcon(entry, sensitiveIconDescriptor, shelfIcon)
setIcon(entry, sensitiveIconDescriptor, aodIcon)
entry.icons =
- IconPack.buildPack(
- sbIcon,
- sbChipIcon,
- shelfIcon,
- aodIcon,
- entry.icons,
- )
+ IconPack.buildPack(sbIcon, sbChipIcon, shelfIcon, aodIcon, entry.icons)
} catch (e: InflationException) {
entry.icons = IconPack.buildEmptyPack(entry.icons)
throw e
@@ -182,7 +176,7 @@ constructor(
Log.wtf(
TAG,
"Updating using the cache is not supported when the " +
- "notifications_background_icons flag is off"
+ "notifications_background_icons flag is off",
)
}
if (!usingCache || !Flags.notificationsBackgroundIcons()) {
@@ -249,10 +243,6 @@ constructor(
val (icon: Icon?, type: StatusBarIcon.Type) =
if (showPeopleAvatar) {
createPeopleAvatar(entry) to StatusBarIcon.Type.PeopleAvatar
- } else if (
- android.app.Flags.notificationsUseMonochromeAppIcon() && n.shouldUseAppIcon()
- ) {
- n.smallIcon to StatusBarIcon.Type.MaybeMonochromeAppIcon
} else {
n.smallIcon to StatusBarIcon.Type.NotifSmallIcon
}
@@ -267,33 +257,25 @@ constructor(
private fun getCachedIconDescriptor(
entry: NotificationEntry,
- showPeopleAvatar: Boolean
+ showPeopleAvatar: Boolean,
): StatusBarIcon? {
val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor
- val appIconDescriptor = entry.icons.appIconDescriptor
val smallIconDescriptor = entry.icons.smallIconDescriptor
// If cached, return corresponding cached values
return when {
showPeopleAvatar && peopleAvatarDescriptor != null -> peopleAvatarDescriptor
- android.app.Flags.notificationsUseMonochromeAppIcon() && appIconDescriptor != null ->
- appIconDescriptor
smallIconDescriptor != null -> smallIconDescriptor
else -> null
}
}
private fun cacheIconDescriptor(entry: NotificationEntry, descriptor: StatusBarIcon) {
- if (
- android.app.Flags.notificationsUseAppIcon() ||
- android.app.Flags.notificationsUseMonochromeAppIcon()
- ) {
- // If either of the new icon flags is enabled, we cache the icon all the time.
+ if (android.app.Flags.notificationsRedesignAppIcons()) {
+ // Although we're not actually using the app icon in the status bar, let's make sure
+ // we cache the icon all the time when the flag is on.
when (descriptor.type) {
StatusBarIcon.Type.PeopleAvatar -> entry.icons.peopleAvatarDescriptor = descriptor
- // When notificationsUseMonochromeAppIcon is enabled, we use the appIconDescriptor.
- StatusBarIcon.Type.MaybeMonochromeAppIcon ->
- entry.icons.appIconDescriptor = descriptor
// When notificationsUseAppIcon is enabled, the app icon overrides the small icon.
// But either way, it's a good idea to cache the descriptor.
else -> entry.icons.smallIconDescriptor = descriptor
@@ -312,7 +294,7 @@ constructor(
private fun setIcon(
entry: NotificationEntry,
iconDescriptor: StatusBarIcon,
- iconView: StatusBarIconView
+ iconView: StatusBarIconView,
) {
iconView.setShowsConversation(showsConversation(entry, iconView, iconDescriptor))
iconView.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP)
@@ -323,7 +305,7 @@ constructor(
private fun Icon.toStatusBarIcon(
entry: NotificationEntry,
- type: StatusBarIcon.Type
+ type: StatusBarIcon.Type,
): StatusBarIcon {
val n = entry.sbn.notification
return StatusBarIcon(
@@ -333,7 +315,7 @@ constructor(
n.iconLevel,
n.number,
iconBuilder.getIconContentDescription(n),
- type
+ type,
)
}
@@ -347,7 +329,7 @@ constructor(
} catch (e: Exception) {
Log.e(
TAG,
- "Error calling LauncherApps#getShortcutIcon for notification $entry: $e"
+ "Error calling LauncherApps#getShortcutIcon for notification $entry: $e",
)
}
}
@@ -431,7 +413,7 @@ constructor(
private fun showsConversation(
entry: NotificationEntry,
iconView: StatusBarIconView,
- iconDescriptor: StatusBarIcon
+ iconDescriptor: StatusBarIcon,
): Boolean {
val usedInSensitiveContext =
iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
index 611cebcf6427..cb6be661c7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -34,7 +34,6 @@ public final class IconPack {
@Nullable private final StatusBarIconView mAodIcon;
@Nullable private StatusBarIcon mSmallIconDescriptor;
- @Nullable private StatusBarIcon mAppIconDescriptor;
@Nullable private StatusBarIcon mPeopleAvatarDescriptor;
private boolean mIsImportantConversation;
@@ -127,15 +126,6 @@ public final class IconPack {
mPeopleAvatarDescriptor = peopleAvatarDescriptor;
}
- @Nullable
- StatusBarIcon getAppIconDescriptor() {
- return mAppIconDescriptor;
- }
-
- void setAppIconDescriptor(@Nullable StatusBarIcon appIconDescriptor) {
- mAppIconDescriptor = appIconDescriptor;
- }
-
boolean isImportantConversation() {
return mIsImportantConversation;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
index 663588c8f8c8..fc432ba973ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -17,11 +17,13 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
@@ -30,7 +32,6 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
class NotificationIconContainerAlwaysOnDisplayViewBinder
@@ -38,7 +39,7 @@ class NotificationIconContainerAlwaysOnDisplayViewBinder
constructor(
private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
private val screenOffAnimationController: ScreenOffAnimationController,
private val systemBarUtilsState: SystemBarUtilsState,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
index 4e40888ee88a..5432f1448902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.bindIcons
@@ -30,7 +31,7 @@ class NotificationIconContainerShelfViewBinder
@Inject
constructor(
private val viewModel: NotificationIconContainerShelfViewModel,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val systemBarUtilsState: SystemBarUtilsState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
private val viewStore: ShelfNotificationIconViewStore,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index f0f529e2c615..a21dabb821d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -17,9 +17,11 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
@@ -27,23 +29,23 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.ui.SystemBarUtilsState
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a [NotificationIconContainer] to a [NotificationIconContainerStatusBarViewModel]. */
class NotificationIconContainerStatusBarViewBinder
@Inject
constructor(
private val viewModel: NotificationIconContainerStatusBarViewModel,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val systemBarUtilsState: SystemBarUtilsState,
private val failureTracker: StatusBarIconViewBindingFailureTracker,
private val viewStore: StatusBarNotificationIconViewStore,
) {
- fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
+ fun bindWhileAttached(view: NotificationIconContainer, displayId: Int): DisposableHandle {
return traceSection("NICStatusBar#bindWhileAttached") {
view.repeatWhenAttached {
lifecycleScope.launch {
NotificationIconContainerViewBinder.bind(
+ displayId = displayId,
view = view,
viewModel = viewModel,
configuration = configuration,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 063fe45cd199..6dbb71463602 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -24,6 +24,7 @@ import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.internal.R as RInternal
import com.android.internal.statusbar.StatusBarIcon
@@ -54,12 +55,12 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a view-model to a [NotificationIconContainer]. */
object NotificationIconContainerViewBinder {
suspend fun bind(
+ displayId: Int,
view: NotificationIconContainer,
viewModel: NotificationIconContainerStatusBarViewModel,
configuration: ConfigurationState,
@@ -70,7 +71,10 @@ object NotificationIconContainerViewBinder {
launch {
val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
val iconColors: StateFlow<NotificationIconColors> =
- viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }.stateIn(this)
+ viewModel
+ .iconColors(displayId)
+ .mapNotNull { it.iconColors(view.viewBounds) }
+ .stateIn(this)
viewModel.icons.bindIcons(
logTag = "statusbar",
view = view,
@@ -79,11 +83,7 @@ object NotificationIconContainerViewBinder {
notifyBindingFailures = { failureTracker.statusBarFailures = it },
viewStore = viewStore,
) { _, sbiv ->
- StatusBarIconViewBinder.bindIconColors(
- sbiv,
- iconColors,
- contrastColorUtil,
- )
+ StatusBarIconViewBinder.bindIconColors(sbiv, iconColors, contrastColorUtil)
}
}
launch { viewModel.bindIsolatedIcon(view, viewStore) }
@@ -194,8 +194,7 @@ object NotificationIconContainerViewBinder {
combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) {
iconSize,
iconHPadding,
- statusBarHeight,
- ->
+ statusBarHeight ->
FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
}
.stateIn(this)
@@ -251,10 +250,7 @@ object NotificationIconContainerViewBinder {
traceSection("addIcon") {
(sbiv.parent as? ViewGroup)?.run {
if (this !== view) {
- Log.wtf(
- TAG,
- "[$logTag] SBIV($notifKey) has an unexpected parent",
- )
+ Log.wtf(TAG, "[$logTag] SBIV($notifKey) has an unexpected parent")
}
// If the container was re-inflated and re-bound, then SBIVs might still
// be attached to the prior view.
@@ -271,7 +267,7 @@ object NotificationIconContainerViewBinder {
launch {
launch {
layoutParams.collectTracingEach(
- tag = { "[$logTag] SBIV#bindLayoutParams" },
+ tag = { "[$logTag] SBIV#bindLayoutParams" }
) {
if (it != sbiv.layoutParams) {
sbiv.layoutParams = it
@@ -344,7 +340,7 @@ object NotificationIconContainerViewBinder {
// a single SBIV instance for the group. Then this whole concept can go away.
private inline fun <R> NotificationIconContainer.withIconReplacements(
replacements: ArrayMap<String, StatusBarIcon>,
- block: () -> R
+ block: () -> R,
): R {
setReplacingIcons(replacements)
return block().also { setReplacingIcons(null) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
index 6b5642af3f10..83f56a092bc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
@@ -20,7 +20,6 @@ import android.graphics.Rect
import android.view.View
import com.android.app.tracing.traceSection
import com.android.internal.util.ContrastColorUtil
-import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.NO_COLOR
@@ -36,11 +35,9 @@ object StatusBarIconViewBinder {
suspend fun bindColor(view: StatusBarIconView, color: Flow<Int>) {
color.collectTracingEach("SBIV#bindColor") { color ->
- // Don't change the icon color if an app icon experiment is enabled.
- if (!android.app.Flags.notificationsUseAppIcon()) {
- view.staticDrawableColor = color
- }
- // Continue changing the overflow dot color
+ // Set the color for the icons
+ view.staticDrawableColor = color
+ // Set the color for the overflow dot
view.setDecorColor(color)
}
}
@@ -59,14 +56,12 @@ object StatusBarIconViewBinder {
contrastColorUtil: ContrastColorUtil,
) {
iconColors.collectTracingEach("SBIV#bindIconColors") { colors ->
- // Don't change the icon color if an app icon experiment is enabled.
- if (!android.app.Flags.notificationsUseAppIcon()) {
- val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
- val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
- view.staticDrawableColor =
- if (isColorized) colors.staticDrawableColor(view.viewBounds) else NO_COLOR
- }
- // Continue changing the overflow dot color
+ // Set the icon color
+ val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
+ val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
+ view.staticDrawableColor =
+ if (isColorized) colors.staticDrawableColor(view.viewBounds) else NO_COLOR
+ // Set the color for the overflow dot
view.setDecorColor(colors.tint)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index a64f888cb238..f0b03065e637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -45,8 +45,8 @@ import kotlinx.coroutines.flow.map
class NotificationIconContainerStatusBarViewModel
@Inject
constructor(
- @Background bgContext: CoroutineContext,
- darkIconInteractor: DarkIconInteractor,
+ @Background private val bgContext: CoroutineContext,
+ private val darkIconInteractor: DarkIconInteractor,
iconsInteractor: StatusBarNotificationIconsInteractor,
headsUpIconInteractor: HeadsUpNotificationIconInteractor,
keyguardInteractor: KeyguardInteractor,
@@ -58,10 +58,9 @@ constructor(
/** Are changes to the icon container animated? */
val animationsEnabled: Flow<Boolean> =
- combine(
- shadeInteractor.isShadeTouchable,
- keyguardInteractor.isKeyguardShowing,
- ) { panelTouchesEnabled, isKeyguardShowing ->
+ combine(shadeInteractor.isShadeTouchable, keyguardInteractor.isKeyguardShowing) {
+ panelTouchesEnabled,
+ isKeyguardShowing ->
panelTouchesEnabled && !isKeyguardShowing
}
.flowOn(bgContext)
@@ -69,8 +68,9 @@ constructor(
.distinctUntilChanged()
/** The colors with which to display the notification icons. */
- val iconColors: Flow<NotificationIconColorLookup> =
- darkIconInteractor.darkState
+ fun iconColors(displayId: Int): Flow<NotificationIconColorLookup> =
+ darkIconInteractor
+ .darkState(displayId)
.map { (areas: Collection<Rect>, tint: Int) ->
NotificationIconColorLookup { viewBounds: Rect ->
if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
@@ -125,10 +125,8 @@ constructor(
val isolatedIconLocation: Flow<Rect> =
headsUpIconInteractor.isolatedIconLocation.filterNotNull().conflate().distinctUntilChanged()
- private class IconColorsImpl(
- override val tint: Int,
- private val areas: Collection<Rect>,
- ) : NotificationIconColors {
+ private class IconColorsImpl(override val tint: Int, private val areas: Collection<Rect>) :
+ NotificationIconColors {
override fun staticDrawableColor(viewBounds: Rect): Int {
return if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 71cddc99b564..52336be742cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -75,7 +75,7 @@ constructor(
private val bubbles: Optional<Bubbles>,
@ShadeDisplayAware private val context: Context,
private val notificationManager: NotificationManager,
- private val settingsInteractor: NotificationSettingsInteractor
+ private val settingsInteractor: NotificationSettingsInteractor,
) : VisualInterruptionDecisionProvider {
init {
@@ -89,7 +89,7 @@ constructor(
private class DecisionImpl(
override val shouldInterrupt: Boolean,
- override val logReason: String
+ override val logReason: String,
) : Decision
private data class LoggableDecision
@@ -107,7 +107,7 @@ constructor(
LoggableDecision(
DecisionImpl(
shouldInterrupt = false,
- logReason = "${legacySuppressor.name}.$methodName"
+ logReason = "${legacySuppressor.name}.$methodName",
)
)
@@ -123,7 +123,7 @@ constructor(
private class FullScreenIntentDecisionImpl(
val entry: NotificationEntry,
- private val fsiDecision: FullScreenIntentDecisionProvider.Decision
+ private val fsiDecision: FullScreenIntentDecisionProvider.Decision,
) : FullScreenIntentDecision, Loggable {
var hasBeenLogged = false
@@ -154,7 +154,7 @@ constructor(
deviceProvisionedController,
keyguardStateController,
powerManager,
- statusBarStateController
+ statusBarStateController,
)
private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>()
@@ -197,7 +197,7 @@ constructor(
context,
notificationManager,
logger,
- systemSettings
+ systemSettings,
)
)
avalancheProvider.register()
@@ -290,7 +290,7 @@ constructor(
private fun logDecision(
type: VisualInterruptionType,
entry: NotificationEntry,
- loggableDecision: LoggableDecision
+ loggableDecision: LoggableDecision,
) {
if (!loggableDecision.isSpammy || logger.spew) {
logger.logDecision(type.name, entry, loggableDecision.decision)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt
new file mode 100644
index 000000000000..632421980772
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationUi.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted
+
+import android.app.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the promoted ongoing notifications UI flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object PromotedNotificationUi {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_UI_RICH_ONGOING
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.uiRichOngoing()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt
new file mode 100644
index 000000000000..4be12bd44a29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsModule.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class PromotedNotificationsModule {
+ @Binds
+ @SysUISingleton
+ abstract fun bindPromotedNotificationsProvider(
+ impl: PromotedNotificationsProviderImpl
+ ): PromotedNotificationsProvider
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
new file mode 100644
index 000000000000..691dc6f5ccac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted
+
+import android.app.Notification.FLAG_PROMOTED_ONGOING
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/** A provider for making decisions on which notifications should be promoted. */
+interface PromotedNotificationsProvider {
+ /** Returns true if the given notification should be promoted and false otherwise. */
+ fun shouldPromote(entry: NotificationEntry): Boolean
+}
+
+@SysUISingleton
+open class PromotedNotificationsProviderImpl @Inject constructor() : PromotedNotificationsProvider {
+ override fun shouldPromote(entry: NotificationEntry): Boolean {
+ if (!PromotedNotificationUi.isEnabled) {
+ return false
+ }
+ return (entry.sbn.notification.flags and FLAG_PROMOTED_ONGOING) != 0
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
new file mode 100644
index 000000000000..41ee3b992c5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.shared.model
+
+import android.annotation.DrawableRes
+import android.graphics.drawable.Icon
+import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+
+/**
+ * The content needed to render a promoted notification to surfaces besides the notification stack,
+ * like the skeleton view on AOD or the status bar chip.
+ */
+data class PromotedNotificationContentModel(
+ val key: String,
+
+ // for all styles:
+ val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val appName: CharSequence?,
+ val subText: CharSequence?,
+ val time: When?,
+ val lastAudiblyAlertedMs: Long,
+ @DrawableRes val profileBadgeResId: Int?,
+ val title: CharSequence?,
+ val text: CharSequence?,
+ val skeletonLargeIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val style: Style,
+
+ // for CallStyle:
+ val personIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val personName: CharSequence?,
+ val verificationIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val verificationText: CharSequence?,
+
+ // for ProgressStyle:
+ val progress: NotificationProgressModel?,
+) {
+ class Builder(val key: String) {
+ var skeletonSmallIcon: Icon? = null
+ var appName: CharSequence? = null
+ var subText: CharSequence? = null
+ var time: When? = null
+ var lastAudiblyAlertedMs: Long = 0L
+ @DrawableRes var profileBadgeResId: Int? = null
+ var title: CharSequence? = null
+ var text: CharSequence? = null
+ var skeletonLargeIcon: Icon? = null
+ var style: Style = Style.Ineligible
+
+ // for CallStyle:
+ var personIcon: Icon? = null
+ var personName: CharSequence? = null
+ var verificationIcon: Icon? = null
+ var verificationText: CharSequence? = null
+
+ // for ProgressStyle:
+ var progress: NotificationProgressModel? = null
+
+ fun build() =
+ PromotedNotificationContentModel(
+ key = key,
+ skeletonSmallIcon = skeletonSmallIcon,
+ appName = appName,
+ subText = subText,
+ time = time,
+ lastAudiblyAlertedMs = lastAudiblyAlertedMs,
+ profileBadgeResId = profileBadgeResId,
+ title = title,
+ text = text,
+ skeletonLargeIcon = skeletonLargeIcon,
+ style = style,
+ personIcon = personIcon,
+ personName = personName,
+ verificationIcon = verificationIcon,
+ verificationText = verificationText,
+ progress = progress,
+ )
+ }
+
+ /** The timestamp associated with a notification, along with the mode used to display it. */
+ data class When(val time: Long, val mode: Mode) {
+ /** The mode used to display a notification's `when` value. */
+ enum class Mode {
+ Absolute,
+ CountDown,
+ CountUp,
+ }
+ }
+
+ /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */
+ enum class Style {
+ BigPicture,
+ BigText,
+ Call,
+ Progress,
+ Ineligible,
+ }
+
+ companion object {
+ @JvmStatic
+ fun featureFlagEnabled(): Boolean =
+ PromotedNotificationUi.isEnabled || StatusBarNotifChips.isEnabled
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
index e233deffe42f..916414548fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -25,6 +25,7 @@ import android.util.Dumpable
import android.util.Log
import android.util.Size
import androidx.annotation.MainThread
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.R
import com.android.internal.widget.NotificationDrawableConsumer
import com.android.internal.widget.NotificationIconManager
@@ -45,7 +46,6 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
private const val TAG = "BigPicImageLoader"
@@ -67,7 +67,7 @@ constructor(
private val statsManager: BigPictureStatsManager,
@Application private val scope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
- @Background private val bgDispatcher: CoroutineDispatcher
+ @Background private val bgDispatcher: CoroutineDispatcher,
) : NotificationIconManager, Dumpable {
private var lastLoadingJob: Job? = null
@@ -153,7 +153,7 @@ constructor(
private fun checkPlaceHolderSizeForDrawable(
displayedState: DrawableState,
- newDrawable: Drawable
+ newDrawable: Drawable,
) {
if (displayedState is PlaceHolder) {
val (oldWidth, oldHeight) = displayedState.drawableSize
@@ -163,7 +163,7 @@ constructor(
Log.e(
TAG,
"Mismatch in dimensions, when replacing PlaceHolder " +
- "$oldWidth X $oldHeight with Drawable $newWidth X $newHeight."
+ "$oldWidth X $oldHeight with Drawable $newWidth X $newHeight.",
)
}
}
@@ -184,9 +184,8 @@ constructor(
displayedState = drawableAndState?.second ?: Empty
}
- private fun startLoadingJob(icon: Icon): Job = scope.launch {
- statsManager.measure { loadImage(icon) }
- }
+ private fun startLoadingJob(icon: Icon): Job =
+ scope.launch { statsManager.measure { loadImage(icon) } }
private suspend fun loadImage(icon: Icon) {
val drawableAndState = withContext(bgDispatcher) { loadImageSync(icon) }
@@ -254,9 +253,12 @@ constructor(
private sealed class DrawableState(open val icon: Icon?) {
data object Initial : DrawableState(null)
+
data object Empty : DrawableState(null)
+
data class PlaceHolder(override val icon: Icon, val drawableSize: Size) :
DrawableState(icon)
+
data class FullImage(override val icon: Icon, val drawableSize: Size) : DrawableState(icon)
}
}
@@ -298,7 +300,7 @@ private fun Size.resizeToMax(maxWidth: Int, maxHeight: Int): Size {
}
private val Drawable.intrinsicSize
- get() = Size(/*width=*/ intrinsicWidth, /*height=*/ intrinsicHeight)
+ get() = Size(/* width= */ intrinsicWidth, /* height= */ intrinsicHeight)
private operator fun Size.component1() = width
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index b166defb96a2..2dcb706234b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -95,7 +95,7 @@ constructor(
private val smartReplyStateInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
private val headsUpStyleProvider: HeadsUpStyleProvider,
- private val logger: NotificationRowContentBinderLogger
+ private val logger: NotificationRowContentBinderLogger,
) : NotificationRowContentBinder {
init {
@@ -110,7 +110,7 @@ constructor(
@InflationFlag contentToBind: Int,
bindParams: BindParams,
forceInflate: Boolean,
- callback: InflationCallback?
+ callback: InflationCallback?,
) {
if (row.isRemoved) {
// We don't want to reinflate anything for removed notifications. Otherwise views might
@@ -147,7 +147,7 @@ constructor(
/* isMediaFlagEnabled = */ smartReplyStateInflater,
notifLayoutInflaterFactoryProvider,
headsUpStyleProvider,
- logger
+ logger,
)
if (inflateSynchronously) {
task.onPostExecute(task.doInBackground())
@@ -165,7 +165,7 @@ constructor(
@InflationFlag reInflateFlags: Int,
builder: Notification.Builder,
packageContext: Context,
- smartRepliesInflater: SmartReplyStateInflater
+ smartRepliesInflater: SmartReplyStateInflater,
): InflationProgress {
val systemUIContext = row.context
val result =
@@ -229,7 +229,7 @@ constructor(
row,
remoteInputManager.remoteViewsOnClickHandler,
/* callback= */ null,
- logger
+ logger,
)
return result
}
@@ -246,7 +246,7 @@ constructor(
override fun unbindContent(
entry: NotificationEntry,
row: ExpandableNotificationRow,
- @InflationFlag contentToUnbind: Int
+ @InflationFlag contentToUnbind: Int,
) {
logger.logUnbinding(entry, contentToUnbind)
var curFlag = 1
@@ -268,7 +268,7 @@ constructor(
private fun freeNotificationView(
entry: NotificationEntry,
row: ExpandableNotificationRow,
- @InflationFlag inflateFlag: Int
+ @InflationFlag inflateFlag: Int,
) {
when (inflateFlag) {
FLAG_CONTENT_VIEW_CONTRACTED ->
@@ -319,7 +319,7 @@ constructor(
*/
private fun cancelContentViewFrees(
row: ExpandableNotificationRow,
- @InflationFlag contentViews: Int
+ @InflationFlag contentViews: Int,
) {
if (contentViews and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED)
@@ -372,7 +372,7 @@ constructor(
private val smartRepliesInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
private val headsUpStyleProvider: HeadsUpStyleProvider,
- private val logger: NotificationRowContentBinderLogger
+ private val logger: NotificationRowContentBinderLogger,
) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask {
private val context: Context
get() = row.context
@@ -393,7 +393,7 @@ constructor(
context.packageManager.getApplicationInfoAsUser(
packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES,
- userId
+ userId,
)
} catch (e: PackageManager.NameNotFoundException) {
return
@@ -442,11 +442,11 @@ constructor(
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
conversationProcessor = conversationProcessor,
- logger = logger
+ logger = logger,
)
logger.logAsyncTaskProgress(
entry,
- "getting existing smart reply state (on wrong thread!)"
+ "getting existing smart reply state (on wrong thread!)",
)
val previousSmartReplyState: InflatedSmartReplyState? = row.existingSmartReplyState
logger.logAsyncTaskProgress(entry, "inflating smart reply views")
@@ -469,7 +469,7 @@ constructor(
reInflateFlags,
entry,
context,
- logger
+ logger,
)
}
}
@@ -483,7 +483,7 @@ constructor(
reInflateFlags,
entry,
context,
- logger
+ logger,
)
}
}
@@ -513,7 +513,7 @@ constructor(
row,
remoteViewClickHandler,
this /* callback */,
- logger
+ logger,
)
}
.onFailure { error -> handleError(error as Exception) }
@@ -530,7 +530,7 @@ constructor(
Log.e(TAG, "couldn't inflate view for notification $ident", e)
callback?.handleInflationException(
row.entry,
- InflationException("Couldn't inflate contentViews$e")
+ InflationException("Couldn't inflate contentViews$e"),
)
// Cancel any image loading tasks, not useful any more
@@ -618,7 +618,7 @@ constructor(
packageContext: Context,
previousSmartReplyState: InflatedSmartReplyState?,
inflater: SmartReplyStateInflater,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
) {
val inflateContracted =
(reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0 &&
@@ -641,7 +641,7 @@ constructor(
packageContext,
entry,
previousSmartReplyState,
- result.inflatedSmartReplyState!!
+ result.inflatedSmartReplyState!!,
)
}
if (inflateHeadsUp) {
@@ -652,7 +652,7 @@ constructor(
packageContext,
entry,
previousSmartReplyState,
- result.inflatedSmartReplyState!!
+ result.inflatedSmartReplyState!!,
)
}
}
@@ -670,7 +670,7 @@ constructor(
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
conversationProcessor: ConversationNotificationProcessor,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): InflationProgress {
// process conversations and extract the messaging style
val messagingStyle =
@@ -713,7 +713,7 @@ constructor(
logger.logAsyncTaskProgress(entry, "inflating public single line view model")
SingleLineViewInflater.inflateRedactedSingleLineViewModel(
systemUIContext,
- entry.ranking.isConversation
+ entry.ranking.isConversation,
)
} else null
@@ -746,7 +746,7 @@ constructor(
row: ExpandableNotificationRow,
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): NewRemoteViews {
return TraceUtils.trace("NotificationContentInflater.createRemoteViews") {
val entryForLogging: NotificationEntry = row.entry
@@ -754,7 +754,7 @@ constructor(
if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating contracted remote view"
+ "creating contracted remote view",
)
createContentView(builder, isMinimized, usesIncreasedHeight)
} else null
@@ -762,7 +762,7 @@ constructor(
if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating expanded remote view"
+ "creating expanded remote view",
)
createExpandedView(builder, isMinimized)
} else null
@@ -770,7 +770,7 @@ constructor(
if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating heads up remote view"
+ "creating heads up remote view",
)
val isHeadsUpCompact = headsUpStyleProvider.shouldApplyCompactStyle()
if (isHeadsUpCompact) {
@@ -791,7 +791,7 @@ constructor(
) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating group summary remote view"
+ "creating group summary remote view",
)
builder.makeNotificationGroupHeader()
} else null
@@ -802,7 +802,7 @@ constructor(
) {
logger.logAsyncTaskProgress(
entryForLogging,
- "creating low-priority group summary remote view"
+ "creating low-priority group summary remote view",
)
builder.makeLowPriorityContentView(true /* useRegularSubtext */)
} else null
@@ -812,7 +812,7 @@ constructor(
expanded = expanded,
public = public,
normalGroupHeader = normalGroupHeader,
- minimizedGroupHeader = minimizedGroupHeader
+ minimizedGroupHeader = minimizedGroupHeader,
)
.withLayoutInflaterFactory(row, notifLayoutInflaterFactoryProvider)
}
@@ -820,7 +820,7 @@ constructor(
private fun NewRemoteViews.withLayoutInflaterFactory(
row: ExpandableNotificationRow,
- provider: NotifLayoutInflaterFactory.Provider
+ provider: NotifLayoutInflaterFactory.Provider,
): NewRemoteViews {
contracted?.let {
it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED)
@@ -848,7 +848,7 @@ constructor(
row: ExpandableNotificationRow,
remoteViewClickHandler: InteractionHandler?,
callback: InflationCallback?,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): CancellationSignal {
Trace.beginAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
val privateLayout = row.privateLayout
@@ -859,7 +859,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.contracted,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -890,7 +890,7 @@ constructor(
existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_CONTRACTED),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
flag = FLAG_CONTENT_VIEW_EXPANDED
@@ -898,7 +898,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.expanded,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -929,7 +929,7 @@ constructor(
existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_EXPANDED),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
flag = FLAG_CONTENT_VIEW_HEADS_UP
@@ -937,7 +937,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.headsUp,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -968,7 +968,7 @@ constructor(
existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
flag = FLAG_CONTENT_VIEW_PUBLIC
@@ -976,7 +976,7 @@ constructor(
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.public,
- oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -1007,7 +1007,7 @@ constructor(
existingWrapper = publicLayout.getVisibleWrapper(VISIBLE_TYPE_CONTRACTED),
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
if (AsyncGroupHeaderViewInflation.isEnabled) {
@@ -1018,7 +1018,7 @@ constructor(
!canReapplyRemoteView(
newView = result.remoteViews.normalGroupHeader,
oldView =
- remoteViewCache.getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)
+ remoteViewCache.getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
@@ -1049,7 +1049,7 @@ constructor(
existingWrapper = childrenContainer.notificationHeaderWrapper,
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) {
@@ -1059,15 +1059,15 @@ constructor(
oldView =
remoteViewCache.getCachedView(
entry,
- FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER
- )
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+ ),
)
val applyCallback: ApplyCallback =
object : ApplyCallback() {
override fun setResultView(v: View) {
logger.logAsyncTaskProgress(
entry,
- "low-priority group header view applied"
+ "low-priority group header view applied",
)
result.inflatedMinimizedGroupHeaderView =
v as NotificationHeaderView?
@@ -1095,7 +1095,7 @@ constructor(
existingWrapper = childrenContainer.minimizedGroupHeaderWrapper,
runningInflations = runningInflations,
applyCallback = applyCallback,
- logger = logger
+ logger = logger,
)
}
}
@@ -1110,7 +1110,7 @@ constructor(
callback,
entry,
row,
- logger
+ logger,
)
val cancellationSignal = CancellationSignal()
cancellationSignal.setOnCancelListener {
@@ -1142,7 +1142,7 @@ constructor(
existingWrapper: NotificationViewWrapper?,
runningInflations: HashMap<Int, CancellationSignal>,
applyCallback: ApplyCallback,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
) {
val newContentView: RemoteViews = applyCallback.remoteView
if (inflateSynchronously) {
@@ -1152,7 +1152,7 @@ constructor(
newContentView.apply(
result.packageContext,
parentLayout,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
validateView(v, entry, row.resources)
applyCallback.setResultView(v)
@@ -1162,7 +1162,7 @@ constructor(
newContentView.reapply(
result.packageContext,
existingView,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
validateView(existingView, entry, row.resources)
existingWrapper.onReinflated()
@@ -1174,7 +1174,7 @@ constructor(
row.entry,
callback,
logger,
- "applying view synchronously"
+ "applying view synchronously",
)
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
@@ -1199,7 +1199,7 @@ constructor(
row.entry,
callback,
logger,
- "applied invalid view"
+ "applied invalid view",
)
runningInflations.remove(inflationId)
return
@@ -1219,7 +1219,7 @@ constructor(
callback,
entry,
row,
- logger
+ logger,
)
}
@@ -1234,20 +1234,20 @@ constructor(
newContentView.apply(
result.packageContext,
parentLayout,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
} else {
newContentView.reapply(
result.packageContext,
existingView,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
existingView!!
}
Log.wtf(
TAG,
"Async Inflation failed but normal inflation finished normally.",
- e
+ e,
)
onViewApplied(newView)
} catch (anotherException: Exception) {
@@ -1258,7 +1258,7 @@ constructor(
row.entry,
callback,
logger,
- "applying view"
+ "applying view",
)
}
}
@@ -1270,7 +1270,7 @@ constructor(
parentLayout,
inflationExecutor,
listener,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
} else {
newContentView.reapplyAsync(
@@ -1278,7 +1278,7 @@ constructor(
existingView,
inflationExecutor,
listener,
- remoteViewClickHandler
+ remoteViewClickHandler,
)
}
runningInflations[inflationId] = cancellationSignal
@@ -1299,7 +1299,7 @@ constructor(
private fun satisfiesMinHeightRequirement(
view: View,
entry: NotificationEntry,
- resources: Resources
+ resources: Resources,
): Boolean {
return if (!requiresHeightCheck(entry)) {
true
@@ -1353,7 +1353,7 @@ constructor(
notification: NotificationEntry,
callback: InflationCallback?,
logger: NotificationRowContentBinderLogger,
- logContext: String
+ logContext: String,
) {
Assert.isMainThread()
logger.logAsyncTaskException(notification, logContext, e)
@@ -1375,7 +1375,7 @@ constructor(
endListener: InflationCallback?,
entry: NotificationEntry,
row: ExpandableNotificationRow,
- logger: NotificationRowContentBinderLogger
+ logger: NotificationRowContentBinderLogger,
): Boolean {
Assert.isMainThread()
if (runningInflations.isNotEmpty()) {
@@ -1439,19 +1439,19 @@ constructor(
FLAG_CONTENT_VIEW_CONTRACTED,
result.remoteViews.contracted,
result.inflatedContentView,
- privateLayout::setContractedChild
+ privateLayout::setContractedChild,
)
remoteViewsUpdater.setContentView(
FLAG_CONTENT_VIEW_EXPANDED,
result.remoteViews.expanded,
result.inflatedExpandedView,
- privateLayout::setExpandedChild
+ privateLayout::setExpandedChild,
)
remoteViewsUpdater.setSmartReplies(
FLAG_CONTENT_VIEW_EXPANDED,
result.remoteViews.expanded,
result.expandedInflatedSmartReplies,
- privateLayout::setExpandedInflatedSmartReplies
+ privateLayout::setExpandedInflatedSmartReplies,
)
if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
row.setExpandable(result.remoteViews.expanded != null)
@@ -1460,19 +1460,19 @@ constructor(
FLAG_CONTENT_VIEW_HEADS_UP,
result.remoteViews.headsUp,
result.inflatedHeadsUpView,
- privateLayout::setHeadsUpChild
+ privateLayout::setHeadsUpChild,
)
remoteViewsUpdater.setSmartReplies(
FLAG_CONTENT_VIEW_HEADS_UP,
result.remoteViews.headsUp,
result.headsUpInflatedSmartReplies,
- privateLayout::setHeadsUpInflatedSmartReplies
+ privateLayout::setHeadsUpInflatedSmartReplies,
)
remoteViewsUpdater.setContentView(
FLAG_CONTENT_VIEW_PUBLIC,
result.remoteViews.public,
result.inflatedPublicView,
- publicLayout::setContractedChild
+ publicLayout::setContractedChild,
)
if (AsyncGroupHeaderViewInflation.isEnabled) {
remoteViewsUpdater.setContentView(
@@ -1540,7 +1540,7 @@ constructor(
private fun createExpandedView(
builder: Notification.Builder,
- isMinimized: Boolean
+ isMinimized: Boolean,
): RemoteViews? {
@Suppress("DEPRECATION")
val bigContentView: RemoteViews? = builder.createBigContentView()
@@ -1558,7 +1558,7 @@ constructor(
private fun createContentView(
builder: Notification.Builder,
isMinimized: Boolean,
- useLarge: Boolean
+ useLarge: Boolean,
): RemoteViews {
return if (isMinimized) {
builder.makeLowPriorityContentView(false /* useRegularSubtext */)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
index 7177a7bd473a..08c1d71b86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -73,8 +73,8 @@ constructor(private val userManager: UserManager, dumpManager: DumpManager) :
private val cache = NotifCollectionCache<Boolean>()
override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
- val packageContext = notification.getPackageContext(context)
return cache.getOrFetch(notification.packageName) {
+ val packageContext = notification.getPackageContext(context)
!belongsToHeadlessSystemApp(packageContext)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index f3521234e67a..b622defbef98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -203,11 +203,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
addRemainingTransformTypes();
updateCropToPaddingForImageViews();
Notification n = row.getEntry().getSbn().getNotification();
- if (n.shouldUseAppIcon()) {
- mIcon.setTag(ImageTransformState.ICON_TAG, n.getAppIcon());
- } else {
- mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
- }
+ mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
// We need to reset all views that are no longer transforming in case a view was previously
// transformed, but now we decided to transform its container instead.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index cf19938aa533..a2b71551eca8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.shared
import android.app.PendingIntent
import android.graphics.drawable.Icon
+import android.util.Log
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.stack.PriorityBucket
/**
@@ -36,6 +38,9 @@ data class ActiveNotificationModel(
val groupKey: String?,
/** When this notification was posted. */
val whenTime: Long,
+ // TODO(b/377566661): Make isPromoted just check if promotedContent != null.
+ /** True if this notification should be promoted and false otherwise. */
+ val isPromoted: Boolean,
/** Is this entry in the ambient / minimized section (lowest priority)? */
val isAmbient: Boolean,
/**
@@ -76,7 +81,24 @@ data class ActiveNotificationModel(
@PriorityBucket val bucket: Int,
/** The call type set on the notification. */
val callType: CallType,
-) : ActiveNotificationEntryModel()
+ /**
+ * The content needed to render this as a promoted notification on various surfaces, or null if
+ * this notification cannot be rendered as a promoted notification.
+ */
+ val promotedContent: PromotedNotificationContentModel?,
+) : ActiveNotificationEntryModel() {
+ init {
+ if (!PromotedNotificationContentModel.featureFlagEnabled()) {
+ if (promotedContent != null) {
+ Log.wtf(TAG, "passing non-null promoted content without feature flag enabled")
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "ActiveNotificationEntryModel"
+ }
+}
/** Model for a group of notifications. */
data class ActiveNotificationGroupModel(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index c9a0010c0de7..31e4d2cac50c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -26,7 +26,14 @@ import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
-import com.android.systemui.statusbar.notification.dagger.*
+import com.android.systemui.statusbar.notification.dagger.AlertingHeader
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader
+import com.android.systemui.statusbar.notification.dagger.NewsHeader
+import com.android.systemui.statusbar.notification.dagger.PeopleHeader
+import com.android.systemui.statusbar.notification.dagger.PromoHeader
+import com.android.systemui.statusbar.notification.dagger.RecsHeader
+import com.android.systemui.statusbar.notification.dagger.SilentHeader
+import com.android.systemui.statusbar.notification.dagger.SocialHeader
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
@@ -54,7 +61,7 @@ internal constructor(
@NewsHeader private val newsHeaderController: SectionHeaderController,
@SocialHeader private val socialHeaderController: SectionHeaderController,
@RecsHeader private val recsHeaderController: SectionHeaderController,
- @PromoHeader private val promoHeaderController: SectionHeaderController
+ @PromoHeader private val promoHeaderController: SectionHeaderController,
) : SectionProvider {
private val configurationListener =
@@ -136,14 +143,16 @@ internal constructor(
override fun beginsSection(view: View, previous: View?): Boolean =
view === silentHeaderView ||
- view === mediaControlsView ||
- view === peopleHeaderView ||
- view === alertingHeaderView ||
- view === incomingHeaderView ||
- (NotificationClassificationFlag.isEnabled && (view === newsHeaderView
- || view === socialHeaderView || view === recsHeaderView
- || view === promoHeaderView)) ||
- getBucket(view) != getBucket(previous)
+ view === mediaControlsView ||
+ view === peopleHeaderView ||
+ view === alertingHeaderView ||
+ view === incomingHeaderView ||
+ (NotificationClassificationFlag.isEnabled &&
+ (view === newsHeaderView ||
+ view === socialHeaderView ||
+ view === recsHeaderView ||
+ view === promoHeaderView)) ||
+ getBucket(view) != getBucket(previous)
private fun getBucket(view: View?): Int? =
when {
@@ -165,6 +174,7 @@ internal constructor(
data class Many(val first: ExpandableView, val last: ExpandableView) : SectionBounds()
data class One(val lone: ExpandableView) : SectionBounds()
+
object None : SectionBounds()
fun addNotif(notif: ExpandableView): SectionBounds =
@@ -183,7 +193,7 @@ internal constructor(
private fun NotificationSection.setFirstAndLastVisibleChildren(
first: ExpandableView?,
- last: ExpandableView?
+ last: ExpandableView?,
): Boolean {
val firstChanged = setFirstVisibleChild(first)
val lastChanged = setLastVisibleChild(last)
@@ -198,7 +208,7 @@ internal constructor(
*/
fun updateFirstAndLastViewsForAllSections(
sections: Array<NotificationSection>,
- children: List<ExpandableView>
+ children: List<ExpandableView>,
): Boolean {
// Create mapping of bucket to section
val sectionBounds =
@@ -213,7 +223,7 @@ internal constructor(
.foldToSparseArray(
SectionBounds.None,
size = sections.size,
- operation = SectionBounds::addNotif
+ operation = SectionBounds::addNotif,
)
// Build a set of the old first/last Views of the sections
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 3bc549543ef2..5dff8120f33f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -21,12 +21,14 @@ import android.util.Log
import android.view.View.GONE
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
@@ -38,6 +40,9 @@ import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
import kotlin.properties.Delegates.notNull
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
private const val TAG = "NotifStackSizeCalc"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
@@ -56,7 +61,9 @@ constructor(
private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
private val mediaDataManager: MediaDataManager,
@Main private val resources: Resources,
- private val splitShadeStateController: SplitShadeStateController
+ private val splitShadeStateController: SplitShadeStateController,
+ private val seenNotificationsInteractor: SeenNotificationsInteractor,
+ @Application private val scope: CoroutineScope,
) {
/**
@@ -74,7 +81,7 @@ constructor(
/** Whether we allow keyguard to show less important notifications above the shelf. */
private val limitLockScreenToOneImportant
- get() = NotificationMinimalism.isEnabled
+ get() = NotificationMinimalism.isEnabled && minimalismSettingEnabled
/** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
private var dividerHeight by notNull<Float>()
@@ -85,8 +92,14 @@ constructor(
*/
private var saveSpaceOnLockscreen = false
+ /** True when the lock screen notification minimalism feature setting is enabled */
+ private var minimalismSettingEnabled = false
+
init {
updateResources()
+ if (NotificationMinimalism.isEnabled) {
+ scope.launch { trackLockScreenNotificationMinimalismSettingChanges() }
+ }
}
private fun allowedByPolicy(stackHeight: StackHeight): Boolean =
@@ -199,7 +212,7 @@ constructor(
canStackFitInSpace(
heightResult,
notifSpace = notifSpace,
- shelfSpace = shelfSpace
+ shelfSpace = shelfSpace,
) == FitResult.FIT
}
@@ -229,7 +242,7 @@ constructor(
canStackFitInSpace(
heightResult,
notifSpace = notifSpace,
- shelfSpace = shelfSpace
+ shelfSpace = shelfSpace,
) != FitResult.NO_FIT
}
log { "\t--- maxNotifications=$maxNotifications" }
@@ -277,7 +290,7 @@ constructor(
fun computeHeight(
stack: NotificationStackScrollLayout,
maxNotifs: Int,
- shelfHeight: Float
+ shelfHeight: Float,
): Float {
log { "\n" }
log { "computeHeight ---" }
@@ -311,7 +324,7 @@ constructor(
private enum class FitResult {
FIT,
FIT_IF_SAVE_SPACE,
- NO_FIT
+ NO_FIT,
}
data class SpaceNeeded(
@@ -319,7 +332,7 @@ constructor(
val whenEnoughSpace: Float,
// Float height of space needed when showing collapsed layout for FSI HUNs.
- val whenSavingSpace: Float
+ val whenSavingSpace: Float,
)
private data class StackHeight(
@@ -335,9 +348,19 @@ constructor(
val shelfHeightWithSpaceBefore: Float,
/** Whether the stack should actually be forced into the shelf before this height. */
- val shouldForceIntoShelf: Boolean
+ val shouldForceIntoShelf: Boolean,
)
+ private suspend fun trackLockScreenNotificationMinimalismSettingChanges() {
+ if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
+ seenNotificationsInteractor.isLockScreenNotificationMinimalismEnabled().collectLatest {
+ if (it != minimalismSettingEnabled) {
+ minimalismSettingEnabled = it
+ }
+ Log.i(TAG, "minimalismSettingEnabled: $minimalismSettingEnabled")
+ }
+ }
+
private fun computeHeightPerNotificationLimit(
stack: NotificationStackScrollLayout,
shelfHeight: Float,
@@ -377,7 +400,7 @@ constructor(
stack,
previous = currentNotification,
current = children[firstViewInShelfIndex],
- currentIndex = firstViewInShelfIndex
+ currentIndex = firstViewInShelfIndex,
)
spaceBeforeShelf + shelfHeight
}
@@ -390,14 +413,15 @@ constructor(
log {
"\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
"notifsHeightSavingSpace=$notifsWithCollapsedHun" +
- " shelfWithSpaceBefore=$shelfWithSpaceBefore"
+ " shelfWithSpaceBefore=$shelfWithSpaceBefore" +
+ " limitLockScreenToOneImportant: $limitLockScreenToOneImportant"
}
yield(
StackHeight(
notifsHeight = notifications,
notifsHeightSavingSpace = notifsWithCollapsedHun,
shelfHeightWithSpaceBefore = shelfWithSpaceBefore,
- shouldForceIntoShelf = counter?.shouldForceIntoShelf() ?: false
+ shouldForceIntoShelf = counter?.shouldForceIntoShelf() ?: false,
)
)
}
@@ -462,6 +486,10 @@ constructor(
fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("NotificationStackSizeCalculator saveSpaceOnLockscreen=$saveSpaceOnLockscreen")
+ pw.println(
+ "NotificationStackSizeCalculator " +
+ "limitLockScreenToOneImportant=$limitLockScreenToOneImportant"
+ )
}
private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean {
@@ -484,7 +512,7 @@ constructor(
stack: NotificationStackScrollLayout,
previous: ExpandableView?,
current: ExpandableView?,
- currentIndex: Int
+ currentIndex: Int,
): Float {
if (currentIndex == 0) {
return 0f
@@ -536,11 +564,7 @@ constructor(
takeWhile(predicate).count() - 1
/** Counts the number of notifications for each type of bucket */
- data class BucketTypeCounter(
- var ongoing: Int = 0,
- var important: Int = 0,
- var other: Int = 0,
- ) {
+ data class BucketTypeCounter(var ongoing: Int = 0, var important: Int = 0, var other: Int = 0) {
fun incrementForBucket(@PriorityBucket bucket: Int?) {
when (bucket) {
BUCKET_MEDIA_CONTROLS,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
index 3a650aa19eb0..53d0c2e879e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractor.kt
@@ -22,6 +22,7 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import com.android.systemui.util.kotlin.WithPrev
@@ -46,9 +47,9 @@ class HideNotificationsInteractor
@Inject
constructor(
private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
private val animationsStatus: AnimationStatusRepository,
- private val powerInteractor: PowerInteractor
+ private val powerInteractor: PowerInteractor,
) {
val shouldHideNotifications: Flow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index e644815960aa..b6ce70826f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -47,7 +47,7 @@ class SharedNotificationContainerInteractor
constructor(
@ShadeDisplayAware private val context: Context,
private val splitShadeStateController: Lazy<SplitShadeStateController>,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
keyguardInteractor: KeyguardInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index f75c89aeb44c..bffcae99e7f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -20,6 +20,7 @@ import android.view.LayoutInflater
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.TraceUtils.traceAsync
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.systemui.common.ui.ConfigurationState
@@ -30,6 +31,7 @@ import com.android.systemui.lifecycle.repeatWhenAttachedToWindow
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
@@ -70,7 +72,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
class NotificationListViewBinder
@@ -78,7 +79,7 @@ class NotificationListViewBinder
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val hiderTracker: DisplaySwitchNotificationsHiderTracker,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
private val falsingManager: FalsingManager,
private val hunBinder: HeadsUpNotificationViewBinder,
private val loggerOptional: Optional<NotificationStatsLogger>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 4a768714b84f..c5bef99f9307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -28,6 +28,7 @@ import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -48,7 +49,7 @@ constructor(
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
private val view: NotificationScrollView,
private val viewModelFactory: NotificationScrollViewModel.Factory,
- private val configuration: ConfigurationState,
+ @ShadeDisplayAware private val configuration: ConfigurationState,
) : FlowDumperImpl(dumpManager) {
private val viewLeftOffset = MutableStateFlow(0).dumpValue("viewLeftOffset")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 827e2bfeba0f..fb60f266e188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -72,6 +72,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode.Dual
import com.android.systemui.shade.shared.model.ShadeMode.Single
@@ -116,7 +117,7 @@ constructor(
dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
private val context: Context,
- configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val shadeInteractor: ShadeInteractor,
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 99efba43b12f..80c8e8b2a109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -171,12 +171,14 @@ import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeLogger;
import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.StatusBarLongPressGestureDetector;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -201,7 +203,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
import com.android.systemui.statusbar.core.StatusBarInitializer;
-import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
+import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.data.model.StatusBarMode;
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -295,7 +297,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
};
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarConnectedDisplays.assertInLegacyMode();
mStatusBarWindowState = state;
updateBubblesVisibility();
}
@@ -366,6 +368,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private PhoneStatusBarViewController mPhoneStatusBarViewController;
private PhoneStatusBarTransitions mStatusBarTransitions;
+ private final Provider<StatusBarLongPressGestureDetector> mStatusBarLongPressGestureDetector;
private final AuthRippleController mAuthRippleController;
@WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
private final NotificationShadeWindowController mNotificationShadeWindowController;
@@ -671,6 +674,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
ShadeController shadeController,
WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ Provider<StatusBarLongPressGestureDetector> statusBarLongPressGestureDetector,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
@@ -778,6 +782,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mShadeController = shadeController;
mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mStatusBarLongPressGestureDetector = statusBarLongPressGestureDetector;
mKeyguardViewMediatorCallback = viewMediatorCallback;
mInitController = initController;
mPluginDependencyProvider = pluginDependencyProvider;
@@ -1226,7 +1231,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
checkBarModes();
});
}
- if (!StatusBarSimpleFragment.isEnabled() && !StatusBarConnectedDisplays.isEnabled()) {
+ if (!StatusBarRootModernization.isEnabled() && !StatusBarConnectedDisplays.isEnabled()) {
// When the flag is on, we register the fragment as a core startable and this is not
// needed
mStatusBarInitializer.initializeStatusBar();
@@ -1527,6 +1532,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// to touch outside the customizer to close it, such as on the status or nav bar.
mShadeController.onStatusBarTouch(event);
}
+ if (ShadeExpandsOnStatusBarLongPress.isEnabled()
+ && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ mStatusBarLongPressGestureDetector.get().handleTouch(event);
+ }
+
return getNotificationShadeWindowView().onTouchEvent(event);
};
}
@@ -1589,8 +1599,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
-
- mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 398c1d43d4fc..90b591f173f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -23,11 +23,15 @@ import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.ArrayMap;
+import android.view.Display;
import android.widget.ImageView;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import kotlinx.coroutines.flow.FlowKt;
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlow;
@@ -36,17 +40,15 @@ import kotlinx.coroutines.flow.StateFlowKt;
import java.io.PrintWriter;
import java.util.ArrayList;
-import javax.inject.Inject;
-
/**
*/
-@SysUISingleton
public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
LightBarTransitionsController.DarkIntensityApplier {
private final LightBarTransitionsController mTransitionsController;
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final ArrayMap<Object, DarkReceiver> mReceivers = new ArrayMap<>();
+ private final DumpManager mDumpManager;
private int mIconTint = DEFAULT_ICON_TINT;
private int mContrastTint = DEFAULT_INVERSE_ICON_TINT;
@@ -61,14 +63,25 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
private final MutableStateFlow<DarkChange> mDarkChangeFlow = StateFlowKt.MutableStateFlow(
DarkChange.EMPTY);
+ private final String mDumpableName;
+
+ /** */
+ @AssistedFactory
+ @FunctionalInterface
+ public interface Factory {
+ /** */
+ DarkIconDispatcherImpl create(int displayId, Context context);
+ }
+
/**
*/
- @Inject
+ @AssistedInject
public DarkIconDispatcherImpl(
- Context context,
+ @Assisted int displayId,
+ @Assisted Context context,
LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
DumpManager dumpManager) {
-
+ mDumpManager = dumpManager;
if (newStatusBarIcons()) {
mDarkModeIconColorSingleTone = Color.BLACK;
mLightModeIconColorSingleTone = Color.WHITE;
@@ -81,7 +94,19 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
mTransitionsController = lightBarTransitionsControllerFactory.create(this);
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
+ mDumpableName = getDumpableName(displayId);
+ dumpManager.registerNormalDumpable(mDumpableName, this);
+ }
+
+ @Override
+ public void stop() {
+ mDumpManager.unregisterDumpable(mDumpableName);
+ }
+
+ private String getDumpableName(int displayId) {
+ String dumpableNameSuffix =
+ displayId == Display.DEFAULT_DISPLAY ? "" : String.valueOf(displayId);
+ return getClass().getSimpleName() + dumpableNameSuffix;
}
public LightBarTransitionsController getTransitionsController() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index d0f4b6f4a4bb..8de03d83d7af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,6 +26,7 @@ import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.dagger.qualifiers.DisplaySpecific;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -117,7 +118,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
PhoneStatusBarTransitions phoneStatusBarTransitions,
KeyguardBypassController bypassController,
NotificationWakeUpCoordinator wakeUpCoordinator,
- DarkIconDispatcher darkIconDispatcher,
+ @DisplaySpecific DarkIconDispatcher darkIconDispatcher,
KeyguardStateController keyguardStateController,
CommandQueue commandQueue,
NotificationStackScrollLayoutController stackScrollerController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index be2fb68ab88d..2433b78fc183 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -50,6 +50,8 @@ import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -82,6 +84,8 @@ import com.android.systemui.util.settings.SecureSettings;
import kotlin.Unit;
+import kotlinx.coroutines.CoroutineDispatcher;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -108,6 +112,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
R.id.keyguard_hun_animator_end_tag,
R.id.keyguard_hun_animator_start_tag);
+ private final CoroutineDispatcher mCoroutineDispatcher;
private final CarrierTextController mCarrierTextController;
private final ConfigurationController mConfigurationController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -133,6 +138,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private final Object mLock = new Object();
private final KeyguardLogger mLogger;
private final CommunalSceneInteractor mCommunalSceneInteractor;
+ private final GlanceableHubToLockscreenTransitionViewModel mHubToLockscreenTransitionViewModel;
+ private final LockscreenToGlanceableHubTransitionViewModel mLockscreenToHubTransitionViewModel;
private View mSystemIconsContainer;
private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
@@ -249,9 +256,20 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private boolean mCommunalShowing;
private final Consumer<Boolean> mCommunalConsumer = (communalShowing) -> {
+ updateCommunalShowing(communalShowing);
+ };
+
+ @VisibleForTesting
+ void updateCommunalShowing(boolean communalShowing) {
mCommunalShowing = communalShowing;
+
+ // When communal is hidden (either by transition or state change), set alpha to fully
+ // visible.
+ if (!mCommunalShowing) {
+ setAlpha(-1f);
+ }
updateViewState();
- };
+ }
private final DisableStateTracker mDisableStateTracker;
@@ -277,6 +295,15 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private boolean mShowingKeyguardHeadsUp;
private StatusBarSystemEventDefaultAnimator mSystemEventAnimator;
private float mSystemEventAnimatorAlpha = 1;
+ private final Consumer<Float> mToGlanceableHubStatusBarAlphaConsumer = (alpha) ->
+ updateCommunalAlphaTransition(alpha);
+
+ private final Consumer<Float> mFromGlanceableHubStatusBarAlphaConsumer = (alpha) ->
+ updateCommunalAlphaTransition(alpha);
+
+ @VisibleForTesting void updateCommunalAlphaTransition(float alpha) {
+ setAlpha(!mCommunalShowing || alpha == 0 ? -1 : alpha);
+ }
/**
* The alpha value to be set on the View. If -1, this value is to be ignored.
@@ -285,6 +312,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
@Inject
public KeyguardStatusBarViewController(
+ @Main CoroutineDispatcher dispatcher,
KeyguardStatusBarView view,
CarrierTextController carrierTextController,
ConfigurationController configurationController,
@@ -310,9 +338,14 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
@Background Executor backgroundExecutor,
KeyguardLogger logger,
StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory,
- CommunalSceneInteractor communalSceneInteractor
+ CommunalSceneInteractor communalSceneInteractor,
+ GlanceableHubToLockscreenTransitionViewModel
+ glanceableHubToLockscreenTransitionViewModel,
+ LockscreenToGlanceableHubTransitionViewModel
+ lockscreenToGlanceableHubTransitionViewModel
) {
super(view);
+ mCoroutineDispatcher = dispatcher;
mCarrierTextController = carrierTextController;
mConfigurationController = configurationController;
mAnimationScheduler = animationScheduler;
@@ -337,6 +370,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mBackgroundExecutor = backgroundExecutor;
mLogger = logger;
mCommunalSceneInteractor = communalSceneInteractor;
+ mHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel;
+ mLockscreenToHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -418,7 +453,12 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
UserHandle.USER_ALL);
updateUserSwitcher();
onThemeChanged();
- collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer);
+ collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
+ mCoroutineDispatcher);
+ collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
+ mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+ collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
+ mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
}
@Override
@@ -573,7 +613,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
&& !mDozing
&& !hideForBypass
&& !mDisableStateTracker.isDisabled()
- && !mCommunalShowing
+ && (!mCommunalShowing || mExplicitAlpha != -1)
? View.VISIBLE : View.INVISIBLE;
updateViewState(newAlpha, newVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt
new file mode 100644
index 000000000000..a6374a66806b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.view.WindowInsetsController
+import com.android.internal.colorextraction.ColorExtractor
+import com.android.internal.view.AppearanceRegion
+import com.android.systemui.CoreStartable
+
+/** Controls how light status bar flag applies to the icons. */
+interface LightBarController : CoreStartable {
+
+ fun stop()
+
+ fun setNavigationBar(navigationBar: LightBarTransitionsController)
+
+ fun onNavigationBarAppearanceChanged(
+ @WindowInsetsController.Appearance appearance: Int,
+ nbModeChanged: Boolean,
+ navigationBarMode: Int,
+ navbarColorManagedByIme: Boolean,
+ )
+
+ fun onNavigationBarModeChanged(newBarMode: Int)
+
+ fun setQsCustomizing(customizing: Boolean)
+
+ /** Set if Quick Settings is fully expanded, which affects notification scrim visibility. */
+ fun setQsExpanded(expanded: Boolean)
+
+ /** Set if Global Actions dialog is visible, which requires dark mode (light buttons). */
+ fun setGlobalActionsVisible(visible: Boolean)
+
+ /**
+ * Controls the light status bar temporarily for back navigation.
+ *
+ * @param appearance the customized appearance.
+ */
+ fun customizeStatusBarAppearance(appearance: AppearanceRegion)
+
+ /**
+ * Sets whether the direct-reply is in use or not.
+ *
+ * @param directReplying `true` when the direct-reply is in-use.
+ */
+ fun setDirectReplying(directReplying: Boolean)
+
+ fun setScrimState(
+ scrimState: ScrimState,
+ scrimBehindAlpha: Float,
+ scrimInFrontColor: ColorExtractor.GradientColors,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
index a33996b99900..ccb9a119d92f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.statusbar.phone;
@@ -22,9 +22,9 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
+import android.view.Display;
import android.view.InsetsFlags;
import android.view.ViewDebug;
import android.view.WindowInsetsController.Appearance;
@@ -34,30 +34,32 @@ import androidx.annotation.Nullable;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.view.AppearanceRegion;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.data.model.StatusBarAppearance;
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore;
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.Compile;
-import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.kotlin.JavaAdapterKt;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
+import kotlin.coroutines.CoroutineContext;
+
+import kotlinx.coroutines.CoroutineScope;
import java.io.PrintWriter;
import java.util.ArrayList;
-import javax.inject.Inject;
-
/**
* Controls how light status bar flag applies to the icons.
*/
-@SysUISingleton
-public class LightBarController implements
- BatteryController.BatteryStateChangeCallback, Dumpable, CoreStartable {
+public class LightBarControllerImpl implements
+ BatteryController.BatteryStateChangeCallback, LightBarController {
private static final String TAG = "LightBarController";
private static final boolean DEBUG_NAVBAR = Compile.IS_DEBUG;
@@ -65,11 +67,14 @@ public class LightBarController implements
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
- private final JavaAdapter mJavaAdapter;
+ private final CoroutineScope mCoroutineScope;
private final SysuiDarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
- private final StatusBarModeRepositoryStore mStatusBarModeRepository;
- private BiometricUnlockController mBiometricUnlockController;
+ private final NavigationModeController mNavModeController;
+ private final DumpManager mDumpManager;
+ private final StatusBarModePerDisplayRepository mStatusBarModeRepository;
+ private final CoroutineContext mMainContext;
+ private final BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
private @Appearance int mAppearance;
@@ -119,47 +124,60 @@ public class LightBarController implements
private String mLastNavigationBarAppearanceChangedLog;
private StringBuilder mLogStringBuilder = null;
- @Inject
- public LightBarController(
- Context ctx,
- JavaAdapter javaAdapter,
- DarkIconDispatcher darkIconDispatcher,
+ private final String mDumpableName;
+
+ private final NavigationModeController.ModeChangedListener mNavigationModeListener =
+ (mode) -> mNavigationMode = mode;
+
+ @AssistedInject
+ public LightBarControllerImpl(
+ @Assisted int displayId,
+ @Assisted CoroutineScope coroutineScope,
+ @Assisted DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
- StatusBarModeRepositoryStore statusBarModeRepository,
+ @Assisted StatusBarModePerDisplayRepository statusBarModeRepository,
DumpManager dumpManager,
- DisplayTracker displayTracker) {
- mJavaAdapter = javaAdapter;
+ @Main CoroutineContext mainContext,
+ BiometricUnlockController biometricUnlockController) {
+ mCoroutineScope = coroutineScope;
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
mBatteryController = batteryController;
- mBatteryController.addCallback(this);
+ mNavModeController = navModeController;
+ mDumpManager = dumpManager;
mStatusBarModeRepository = statusBarModeRepository;
- mNavigationMode = navModeController.addListener((mode) -> {
- mNavigationMode = mode;
- });
-
- if (ctx.getDisplayId() == displayTracker.getDefaultDisplayId()) {
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
- }
+ mMainContext = mainContext;
+ mBiometricUnlockController = biometricUnlockController;
+ String dumpableNameSuffix =
+ displayId == Display.DEFAULT_DISPLAY ? "" : String.valueOf(displayId);
+ mDumpableName = getClass().getSimpleName() + dumpableNameSuffix;
}
@Override
public void start() {
- mJavaAdapter.alwaysCollectFlow(
- mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance(),
+ mDumpManager.registerCriticalDumpable(mDumpableName, this);
+ mBatteryController.addCallback(this);
+ mNavigationMode = mNavModeController.addListener(mNavigationModeListener);
+ JavaAdapterKt.collectFlow(
+ mCoroutineScope,
+ mMainContext,
+ mStatusBarModeRepository.getStatusBarAppearance(),
this::onStatusBarAppearanceChanged);
}
+ @Override
+ public void stop() {
+ mDumpManager.unregisterDumpable(mDumpableName);
+ mBatteryController.removeCallback(this);
+ mNavModeController.removeListener(mNavigationModeListener);
+ }
+
+ @Override
public void setNavigationBar(LightBarTransitionsController navigationBar) {
mNavigationBarController = navigationBar;
updateNavigation();
}
- public void setBiometricUnlockController(
- BiometricUnlockController biometricUnlockController) {
- mBiometricUnlockController = biometricUnlockController;
- }
-
private void onStatusBarAppearanceChanged(@Nullable StatusBarAppearance params) {
if (params == null) {
return;
@@ -202,6 +220,7 @@ public class LightBarController implements
mNavbarColorManagedByIme = navbarColorManagedByIme;
}
+ @Override
public void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged,
int navigationBarMode, boolean navbarColorManagedByIme) {
int diff = appearance ^ mAppearance;
@@ -244,6 +263,7 @@ public class LightBarController implements
mNavbarColorManagedByIme = navbarColorManagedByIme;
}
+ @Override
public void onNavigationBarModeChanged(int newBarMode) {
mHasLightNavigationBar = isLight(mAppearance, newBarMode, APPEARANCE_LIGHT_NAVIGATION_BARS);
}
@@ -258,30 +278,28 @@ public class LightBarController implements
mNavigationBarMode, mNavbarColorManagedByIme);
}
+ @Override
public void setQsCustomizing(boolean customizing) {
if (mQsCustomizing == customizing) return;
mQsCustomizing = customizing;
reevaluate();
}
- /** Set if Quick Settings is fully expanded, which affects notification scrim visibility */
+ @Override
public void setQsExpanded(boolean expanded) {
if (mQsExpanded == expanded) return;
mQsExpanded = expanded;
reevaluate();
}
- /** Set if Global Actions dialog is visible, which requires dark mode (light buttons) */
+ @Override
public void setGlobalActionsVisible(boolean visible) {
if (mGlobalActionsVisible == visible) return;
mGlobalActionsVisible = visible;
reevaluate();
}
- /**
- * Controls the light status bar temporarily for back navigation.
- * @param appearance the custmoized appearance.
- */
+ @Override
public void customizeStatusBarAppearance(AppearanceRegion appearance) {
if (appearance != null) {
final ArrayList<AppearanceRegion> appearancesList = new ArrayList<>();
@@ -303,16 +321,14 @@ public class LightBarController implements
}
}
- /**
- * Sets whether the direct-reply is in use or not.
- * @param directReplying {@code true} when the direct-reply is in-use.
- */
+ @Override
public void setDirectReplying(boolean directReplying) {
if (mDirectReplying == directReplying) return;
mDirectReplying = directReplying;
reevaluate();
}
+ @Override
public void setScrimState(ScrimState scrimState, float scrimBehindAlpha,
GradientColors scrimInFrontColor) {
boolean bouncerVisibleLast = mBouncerVisible;
@@ -368,9 +384,6 @@ public class LightBarController implements
}
private boolean animateChange() {
- if (mBiometricUnlockController == null) {
- return false;
- }
int unlockMode = mBiometricUnlockController.getMode();
return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
&& unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -387,20 +400,17 @@ public class LightBarController implements
}
}
- // If no one is light, all icons become white.
if (lightBarBounds.isEmpty()) {
- mStatusBarIconController.getTransitionsController().setIconsDark(
- false, animateChange());
- }
-
- // If all stacks are light, all icons get dark.
- else if (lightBarBounds.size() == numStacks) {
+ // If no one is light, all icons become white.
+ mStatusBarIconController
+ .getTransitionsController()
+ .setIconsDark(false, animateChange());
+ } else if (lightBarBounds.size() == numStacks) {
+ // If all stacks are light, all icons get dark.
mStatusBarIconController.setIconsDarkArea(null);
mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
- }
-
- // Not the same for every stack, magic!
- else {
+ } else {
+ // Not the same for every stack, magic!
mStatusBarIconController.setIconsDarkArea(lightBarBounds);
mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
}
@@ -468,47 +478,16 @@ public class LightBarController implements
}
}
- /**
- * Injectable factory for creating a {@link LightBarController}.
- */
- public static class Factory {
- private final JavaAdapter mJavaAdapter;
- private final DarkIconDispatcher mDarkIconDispatcher;
- private final BatteryController mBatteryController;
- private final NavigationModeController mNavModeController;
- private final StatusBarModeRepositoryStore mStatusBarModeRepository;
- private final DumpManager mDumpManager;
- private final DisplayTracker mDisplayTracker;
-
- @Inject
- public Factory(
- JavaAdapter javaAdapter,
- DarkIconDispatcher darkIconDispatcher,
- BatteryController batteryController,
- NavigationModeController navModeController,
- StatusBarModeRepositoryStore statusBarModeRepository,
- DumpManager dumpManager,
- DisplayTracker displayTracker) {
- mJavaAdapter = javaAdapter;
- mDarkIconDispatcher = darkIconDispatcher;
- mBatteryController = batteryController;
- mNavModeController = navModeController;
- mStatusBarModeRepository = statusBarModeRepository;
- mDumpManager = dumpManager;
- mDisplayTracker = displayTracker;
- }
+ /** Injectable factory for creating a {@link LightBarControllerImpl}. */
+ @AssistedFactory
+ @FunctionalInterface
+ public interface Factory {
- /** Create an {@link LightBarController} */
- public LightBarController create(Context context) {
- return new LightBarController(
- context,
- mJavaAdapter,
- mDarkIconDispatcher,
- mBatteryController,
- mNavModeController,
- mStatusBarModeRepository,
- mDumpManager,
- mDisplayTracker);
- }
+ /** Creates a {@link LightBarControllerImpl}. */
+ LightBarControllerImpl create(
+ int displayId,
+ CoroutineScope coroutineScope,
+ DarkIconDispatcher darkIconDispatcher,
+ StatusBarModePerDisplayRepository statusBarModePerDisplayRepository);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 91c43ddf1ce4..176dd8de6cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -39,8 +39,8 @@ import com.android.systemui.Dependency;
import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.res.R;
-import com.android.systemui.shade.LongPressGestureDetector;
import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress;
+import com.android.systemui.shade.StatusBarLongPressGestureDetector;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
@@ -69,7 +69,7 @@ public class PhoneStatusBarView extends FrameLayout {
private InsetsFetcher mInsetsFetcher;
private int mDensity;
private float mFontScale;
- private LongPressGestureDetector mLongPressGestureDetector;
+ private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -81,9 +81,10 @@ public class PhoneStatusBarView extends FrameLayout {
mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class);
}
- void setLongPressGestureDetector(LongPressGestureDetector longPressGestureDetector) {
+ void setLongPressGestureDetector(
+ StatusBarLongPressGestureDetector statusBarLongPressGestureDetector) {
if (ShadeExpandsOnStatusBarLongPress.isEnabled()) {
- mLongPressGestureDetector = longPressGestureDetector;
+ mStatusBarLongPressGestureDetector = statusBarLongPressGestureDetector;
}
}
@@ -207,8 +208,9 @@ public class PhoneStatusBarView extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (ShadeExpandsOnStatusBarLongPress.isEnabled() && mLongPressGestureDetector != null) {
- mLongPressGestureDetector.handleTouch(event);
+ if (ShadeExpandsOnStatusBarLongPress.isEnabled()
+ && mStatusBarLongPressGestureDetector != null) {
+ mStatusBarLongPressGestureDetector.handleTouch(event);
}
if (mTouchEventHandler == null) {
Log.w(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index c24f4327f471..16e023ce17fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -27,17 +27,18 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags
import com.android.systemui.Gefingerpoken
import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.dagger.qualifiers.DisplaySpecific
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.shade.LongPressGestureDetector
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.StatusBarLongPressGestureDetector
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
@@ -68,7 +69,7 @@ private constructor(
private val shadeController: ShadeController,
private val shadeViewController: ShadeViewController,
private val panelExpansionInteractor: PanelExpansionInteractor,
- private val longPressGestureDetector: Provider<LongPressGestureDetector>,
+ private val statusBarLongPressGestureDetector: Provider<StatusBarLongPressGestureDetector>,
private val windowRootView: Provider<WindowRootView>,
private val shadeLogger: ShadeLogger,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
@@ -118,7 +119,7 @@ private constructor(
addCursorSupportToIconContainers()
if (ShadeExpandsOnStatusBarLongPress.isEnabled) {
- mView.setLongPressGestureDetector(longPressGestureDetector.get())
+ mView.setLongPressGestureDetector(statusBarLongPressGestureDetector.get())
}
progressProvider?.setReadyToHandleTransition(true)
@@ -335,13 +336,13 @@ private constructor(
private val shadeController: ShadeController,
private val shadeViewController: ShadeViewController,
private val panelExpansionInteractor: PanelExpansionInteractor,
- private val longPressGestureDetector: Provider<LongPressGestureDetector>,
+ private val statusBarLongPressGestureDetector: Provider<StatusBarLongPressGestureDetector>,
private val windowRootView: Provider<WindowRootView>,
private val shadeLogger: ShadeLogger,
private val viewUtil: ViewUtil,
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
- private val darkIconDispatcher: DarkIconDispatcher,
+ @DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
@@ -360,7 +361,7 @@ private constructor(
shadeController,
shadeViewController,
panelExpansionInteractor,
- longPressGestureDetector,
+ statusBarLongPressGestureDetector,
windowRootView,
shadeLogger,
statusBarMoveFromCenterAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index d991b1df45fe..41db5f450df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -174,6 +174,14 @@ constructor(
private val dumpableName = TAG + nameSuffix
private val commandName = StatusBarInsetsCommand.NAME + nameSuffix
+ init {
+ if (!StatusBarConnectedDisplays.isEnabled) {
+ // Call start(), since it is not called when the flag is disabled, to keep the old
+ // behavior as it was.
+ start()
+ }
+ }
+
override fun start() {
configurationController.addCallback(this)
dumpManager.registerDumpable(dumpableName, this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0c511aeae3e5..aef26dea3c0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -520,6 +520,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mListenForCanShowAlternateBouncer.cancel(null);
}
mListenForCanShowAlternateBouncer = null;
+
// Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
@@ -568,6 +569,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
private void consumeCanShowAlternateBouncer(boolean canShow) {
+ if (SceneContainerFlag.isEnabled()) {
+ // When the scene framework is enabled, the alternative bouncer is hidden from the scene
+ // framework logic so there's no need for this logic here.
+ return;
+ }
+
// Hack: this is required to fix issues where
// KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never
// reset. This is caused by usages of show()/forceShow() that only read this flow to set the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
index c341bd3466ed..394502b2d31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
@@ -28,10 +28,13 @@ import androidx.annotation.ColorInt
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore
+import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -40,14 +43,14 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
class StatusOverlayHoverListenerFactory
@Inject
constructor(
@Main private val resources: Resources,
private val configurationController: ConfigurationController,
- private val darkIconDispatcher: SysuiDarkIconDispatcher,
+ private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore,
+ private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
) {
/** Creates listener always using the same light color for overlay */
@@ -63,7 +66,7 @@ constructor(
* Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
*/
fun createDarkAwareListener(view: View) =
- createDarkAwareListener(view, darkIconDispatcher.darkChangeFlow())
+ createDarkAwareListener(view, view.darkIconDispatcher.darkChangeFlow())
/**
* Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
@@ -78,7 +81,7 @@ constructor(
) =
createDarkAwareListener(
view,
- darkIconDispatcher.darkChangeFlow(),
+ view.darkIconDispatcher.darkChangeFlow(),
leftHoverMargin,
rightHoverMargin,
topHoverMargin,
@@ -92,8 +95,8 @@ constructor(
fun createDarkAwareListener(view: View, darkFlow: StateFlow<DarkChange>) =
StatusOverlayHoverListener(
view,
- configurationController,
- resources,
+ view.statusBarConfigurationController,
+ view.resources,
darkFlow.map { toHoverTheme(view, it) },
)
@@ -107,8 +110,8 @@ constructor(
) =
StatusOverlayHoverListener(
view,
- configurationController,
- resources,
+ view.statusBarConfigurationController,
+ view.resources,
darkFlow.map { toHoverTheme(view, it) },
leftHoverMargin,
rightHoverMargin,
@@ -116,6 +119,12 @@ constructor(
bottomHoverMargin,
)
+ private val View.statusBarConfigurationController
+ get() = statusBarConfigurationControllerStore.forDisplay(context.displayId)
+
+ private val View.darkIconDispatcher
+ get() = darkIconDispatcherStore.forDisplay(context.displayId)
+
private fun toHoverTheme(view: View, darkChange: DarkChange): HoverTheme {
val calculatedTint = DarkIconDispatcher.getTint(darkChange.areas, view, darkChange.tint)
// currently calculated tint is either white or some shade of black.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 23f3482d40bd..58386b0cad7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -30,8 +30,9 @@ import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.core.StatusBarInitializerImpl
import com.android.systemui.statusbar.core.StatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarOrchestrator
-import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule
+import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
@@ -48,7 +49,12 @@ import kotlinx.coroutines.CoroutineScope
/** Similar in purpose to [StatusBarModule], but scoped only to phones */
@Module(
- includes = [PrivacyDotViewControllerModule::class, PrivacyDotViewControllerStoreModule::class]
+ includes =
+ [
+ PrivacyDotViewControllerModule::class,
+ PrivacyDotWindowControllerStoreModule::class,
+ PrivacyDotViewControllerStoreModule::class,
+ ]
)
interface StatusBarPhoneModule {
@@ -81,7 +87,7 @@ interface StatusBarPhoneModule {
return if (StatusBarConnectedDisplays.isEnabled) {
// Will be started through MultiDisplayStatusBarStarter
CoreStartable.NOP
- } else if (StatusBarSimpleFragment.isEnabled) {
+ } else if (StatusBarRootModernization.isEnabled) {
defaultInitializerLazy.get()
} else {
// Will be started through CentralSurfaces
@@ -97,8 +103,12 @@ interface StatusBarPhoneModule {
fun statusBarInitializerImpl(
implFactory: StatusBarInitializerImpl.Factory,
statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
): StatusBarInitializerImpl {
- return implFactory.create(statusBarWindowControllerStore.defaultDisplay)
+ return implFactory.create(
+ statusBarWindowControllerStore.defaultDisplay,
+ statusBarModeRepositoryStore.defaultDisplay,
+ )
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
index ba377497bce4..49356eba2842 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone.data.repository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
+import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
import dagger.Binds
import dagger.Module
@@ -25,16 +25,16 @@ import kotlinx.coroutines.flow.StateFlow
/** Dark-mode state for tinting icons. */
interface DarkIconRepository {
- val darkState: StateFlow<DarkChange>
+ fun darkState(displayId: Int): StateFlow<DarkChange>
}
@SysUISingleton
class DarkIconRepositoryImpl
@Inject
-constructor(
- darkIconDispatcher: SysuiDarkIconDispatcher,
-) : DarkIconRepository {
- override val darkState: StateFlow<DarkChange> = darkIconDispatcher.darkChangeFlow()
+constructor(private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore) :
+ DarkIconRepository {
+ override fun darkState(displayId: Int): StateFlow<DarkChange> =
+ darkIconDispatcherStore.forDisplay(displayId).darkChangeFlow()
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
index 72f45406b35e..095f0cba8a46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
@@ -22,7 +22,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/** States pertaining to calculating colors for icons in dark mode. */
-class DarkIconInteractor @Inject constructor(repository: DarkIconRepository) {
+class DarkIconInteractor @Inject constructor(private val repository: DarkIconRepository) {
/** Dark-mode state for tinting icons. */
- val darkState: Flow<DarkState> = repository.darkState.map { DarkState(it.areas, it.tint) }
+ fun darkState(displayId: Int): Flow<DarkState> =
+ repository.darkState(displayId).map { DarkState(it.areas, it.tint) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 013141b5e142..23b4b65bb2ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -58,7 +58,7 @@ import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
-import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
+import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -351,8 +351,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
}
- mDarkIconManager = mDarkIconManagerFactory.create(
- view.findViewById(R.id.statusIcons), StatusBarLocation.HOME);
+ mDarkIconManager =
+ mDarkIconManagerFactory.create(
+ view.findViewById(R.id.statusIcons),
+ StatusBarLocation.HOME,
+ mHomeStatusBarComponent.getDarkIconDispatcher());
mDarkIconManager.setShouldLog(true);
updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
@@ -362,7 +365,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mPrimaryOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip_primary);
mSecondaryOngoingActivityChip =
mStatusBar.findViewById(R.id.ongoing_activity_chip_secondary);
- if (!StatusBarSimpleFragment.isEnabled()) {
+ if (!StatusBarRootModernization.isEnabled()) {
showEndSideContent(false);
showClock(false);
}
@@ -373,7 +376,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
mHomeStatusBarViewBinder.bind(
- mStatusBar, mHomeStatusBarViewModel, mStatusBarVisibilityChangeListener);
+ mStatusBar,
+ mHomeStatusBarViewModel,
+ /* systemEventChipAnimateIn */ null,
+ /* systemEventChipAnimateOut */ null,
+ mStatusBarVisibilityChangeListener);
}
private String getDumpableName() {
@@ -461,7 +468,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
super.onPause();
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
- if (!StatusBarSimpleFragment.isEnabled()) {
+ if (!StatusBarRootModernization.isEnabled()) {
mOngoingCallController.removeCallback(mOngoingCallListener);
}
mAnimationScheduler.removeCallback(this);
@@ -496,14 +503,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
mNotificationIconAreaInner = notificationIcons;
- if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ int displayId = mHomeStatusBarComponent.getDisplayId();
+ if (displayId == Display.DEFAULT_DISPLAY) {
//TODO(b/369337701): implement notification icons for all displays.
// Currently if we try to bind for all displays, there is a crash, because the same
// notification icon view can't have multiple parents.
- mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+ mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons, displayId);
}
- if (!StatusBarSimpleFragment.isEnabled()) {
+ if (!StatusBarRootModernization.isEnabled()) {
updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
}
Trace.endSection();
@@ -524,7 +532,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
new StatusBarVisibilityChangeListener() {
@Override
public void onStatusBarVisibilityMaybeChanged() {
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
return;
}
updateStatusBarVisibilities(/* animate= */ true);
@@ -532,7 +540,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onTransitionFromLockscreenToDreamStarted() {
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
return;
}
mTransitionFromLockscreenToDreamStarted = true;
@@ -543,7 +551,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
boolean hasPrimaryOngoingActivity,
boolean hasSecondaryOngoingActivity,
boolean shouldAnimate) {
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
return;
}
mHasPrimaryOngoingActivity = hasPrimaryOngoingActivity;
@@ -554,7 +562,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onIsHomeStatusBarAllowedBySceneChanged(
boolean isHomeStatusBarAllowedByScene) {
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
return;
}
mHomeStatusBarAllowedByScene = isHomeStatusBarAllowedByScene;
@@ -564,7 +572,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
return;
}
if (displayId != getContext().getDisplayId()) {
@@ -578,7 +586,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void updateStatusBarVisibilities(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
StatusBarVisibilityModel previousModel = mLastModifiedVisibility;
StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility);
@@ -619,7 +627,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private StatusBarVisibilityModel calculateInternalModel(
StatusBarVisibilityModel externalModel) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
// TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
boolean headsUpVisible =
@@ -673,7 +681,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* mLastModifiedVisibility.
*/
private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility;
boolean disableNotifications = !visibilityModel.getShowNotificationIcons();
@@ -710,7 +718,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private boolean shouldHideStatusBar() {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
boolean isDefaultDisplay = getContext().getDisplayId() == Display.DEFAULT_DISPLAY;
boolean shouldHideForCurrentDisplay =
@@ -772,7 +780,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void hideEndSideContent(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
if (!animate || !mAnimationsEnabled) {
mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER);
} else {
@@ -782,7 +790,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void showEndSideContent(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
if (!animate || !mAnimationsEnabled) {
mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER);
return;
@@ -799,18 +807,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void hideClock(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateHiddenState(mClockView, clockHiddenMode(), animate);
}
private void showClock(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateShow(mClockView, animate);
}
/** Hides the primary ongoing activity chip. */
private void hidePrimaryOngoingActivityChip(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateHiddenState(mPrimaryOngoingActivityChip, View.GONE, animate);
}
@@ -822,18 +830,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* activities. See b/332662551.
*/
private void showPrimaryOngoingActivityChip(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateShow(mPrimaryOngoingActivityChip, animate);
}
private void hideSecondaryOngoingActivityChip(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateHiddenState(mSecondaryOngoingActivityChip, View.GONE, animate);
}
private void showSecondaryOngoingActivityChip(boolean animate) {
StatusBarNotifChips.assertInNewMode();
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateShow(mSecondaryOngoingActivityChip, animate);
}
@@ -842,7 +850,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* don't set the clock GONE otherwise it'll mess up the animation.
*/
private int clockHiddenMode() {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
if (!mShadeExpansionStateManager.isClosed() && !mKeyguardStateController.isShowing()
&& !mStatusBarStateController.isDozing()) {
return View.INVISIBLE;
@@ -851,24 +859,24 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
public void hideNotificationIconArea(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateHide(mNotificationIconAreaInner, animate);
}
public void showNotificationIconArea(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateShow(mNotificationIconAreaInner, animate);
}
public void hideOperatorName(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
if (mOperatorNameViewController != null) {
animateHide(mOperatorNameViewController.getView(), animate);
}
}
public void showOperatorName(boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
if (mOperatorNameViewController != null) {
animateShow(mOperatorNameViewController.getView(), animate);
}
@@ -878,7 +886,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* Animate a view to INVISIBLE or GONE
*/
private void animateHiddenState(final View v, int state, boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
v.animate().cancel();
if (!animate || !mAnimationsEnabled) {
v.setAlpha(0f);
@@ -898,7 +906,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* Hides a view.
*/
private void animateHide(final View v, boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
animateHiddenState(v, View.INVISIBLE, animate);
}
@@ -906,7 +914,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable.
*/
private void animateShow(View v, boolean animate) {
- StatusBarSimpleFragment.assertInLegacyMode();
+ StatusBarRootModernization.assertInLegacyMode();
v.animate().cancel();
v.setVisibility(View.VISIBLE);
if (!animate || !mAnimationsEnabled) {
@@ -939,11 +947,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) {
View view = mStatusBar.findViewById(R.id.operator_name);
mOperatorNameViewController =
- mOperatorNameViewControllerFactory.create((OperatorNameView) view);
+ mOperatorNameViewControllerFactory.create(
+ (OperatorNameView) view,
+ mHomeStatusBarComponent.getDarkIconDispatcher());
mOperatorNameViewController.init();
// This view should not be visible on lock-screen
if (mKeyguardStateController.isShowing()) {
- if (!StatusBarSimpleFragment.isEnabled()) {
+ if (!StatusBarRootModernization.isEnabled()) {
hideOperatorName(false);
}
}
@@ -951,7 +961,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void initOngoingCallChip() {
- if (!StatusBarSimpleFragment.isEnabled()) {
+ if (!StatusBarRootModernization.isEnabled()) {
mOngoingCallController.addCallback(mOngoingCallListener);
}
// TODO(b/364653005): Do we also need to set the secondary activity chip?
@@ -963,7 +973,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onDozingChanged(boolean isDozing) {
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
return;
}
updateStatusBarVisibilities(/* animate= */ false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
index eaf15a8cbe17..e4929b574bd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
@@ -20,7 +20,7 @@ import android.view.View
import androidx.core.animation.Interpolator
import androidx.core.animation.ValueAnimator
import com.android.app.animation.InterpolatorsAndroidX
-import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.core.StatusBarRootModernization
/**
* A controller that keeps track of multiple sources applying alpha value changes to a view. It will
@@ -75,7 +75,7 @@ constructor(private val view: View, private val initialAlpha: Float = 1f) {
private fun applyAlphaToView() {
val minAlpha = getMinAlpha()
- if (!StatusBarSimpleFragment.isEnabled) {
+ if (!StatusBarRootModernization.isEnabled) {
view.visibility = if (minAlpha != 0f) View.VISIBLE else View.INVISIBLE
view.alpha = minAlpha
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
index 1f9ea08e602f..fd7bce099d81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -71,49 +71,87 @@ constructor(
override fun onSystemEventAnimationBegin(): Animator {
isAnimationRunning = true
- val moveOut =
- ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 23.frames
- interpolator = STATUS_BAR_X_MOVE_OUT
- addUpdateListener {
- onTranslationXChanged(-(translationXIn * animatedValue as Float))
- }
- }
- val alphaOut =
- ValueAnimator.ofFloat(1f, 0f).apply {
- duration = 8.frames
- interpolator = null
- addUpdateListener { onAlphaChanged(animatedValue as Float) }
- }
-
- val animSet = AnimatorSet()
- animSet.playTogether(moveOut, alphaOut)
- return animSet
+ return getDefaultStatusBarAnimationForChipEnter(
+ translationXIn,
+ onTranslationXChanged,
+ onAlphaChanged,
+ )
}
override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
onTranslationXChanged(translationXOut.toFloat())
- val moveIn =
- ValueAnimator.ofFloat(1f, 0f).apply {
- duration = 23.frames
- startDelay = 7.frames
- interpolator = STATUS_BAR_X_MOVE_IN
- addUpdateListener {
- onTranslationXChanged(translationXOut * animatedValue as Float)
+ val anim =
+ getDefaultStatusBarAnimationForChipExit(
+ translationXOut,
+ onTranslationXChanged,
+ onAlphaChanged,
+ )
+ anim.doOnEnd { isAnimationRunning = false }
+ anim.doOnCancel { isAnimationRunning = false }
+
+ return anim
+ }
+
+ /** Static definition of these animations so we can use them more easily from view binders */
+ companion object {
+ /**
+ * Chip: coming in. Animated view: going out.
+ *
+ * Implements the exact spec for animating any status bar elements OUT to make space for the
+ * chip IN animation.
+ */
+ fun getDefaultStatusBarAnimationForChipEnter(
+ targetTranslation: Int,
+ setX: (Float) -> Unit,
+ setAlpha: (Float) -> Unit,
+ ): Animator {
+ val moveOut =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 23.frames
+ interpolator = STATUS_BAR_X_MOVE_OUT
+ addUpdateListener { setX(-(targetTranslation * animatedValue as Float)) }
+ }
+ val alphaOut =
+ ValueAnimator.ofFloat(1f, 0f).apply {
+ duration = 8.frames
+ interpolator = null
+ addUpdateListener { setAlpha(animatedValue as Float) }
+ }
+
+ val animSet = AnimatorSet()
+ animSet.playTogether(moveOut, alphaOut)
+ return animSet
+ }
+
+ /**
+ * Chip: going out. Animated view: coming in.
+ *
+ * Implements the exact spec for animating any status bar elements IN as the chip is
+ * animating OUT
+ */
+ fun getDefaultStatusBarAnimationForChipExit(
+ targetTranslation: Int,
+ setX: (Float) -> Unit,
+ setAlpha: (Float) -> Unit,
+ ): Animator {
+ val moveIn =
+ ValueAnimator.ofFloat(1f, 0f).apply {
+ duration = 23.frames
+ startDelay = 7.frames
+ interpolator = STATUS_BAR_X_MOVE_IN
+ addUpdateListener { setX(targetTranslation * animatedValue as Float) }
+ }
+ val alphaIn =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 5.frames
+ startDelay = 11.frames
+ interpolator = null
+ addUpdateListener { setAlpha(animatedValue as Float) }
}
- }
- val alphaIn =
- ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 5.frames
- startDelay = 11.frames
- interpolator = null
- addUpdateListener { onAlphaChanged(animatedValue as Float) }
- }
- val animatorSet = AnimatorSet()
- animatorSet.playTogether(moveIn, alphaIn)
- animatorSet.doOnEnd { isAnimationRunning = false }
- animatorSet.doOnCancel { isAnimationRunning = false }
- return animatorSet
+ val animatorSet = AnimatorSet()
+ animatorSet.playTogether(moveIn, alphaIn)
+ return animatorSet
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
index d4cb625d3722..f8ad0f2324bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.phone.fragment.dagger;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dagger.qualifiers.DisplaySpecific;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController;
@@ -121,4 +123,12 @@ public interface HomeStatusBarComponent {
/** */
StatusBarBoundsProvider getBoundsProvider();
+
+ /** */
+ @DisplaySpecific
+ DarkIconDispatcher getDarkIconDispatcher();
+
+ /** */
+ @DisplaySpecific
+ int getDisplayId();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
index 45c53b05d478..182f8d7e2fd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
@@ -22,8 +22,10 @@ import android.view.ViewStub;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.DisplaySpecific;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
@@ -164,4 +166,12 @@ public interface HomeStatusBarModule {
return store.forDisplay(displayId);
}
+ /** */
+ @Provides
+ @HomeStatusBarScope
+ @DisplaySpecific
+ static DarkIconDispatcher darkIconDispatcher(
+ @DisplaySpecific int displayId, DarkIconDispatcherStore store) {
+ return store.forDisplay(displayId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
index 6c303303c8f8..8d314eeb8493 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone.ui;
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
@@ -29,35 +28,34 @@ import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
-import javax.inject.Inject;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
-/**
- * Version of {@link IconManager} that observes state from the DarkIconDispatcher.
- */
+/** Version of {@link IconManager} that observes state from the {@link DarkIconDispatcher}. */
public class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
private final int mIconHorizontalMargin;
+ @AssistedInject
public DarkIconManager(
- LinearLayout linearLayout,
- StatusBarLocation location,
+ @Assisted LinearLayout linearLayout,
+ @Assisted StatusBarLocation location,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider,
- DarkIconDispatcher darkIconDispatcher) {
- super(linearLayout,
- location,
- wifiUiAdapter,
- mobileUiAdapter,
- mobileContextProvider);
- mIconHorizontalMargin = mContext.getResources().getDimensionPixelSize(
- com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin);
+ @Assisted DarkIconDispatcher darkIconDispatcher) {
+ super(linearLayout, location, wifiUiAdapter, mobileUiAdapter, mobileContextProvider);
+ mIconHorizontalMargin =
+ mContext.getResources()
+ .getDimensionPixelSize(
+ com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin);
mDarkIconDispatcher = darkIconDispatcher;
}
@Override
- protected void onIconAdded(int index, String slot, boolean blocked,
- StatusBarIconHolder holder) {
+ protected void onIconAdded(
+ int index, String slot, boolean blocked, StatusBarIconHolder holder) {
StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
mDarkIconDispatcher.addDarkReceiver(view);
}
@@ -106,34 +104,14 @@ public class DarkIconManager extends IconManager {
super.exitDemoMode();
}
- @SysUISingleton
- public static class Factory {
- private final WifiUiAdapter mWifiUiAdapter;
- private final MobileContextProvider mMobileContextProvider;
- private final MobileUiAdapter mMobileUiAdapter;
- private final DarkIconDispatcher mDarkIconDispatcher;
-
- @Inject
- public Factory(
- WifiUiAdapter wifiUiAdapter,
- MobileContextProvider mobileContextProvider,
- MobileUiAdapter mobileUiAdapter,
- DarkIconDispatcher darkIconDispatcher) {
- mWifiUiAdapter = wifiUiAdapter;
- mMobileContextProvider = mobileContextProvider;
- mMobileUiAdapter = mobileUiAdapter;
- mDarkIconDispatcher = darkIconDispatcher;
- }
+ /** */
+ @AssistedFactory
+ public interface Factory {
- /** Creates a new {@link DarkIconManager} for the given view group and location. */
- public DarkIconManager create(LinearLayout group, StatusBarLocation location) {
- return new DarkIconManager(
- group,
- location,
- mWifiUiAdapter,
- mMobileUiAdapter,
- mMobileContextProvider,
- mDarkIconDispatcher);
- }
+ /** Creates a new {@link DarkIconManager}. */
+ DarkIconManager create(
+ LinearLayout group,
+ StatusBarLocation location,
+ DarkIconDispatcher darkIconDispatcher);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 935b1012be31..bfdc8bd5e4c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -23,6 +23,8 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepository
+import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepositoryImpl
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -85,6 +87,11 @@ abstract class StatusBarPipelineModule {
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
@Binds
+ abstract fun systemStatusEventAnimationRepository(
+ impl: SystemStatusEventAnimationRepositoryImpl
+ ): SystemStatusEventAnimationRepository
+
+ @Binds
abstract fun realDeviceBasedSatelliteRepository(
impl: DeviceBasedSatelliteRepositoryImpl
): RealDeviceBasedSatelliteRepository
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 9cbfc440ab16..94e9d26c9dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -21,6 +21,7 @@ import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
+import android.telephony.satellite.NtnSignalStrength
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.dagger.SysUISingleton
@@ -31,11 +32,7 @@ import javax.inject.Inject
/** Logs for inputs into the mobile pipeline. */
@SysUISingleton
-class MobileInputLogger
-@Inject
-constructor(
- @MobileInputLog private val buffer: LogBuffer,
-) {
+class MobileInputLogger @Inject constructor(@MobileInputLog private val buffer: LogBuffer) {
fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) {
buffer.log(
TAG,
@@ -49,7 +46,7 @@ constructor(
{
"onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" +
" operator=$str1"
- }
+ },
)
}
@@ -61,7 +58,7 @@ constructor(
int1 = subId
bool1 = serviceState.isEmergencyOnly
},
- { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" }
+ { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" },
)
}
@@ -70,7 +67,7 @@ constructor(
TAG,
LogLevel.INFO,
{ int1 = subId },
- { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" }
+ { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" },
)
}
@@ -82,7 +79,16 @@ constructor(
int1 = subId
str1 = signalStrength.toString()
},
- { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" }
+ { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" },
+ )
+ }
+
+ fun logNtnSignalStrengthChanged(signalStrength: NtnSignalStrength) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { int1 = signalStrength.level },
+ { "onCarrierRoamingNtnSignalStrengthChanged: level=$int1" },
)
}
@@ -128,7 +134,7 @@ constructor(
TAG,
LogLevel.INFO,
{ bool1 = active },
- { "onCarrierRoamingNtnModeChanged: $bool1" }
+ { "onCarrierRoamingNtnModeChanged: $bool1" },
)
}
@@ -146,12 +152,7 @@ constructor(
}
fun logCarrierConfigChanged(subId: Int) {
- buffer.log(
- TAG,
- LogLevel.INFO,
- { int1 = subId },
- { "onCarrierConfigChanged: subId=$int1" },
- )
+ buffer.log(TAG, LogLevel.INFO, { int1 = subId }, { "onCarrierConfigChanged: subId=$int1" })
}
fun logOnDataEnabledChanged(enabled: Boolean, subId: Int) {
@@ -175,7 +176,7 @@ constructor(
TAG,
LogLevel.INFO,
{ str1 = config.toString() },
- { "defaultDataSubRatConfig: $str1" }
+ { "defaultDataSubRatConfig: $str1" },
)
}
@@ -184,7 +185,7 @@ constructor(
TAG,
LogLevel.INFO,
{ str1 = mapping.toString() },
- { "defaultMobileIconMapping: $str1" }
+ { "defaultMobileIconMapping: $str1" },
)
}
@@ -216,7 +217,7 @@ constructor(
{
"Intent: ACTION_SERVICE_PROVIDERS_UPDATED." +
" showSpn=$bool1 spn=$str1 dataSpn=$str2 showPlmn=$bool2 plmn=$str3"
- }
+ },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 205205eac210..07843f1ef041 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -107,6 +107,12 @@ interface MobileConnectionRepository {
// @IntRange(from = 0, to = 4)
val primaryLevel: StateFlow<Int>
+ /**
+ * This level can be used to reflect the signal strength when in carrier roaming NTN mode
+ * (carrier-based satellite)
+ */
+ val satelliteLevel: StateFlow<Int>
+
/** The current data connection state. See [DataConnectionState] */
val dataConnectionState: StateFlow<DataConnectionState>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 3261b71ece3c..be3977ecd4ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -37,12 +37,14 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullM
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_OPERATOR
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_PRIMARY_LEVEL
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_ROAMING
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_SATELLITE_LEVEL
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -75,7 +77,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = "inflate",
- _inflateSignalStrength.value
+ _inflateSignalStrength.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value)
@@ -89,7 +91,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_EMERGENCY,
- _isEmergencyOnly.value
+ _isEmergencyOnly.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _isEmergencyOnly.value)
@@ -100,7 +102,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_ROAMING,
- _isRoaming.value
+ _isRoaming.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _isRoaming.value)
@@ -111,7 +113,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_OPERATOR,
- _operatorAlphaShort.value
+ _operatorAlphaShort.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _operatorAlphaShort.value)
@@ -122,7 +124,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_IS_IN_SERVICE,
- _isInService.value
+ _isInService.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _isInService.value)
@@ -133,7 +135,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_IS_NTN,
- _isNonTerrestrial.value
+ _isNonTerrestrial.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _isNonTerrestrial.value)
@@ -144,7 +146,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_IS_GSM,
- _isGsm.value
+ _isGsm.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _isGsm.value)
@@ -155,7 +157,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_CDMA_LEVEL,
- _cdmaLevel.value
+ _cdmaLevel.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _cdmaLevel.value)
@@ -166,10 +168,21 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_PRIMARY_LEVEL,
- _primaryLevel.value
+ _primaryLevel.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _primaryLevel.value)
+ private val _satelliteLevel = MutableStateFlow(0)
+ override val satelliteLevel: StateFlow<Int> =
+ _satelliteLevel
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = COL_SATELLITE_LEVEL,
+ _satelliteLevel.value,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), _satelliteLevel.value)
+
private val _dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected)
override val dataConnectionState =
_dataConnectionState
@@ -177,12 +190,7 @@ class DemoMobileConnectionRepository(
.stateIn(scope, SharingStarted.WhileSubscribed(), _dataConnectionState.value)
private val _dataActivityDirection =
- MutableStateFlow(
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = false,
- )
- )
+ MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
override val dataActivityDirection =
_dataActivityDirection
.logDiffsForTable(tableLogBuffer, columnPrefix = "", _dataActivityDirection.value)
@@ -195,7 +203,7 @@ class DemoMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_CARRIER_NETWORK_CHANGE,
- _carrierNetworkChangeActive.value
+ _carrierNetworkChangeActive.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _carrierNetworkChangeActive.value)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index 2e4767893c3d..75f613d7e3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -90,7 +90,7 @@ class CarrierMergedConnectionRepository(
TAG,
"Connection repo subId=$subId " +
"does not equal wifi repo subId=${network.subscriptionId}; " +
- "not showing carrier merged"
+ "not showing carrier merged",
)
null
}
@@ -149,7 +149,7 @@ class CarrierMergedConnectionRepository(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- ResolvedNetworkType.UnknownNetworkType
+ ResolvedNetworkType.UnknownNetworkType,
)
override val dataConnectionState =
@@ -173,6 +173,7 @@ class CarrierMergedConnectionRepository(
override val isNonTerrestrial = MutableStateFlow(false).asStateFlow()
override val isGsm = MutableStateFlow(false).asStateFlow()
override val carrierNetworkChangeActive = MutableStateFlow(false).asStateFlow()
+ override val satelliteLevel = MutableStateFlow(0)
/**
* Carrier merged connections happen over wifi but are displayed as a mobile triangle. Because
@@ -207,10 +208,7 @@ class CarrierMergedConnectionRepository(
@Application private val scope: CoroutineScope,
private val wifiRepository: WifiRepository,
) {
- fun build(
- subId: Int,
- mobileLogger: TableLogBuffer,
- ): MobileConnectionRepository {
+ fun build(subId: Int, mobileLogger: TableLogBuffer): MobileConnectionRepository {
return CarrierMergedConnectionRepository(
subId,
mobileLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index a5e47a6e68cd..fae9be083e12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -132,12 +132,12 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_EMERGENCY,
- activeRepo.value.isEmergencyOnly.value
+ activeRepo.value.isEmergencyOnly.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.isEmergencyOnly.value
+ activeRepo.value.isEmergencyOnly.value,
)
override val isRoaming =
@@ -147,7 +147,7 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_ROAMING,
- activeRepo.value.isRoaming.value
+ activeRepo.value.isRoaming.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isRoaming.value)
@@ -158,12 +158,12 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_OPERATOR,
- activeRepo.value.operatorAlphaShort.value
+ activeRepo.value.operatorAlphaShort.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.operatorAlphaShort.value
+ activeRepo.value.operatorAlphaShort.value,
)
override val isInService =
@@ -173,7 +173,7 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_IS_IN_SERVICE,
- activeRepo.value.isInService.value
+ activeRepo.value.isInService.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isInService.value)
@@ -184,12 +184,12 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_IS_NTN,
- activeRepo.value.isNonTerrestrial.value
+ activeRepo.value.isNonTerrestrial.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.isNonTerrestrial.value
+ activeRepo.value.isNonTerrestrial.value,
)
override val isGsm =
@@ -199,7 +199,7 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_IS_GSM,
- activeRepo.value.isGsm.value
+ activeRepo.value.isGsm.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isGsm.value)
@@ -210,7 +210,7 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_CDMA_LEVEL,
- activeRepo.value.cdmaLevel.value
+ activeRepo.value.cdmaLevel.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaLevel.value)
@@ -221,22 +221,33 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_PRIMARY_LEVEL,
- activeRepo.value.primaryLevel.value
+ activeRepo.value.primaryLevel.value,
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.primaryLevel.value)
+ override val satelliteLevel: StateFlow<Int> =
+ activeRepo
+ .flatMapLatest { it.satelliteLevel }
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = COL_SATELLITE_LEVEL,
+ activeRepo.value.satelliteLevel.value,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.satelliteLevel.value)
+
override val dataConnectionState =
activeRepo
.flatMapLatest { it.dataConnectionState }
.logDiffsForTable(
tableLogBuffer,
columnPrefix = "",
- activeRepo.value.dataConnectionState.value
+ activeRepo.value.dataConnectionState.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.dataConnectionState.value
+ activeRepo.value.dataConnectionState.value,
)
override val dataActivityDirection =
@@ -245,12 +256,12 @@ class FullMobileConnectionRepository(
.logDiffsForTable(
tableLogBuffer,
columnPrefix = "",
- activeRepo.value.dataActivityDirection.value
+ activeRepo.value.dataActivityDirection.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.dataActivityDirection.value
+ activeRepo.value.dataActivityDirection.value,
)
override val carrierNetworkChangeActive =
@@ -260,12 +271,12 @@ class FullMobileConnectionRepository(
tableLogBuffer,
columnPrefix = "",
columnName = COL_CARRIER_NETWORK_CHANGE,
- activeRepo.value.carrierNetworkChangeActive.value
+ activeRepo.value.carrierNetworkChangeActive.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.carrierNetworkChangeActive.value
+ activeRepo.value.carrierNetworkChangeActive.value,
)
override val resolvedNetworkType =
@@ -274,12 +285,12 @@ class FullMobileConnectionRepository(
.logDiffsForTable(
tableLogBuffer,
columnPrefix = "",
- activeRepo.value.resolvedNetworkType.value
+ activeRepo.value.resolvedNetworkType.value,
)
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.resolvedNetworkType.value
+ activeRepo.value.resolvedNetworkType.value,
)
override val dataEnabled =
@@ -305,7 +316,7 @@ class FullMobileConnectionRepository(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.inflateSignalStrength.value
+ activeRepo.value.inflateSignalStrength.value,
)
override val allowNetworkSliceIndicator =
@@ -320,7 +331,7 @@ class FullMobileConnectionRepository(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- activeRepo.value.allowNetworkSliceIndicator.value
+ activeRepo.value.allowNetworkSliceIndicator.value,
)
override val numberOfLevels =
@@ -439,6 +450,7 @@ class FullMobileConnectionRepository(
const val COL_IS_IN_SERVICE = "isInService"
const val COL_OPERATOR = "operatorName"
const val COL_PRIMARY_LEVEL = "primaryLevel"
+ const val COL_SATELLITE_LEVEL = "satelliteLevel"
const val COL_ROAMING = "roaming"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 62bd8ad4317c..8a1e7f9a0096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -41,6 +41,7 @@ import android.telephony.TelephonyManager.ERI_ON
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
+import android.telephony.satellite.NtnSignalStrength
import com.android.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -173,7 +174,7 @@ class MobileConnectionRepositoryImpl(
override fun onDataConnectionStateChanged(
dataState: Int,
- networkType: Int
+ networkType: Int,
) {
logger.logOnDataConnectionStateChanged(dataState, networkType, subId)
trySend(CallbackEvent.OnDataConnectionStateChanged(dataState))
@@ -195,6 +196,17 @@ class MobileConnectionRepositoryImpl(
logger.logOnSignalStrengthsChanged(signalStrength, subId)
trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength))
}
+
+ override fun onCarrierRoamingNtnSignalStrengthChanged(
+ signalStrength: NtnSignalStrength
+ ) {
+ logger.logNtnSignalStrengthChanged(signalStrength)
+ trySend(
+ CallbackEvent.OnCarrierRoamingNtnSignalStrengthChanged(
+ signalStrength
+ )
+ )
+ }
}
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
@@ -267,6 +279,12 @@ class MobileConnectionRepositoryImpl(
.map { it.signalStrength.level }
.stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
+ override val satelliteLevel: StateFlow<Int> =
+ callbackEvents
+ .mapNotNull { it.onCarrierRoamingNtnSignalStrengthChanged }
+ .map { it.signalStrength.level }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+
override val dataConnectionState =
callbackEvents
.mapNotNull { it.onDataConnectionStateChanged }
@@ -280,7 +298,7 @@ class MobileConnectionRepositoryImpl(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ DataActivityModel(hasActivityIn = false, hasActivityOut = false),
)
override val carrierNetworkChangeActive =
@@ -385,7 +403,7 @@ class MobileConnectionRepositoryImpl(
if (
intent.getIntExtra(
EXTRA_SUBSCRIPTION_INDEX,
- INVALID_SUBSCRIPTION_ID
+ INVALID_SUBSCRIPTION_ID,
) == subId
) {
logger.logServiceProvidersUpdatedBroadcast(intent)
@@ -399,7 +417,7 @@ class MobileConnectionRepositoryImpl(
context.registerReceiver(
receiver,
- IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)
+ IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED),
)
awaitClose { context.unregisterReceiver(receiver) }
@@ -524,6 +542,9 @@ sealed interface CallbackEvent {
data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
+
+ data class OnCarrierRoamingNtnSignalStrengthChanged(val signalStrength: NtnSignalStrength) :
+ CallbackEvent
}
/**
@@ -539,6 +560,9 @@ data class TelephonyCallbackState(
val onDisplayInfoChanged: CallbackEvent.OnDisplayInfoChanged? = null,
val onServiceStateChanged: CallbackEvent.OnServiceStateChanged? = null,
val onSignalStrengthChanged: CallbackEvent.OnSignalStrengthChanged? = null,
+ val onCarrierRoamingNtnSignalStrengthChanged:
+ CallbackEvent.OnCarrierRoamingNtnSignalStrengthChanged? =
+ null,
) {
fun applyEvent(event: CallbackEvent): TelephonyCallbackState {
return when (event) {
@@ -555,6 +579,8 @@ data class TelephonyCallbackState(
copy(onServiceStateChanged = event)
}
is CallbackEvent.OnSignalStrengthChanged -> copy(onSignalStrengthChanged = event)
+ is CallbackEvent.OnCarrierRoamingNtnSignalStrengthChanged ->
+ copy(onCarrierRoamingNtnSignalStrengthChanged = event)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 4ef328cf1623..1bf14af7ea6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -335,7 +335,11 @@ class MobileIconInteractorImpl(
// Satellite level is unaffected by the inflateSignalStrength property
// See b/346904529 for details
private val satelliteShownLevel: StateFlow<Int> =
- combine(level, isInService) { level, isInService -> if (isInService) level else 0 }
+ if (Flags.carrierRoamingNbIotNtn()) {
+ connectionRepository.satelliteLevel
+ } else {
+ combine(level, isInService) { level, isInService -> if (isInService) level else 0 }
+ }
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
private val cellularIcon: Flow<SignalIconModel.Cellular> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
index 1d08f2ba9522..98eed848bab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
@@ -37,6 +37,9 @@ interface DeviceBasedSatelliteRepository {
/** Clients must observe this property, as device-based satellite is location-dependent */
val isSatelliteAllowedForCurrentLocation: StateFlow<Boolean>
+
+ /** When enabled, a satellite icon will display when all other connections are OOS */
+ val isOpportunisticSatelliteIconEnabled: Boolean
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
index 58c30e018efd..de42b9229715 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
@@ -97,6 +97,9 @@ constructor(
}
.stateIn(scope, SharingStarted.WhileSubscribed(), realImpl)
+ override val isOpportunisticSatelliteIconEnabled: Boolean
+ get() = activeRepo.value.isOpportunisticSatelliteIconEnabled
+
override val isSatelliteProvisioned: StateFlow<Boolean> =
activeRepo
.flatMapLatest { it.isSatelliteProvisioned }
@@ -118,6 +121,6 @@ constructor(
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
- realImpl.isSatelliteAllowedForCurrentLocation.value
+ realImpl.isSatelliteAllowedForCurrentLocation.value,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
index d557bbf306a6..755899f80928 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
@@ -16,15 +16,18 @@
package com.android.systemui.statusbar.pipeline.satellite.data.demo
+import android.content.res.Resources
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
/** A satellite repository that represents the latest satellite values sent via demo mode. */
@SysUISingleton
@@ -33,9 +36,13 @@ class DemoDeviceBasedSatelliteRepository
constructor(
private val dataSource: DemoDeviceBasedSatelliteDataSource,
@Application private val scope: CoroutineScope,
+ @Main resources: Resources,
) : DeviceBasedSatelliteRepository {
private var demoCommandJob: Job? = null
+ override val isOpportunisticSatelliteIconEnabled =
+ resources.getBoolean(R.bool.config_showOpportunisticSatelliteIcon)
+
override val isSatelliteProvisioned = MutableStateFlow(true)
override val connectionState = MutableStateFlow(SatelliteConnectionState.Unknown)
override val signalStrength = MutableStateFlow(0)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 7686338fd9eb..a36ef56e57a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.satellite.data.prod
+import android.content.res.Resources
import android.os.OutcomeReceiver
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
@@ -27,14 +28,17 @@ import android.telephony.satellite.SatelliteModemStateCallback
import android.telephony.satellite.SatelliteProvisionStateCallback
import android.telephony.satellite.SatelliteSupportedStateCallback
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.MessageInitializer
import com.android.systemui.log.core.MessagePrinter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
import com.android.systemui.statusbar.pipeline.dagger.VerboseDeviceBasedSatelliteInputLog
import com.android.systemui.statusbar.pipeline.satellite.data.RealDeviceBasedSatelliteRepository
@@ -66,7 +70,6 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
@@ -146,10 +149,14 @@ constructor(
@DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
@VerboseDeviceBasedSatelliteInputLog private val verboseLogBuffer: LogBuffer,
private val systemClock: SystemClock,
+ @Main resources: Resources,
) : RealDeviceBasedSatelliteRepository {
private val satelliteManager: SatelliteManager?
+ override val isOpportunisticSatelliteIconEnabled: Boolean =
+ resources.getBoolean(R.bool.config_showOpportunisticSatelliteIcon)
+
// Some calls into satellite manager will throw exceptions if it is not supported.
// This is never expected to change after boot, but may need to be retried in some cases
@get:VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index f1a444f0158a..08a98c397d5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -53,6 +53,9 @@ constructor(
@DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
@DeviceBasedSatelliteTableLog private val tableLog: TableLogBuffer,
) {
+ /** Whether or not we should show the satellite icon when all connections are OOS */
+ val isOpportunisticSatelliteIconEnabled = repo.isOpportunisticSatelliteIconEnabled
+
/** Must be observed by any UI showing Satellite iconography */
val isSatelliteAllowed =
if (Flags.oemEnabledSatelliteFlag()) {
@@ -93,12 +96,7 @@ constructor(
flowOf(0)
}
.distinctUntilChanged()
- .logDiffsForTable(
- tableLog,
- columnPrefix = "",
- columnName = COL_LEVEL,
- initialValue = 0,
- )
+ .logDiffsForTable(tableLog, columnPrefix = "", columnName = COL_LEVEL, initialValue = 0)
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
val isSatelliteProvisioned = repo.isSatelliteProvisioned
@@ -132,10 +130,9 @@ constructor(
/** When all connections are considered OOS, satellite connectivity is potentially valid */
val areAllConnectionsOutOfService =
if (Flags.oemEnabledSatelliteFlag()) {
- combine(
- allConnectionsOos,
- iconsInteractor.isDeviceInEmergencyCallsOnlyMode,
- ) { connectionsOos, deviceEmergencyOnly ->
+ combine(allConnectionsOos, iconsInteractor.isDeviceInEmergencyCallsOnlyMode) {
+ connectionsOos,
+ deviceEmergencyOnly ->
logBuffer.log(
TAG,
LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 37f2f195ebf6..13ac321473f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -77,35 +77,38 @@ constructor(
// This adds a 10 seconds delay before showing the icon
private val shouldShowIconForOosAfterHysteresis: StateFlow<Boolean> =
- interactor.areAllConnectionsOutOfService
- .flatMapLatest { shouldShow ->
- if (shouldShow) {
- logBuffer.log(
- TAG,
- LogLevel.INFO,
- { long1 = DELAY_DURATION.inWholeSeconds },
- { "Waiting $long1 seconds before showing the satellite icon" }
+ if (interactor.isOpportunisticSatelliteIconEnabled) {
+ interactor.areAllConnectionsOutOfService
+ .flatMapLatest { shouldShow ->
+ if (shouldShow) {
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { long1 = DELAY_DURATION.inWholeSeconds },
+ { "Waiting $long1 seconds before showing the satellite icon" },
+ )
+ delay(DELAY_DURATION)
+ flowOf(true)
+ } else {
+ flowOf(false)
+ }
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLog,
+ columnPrefix = "vm",
+ columnName = COL_VISIBLE_FOR_OOS,
+ initialValue = false,
)
- delay(DELAY_DURATION)
- flowOf(true)
- } else {
- flowOf(false)
- }
+ } else {
+ flowOf(false)
}
- .distinctUntilChanged()
- .logDiffsForTable(
- tableLog,
- columnPrefix = "vm",
- columnName = COL_VISIBLE_FOR_OOS,
- initialValue = false,
- )
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
private val canShowIcon =
- combine(
- interactor.isSatelliteAllowed,
- interactor.isSatelliteProvisioned,
- ) { allowed, provisioned ->
+ combine(interactor.isSatelliteAllowed, interactor.isSatelliteProvisioned) {
+ allowed,
+ provisioned ->
allowed && provisioned
}
@@ -141,11 +144,10 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val icon: StateFlow<Icon?> =
- combine(
- showIcon,
- interactor.connectionState,
- interactor.signalStrength,
- ) { shouldShow, state, signalStrength ->
+ combine(showIcon, interactor.connectionState, interactor.signalStrength) {
+ shouldShow,
+ state,
+ signalStrength ->
if (shouldShow) {
SatelliteIconModel.fromConnectionState(state, signalStrength)
} else {
@@ -155,10 +157,7 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
override val carrierText: StateFlow<String?> =
- combine(
- showIcon,
- interactor.connectionState,
- ) { shouldShow, connectionState ->
+ combine(showIcon, interactor.connectionState) { shouldShow, connectionState ->
logBuffer.log(
TAG,
LogLevel.INFO,
@@ -166,7 +165,7 @@ constructor(
bool1 = shouldShow
str1 = connectionState.name
},
- { "Updating carrier text. shouldShow=$bool1 connectionState=$str1" }
+ { "Updating carrier text. shouldShow=$bool1 connectionState=$str1" },
)
if (shouldShow) {
when (connectionState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 11d73397ca22..72df02714d43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -22,6 +22,7 @@ import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -30,13 +31,16 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel].
@@ -46,10 +50,16 @@ interface HomeStatusBarViewBinder {
/**
* Binds the view to the view-model. [listener] will be notified whenever an event that may
* change the status bar visibility occurs.
+ *
+ * Null chip animations are used when [StatusBarRootModernization] is off (i.e., when we are
+ * binding from the fragment). If non-null, they control the animation of the system icon area
+ * to support the chip animations.
*/
fun bind(
view: View,
viewModel: HomeStatusBarViewModel,
+ systemEventChipAnimateIn: ((View) -> Unit)?,
+ systemEventChipAnimateOut: ((View) -> Unit)?,
listener: StatusBarVisibilityChangeListener,
)
}
@@ -59,6 +69,8 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
override fun bind(
view: View,
viewModel: HomeStatusBarViewModel,
+ systemEventChipAnimateIn: ((View) -> Unit)?,
+ systemEventChipAnimateOut: ((View) -> Unit)?,
listener: StatusBarVisibilityChangeListener,
) {
view.repeatWhenAttached {
@@ -91,10 +103,11 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
launch {
viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView)
- if (StatusBarSimpleFragment.isEnabled) {
+ if (StatusBarRootModernization.isEnabled) {
when (primaryChipModel) {
is OngoingActivityChipModel.Shown ->
primaryChipView.show(shouldAnimateChange = true)
+
is OngoingActivityChipModel.Hidden ->
primaryChipView.hide(
state = View.GONE,
@@ -109,6 +122,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
hasSecondaryOngoingActivity = false,
shouldAnimate = true,
)
+
is OngoingActivityChipModel.Hidden ->
listener.onOngoingActivityStatusChanged(
hasPrimaryOngoingActivity = false,
@@ -133,7 +147,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
// enough space for it.
OngoingActivityChipBinder.bind(chips.secondary, secondaryChipView)
- if (StatusBarSimpleFragment.isEnabled) {
+ if (StatusBarRootModernization.isEnabled) {
primaryChipView.adjustVisibility(chips.primary.toVisibilityModel())
secondaryChipView.adjustVisibility(
chips.secondary.toVisibilityModel()
@@ -160,7 +174,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
}
}
- if (StatusBarSimpleFragment.isEnabled) {
+ if (StatusBarRootModernization.isEnabled) {
val clockView = view.requireViewById<View>(R.id.clock)
launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } }
@@ -175,10 +189,30 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
view.requireViewById<View>(R.id.status_bar_end_side_content)
// TODO(b/364360986): Also handle operator name view.
launch {
- viewModel.isSystemInfoVisible.collect {
- systemInfoView.adjustVisibility(it)
- // TODO(b/364360986): The system info view has a custom alpha controller
- // in CollapsedStatusBarFragment.
+ viewModel.systemInfoCombinedVis.collect { (baseVis, animState) ->
+ // Broadly speaking, the baseVis controls the view.visibility, and
+ // the animation state uses only alpha to achieve its effect. This
+ // means that we can always modify the visibility, and if we're
+ // animating we can use the animState to handle it. If we are not
+ // animating, then we can use the baseVis default animation
+ if (animState.isAnimatingChip()) {
+ // Just apply the visibility of the view, but don't animate
+ systemInfoView.visibility = baseVis.visibility
+ // Now apply the animation state, with its animator
+ when (animState) {
+ AnimatingIn -> {
+ systemEventChipAnimateIn?.invoke(systemInfoView)
+ }
+ AnimatingOut -> {
+ systemEventChipAnimateOut?.invoke(systemInfoView)
+ }
+ else -> {
+ // Nothing to do here
+ }
+ }
+ } else {
+ systemInfoView.adjustVisibility(baseVis)
+ }
}
}
}
@@ -186,6 +220,14 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde
}
}
+ private fun SystemEventAnimationState.isAnimatingChip() =
+ when (this) {
+ AnimatingIn,
+ AnimatingOut,
+ RunningChipAnim -> true
+ else -> false
+ }
+
private fun OngoingActivityChipModel.toVisibilityModel(): VisibilityModel {
return VisibilityModel(
visibility = if (this is OngoingActivityChipModel.Shown) View.VISIBLE else View.GONE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index a472318a1e86..1faa9f32af1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -31,7 +31,11 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
+import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.PhoneStatusBarView
@@ -44,7 +48,6 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarVie
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Factory to simplify the dependency management for [StatusBarRoot] */
class StatusBarRootFactory
@@ -56,6 +59,8 @@ constructor(
private val darkIconManagerFactory: DarkIconManager.Factory,
private val iconController: StatusBarIconController,
private val ongoingCallController: OngoingCallController,
+ private val darkIconDispatcherStore: DarkIconDispatcherStore,
+ private val eventAnimationInteractor: SystemStatusEventAnimationInteractor,
) {
fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView {
val composeView = ComposeView(root.context)
@@ -69,6 +74,8 @@ constructor(
darkIconManagerFactory = darkIconManagerFactory,
iconController = iconController,
ongoingCallController = ongoingCallController,
+ darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId),
+ eventAnimationInteractor = eventAnimationInteractor,
onViewCreated = andThen,
)
}
@@ -97,9 +104,11 @@ fun StatusBarRoot(
darkIconManagerFactory: DarkIconManager.Factory,
iconController: StatusBarIconController,
ongoingCallController: OngoingCallController,
+ darkIconDispatcher: DarkIconDispatcher,
+ eventAnimationInteractor: SystemStatusEventAnimationInteractor,
onViewCreated: (ViewGroup) -> Unit,
) {
- // None of these methods are used when [StatusBarSimpleFragment] is on.
+ // None of these methods are used when [StatusBarRootModernization] is on.
// This can be deleted once the fragment is gone
val nopVisibilityChangeListener =
object : StatusBarVisibilityChangeListener {
@@ -135,7 +144,11 @@ fun StatusBarRoot(
phoneStatusBarView.requireViewById<StatusIconContainer>(R.id.statusIcons)
// TODO(b/364360986): turn this into a repo/intr/viewmodel
val darkIconManager =
- darkIconManagerFactory.create(statusIconContainer, StatusBarLocation.HOME)
+ darkIconManagerFactory.create(
+ statusIconContainer,
+ StatusBarLocation.HOME,
+ darkIconDispatcher,
+ )
iconController.addIconGroup(darkIconManager)
// TODO(b/372657935): This won't be needed once OngoingCallController is
@@ -157,9 +170,13 @@ fun StatusBarRoot(
// TODO(b/369337701): implement notification icons for all displays.
// Currently if we try to bind for all displays, there is a crash, because the
// same notification icon view can't have multiple parents.
- if (context.displayId == Display.DEFAULT_DISPLAY) {
+ val displayId = context.displayId
+ if (displayId == Display.DEFAULT_DISPLAY) {
scope.launch {
- notificationIconsBinder.bindWhileAttached(notificationIconContainer)
+ notificationIconsBinder.bindWhileAttached(
+ notificationIconContainer,
+ displayId,
+ )
}
}
@@ -168,6 +185,8 @@ fun StatusBarRoot(
statusBarViewBinder.bind(
phoneStatusBarView,
statusBarViewModel,
+ eventAnimationInteractor::animateStatusBarContentForChipEnter,
+ eventAnimationInteractor::animateStatusBarContentForChipExit,
nopVisibilityChangeListener,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 4277a8be64d3..6a9b43c995e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -35,6 +35,9 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
+import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
@@ -95,7 +98,12 @@ interface HomeStatusBarViewModel {
val isClockVisible: Flow<VisibilityModel>
val isNotificationIconContainerVisible: Flow<VisibilityModel>
- val isSystemInfoVisible: Flow<VisibilityModel>
+ /**
+ * Pair of (system info visibility, event animation state). The animation state can be used to
+ * respond to the system event chip animations. In all cases, system info visibility correctly
+ * models the View.visibility for the system info area
+ */
+ val systemInfoCombinedVis: StateFlow<SystemInfoCombinedVisibilityModel>
/**
* Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
@@ -114,6 +122,12 @@ interface HomeStatusBarViewModel {
/** True if a visibility change should be animated. */
val shouldAnimateChange: Boolean,
)
+
+ /** The combined visibility + animation state for the system info status bar area */
+ data class SystemInfoCombinedVisibilityModel(
+ val baseVisibility: VisibilityModel,
+ val animationState: SystemEventAnimationState,
+ )
}
@SysUISingleton
@@ -129,6 +143,7 @@ constructor(
sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
shadeInteractor: ShadeInteractor,
ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
+ animations: SystemStatusEventAnimationInteractor,
@Application coroutineScope: CoroutineScope,
) : HomeStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
@@ -228,7 +243,7 @@ constructor(
visibilityViaDisableFlags.animate,
)
}
- override val isSystemInfoVisible: Flow<VisibilityModel> =
+ private val isSystemInfoVisible =
combine(
shouldHomeStatusBarBeVisible,
collapsedStatusBarInteractor.visibilityViaDisableFlags,
@@ -238,6 +253,22 @@ constructor(
VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate)
}
+ override val systemInfoCombinedVis =
+ combine(isSystemInfoVisible, animations.animationState) { sysInfoVisible, animationState ->
+ HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
+ sysInfoVisible,
+ animationState,
+ )
+ }
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel(
+ VisibilityModel(View.VISIBLE, false),
+ Idle,
+ ),
+ )
+
@View.Visibility
private fun Boolean.toVisibilityInt(): Int {
return if (this) View.VISIBLE else View.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 76024cd565d1..c7b6be3fc4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -17,12 +17,15 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
import android.annotation.SuppressLint
+import android.content.Context
import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
+import android.os.UserHandle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Flags.multiuserWifiPickerTrackerSupport
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -43,6 +46,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiReposito
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Unavailable.toHotspotDeviceType
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.user.data.repository.UserRepository
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
@@ -53,10 +57,12 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -68,6 +74,8 @@ import kotlinx.coroutines.flow.stateIn
class WifiRepositoryImpl
@Inject
constructor(
+ @Application applicationContext: Context,
+ private val userRepository: UserRepository,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
@Background private val bgDispatcher: CoroutineDispatcher,
@@ -84,90 +92,226 @@ constructor(
private var wifiPickerTracker: WifiPickerTracker? = null
- private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> = run {
- var current =
- WifiPickerTrackerInfo(
- state = WIFI_STATE_DEFAULT,
- isDefault = false,
- primaryNetwork = WIFI_NETWORK_DEFAULT,
- secondaryNetworks = emptyList(),
- )
- callbackFlow {
- val callback =
- object : WifiPickerTracker.WifiPickerTrackerCallback {
- override fun onWifiEntriesChanged() {
- val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection
- logOnWifiEntriesChanged(connectedEntry)
-
- val activeNetworks = wifiPickerTracker?.activeWifiEntries ?: emptyList()
- val secondaryNetworks =
- activeNetworks
- .filter { it != connectedEntry && !it.isPrimaryNetwork }
- .map { it.toWifiNetworkModel() }
-
- // [WifiPickerTracker.connectedWifiEntry] will return the same instance
- // but with updated internals. For example, when its validation status
- // changes from false to true, the same instance is re-used but with the
- // validated field updated.
- //
- // Because it's the same instance, the flow won't re-emit the value
- // (even though the internals have changed). So, we need to transform it
- // into our internal model immediately. [toWifiNetworkModel] always
- // returns a new instance, so the flow is guaranteed to emit.
- send(
- newPrimaryNetwork =
- connectedEntry?.toPrimaryWifiNetworkModel()
- ?: WIFI_NETWORK_DEFAULT,
- newSecondaryNetworks = secondaryNetworks,
- newIsDefault = connectedEntry?.isDefaultNetwork ?: false,
- )
- }
+ @VisibleForTesting
+ val selectedUserContext: Flow<Context> =
+ userRepository.selectedUserInfo.map {
+ applicationContext.createContextAsUser(UserHandle.of(it.id), /* flags= */ 0)
+ }
- override fun onWifiStateChanged() {
- val state = wifiPickerTracker?.wifiState
- logOnWifiStateChanged(state)
- send(newState = state ?: WIFI_STATE_DEFAULT)
- }
+ var current =
+ WifiPickerTrackerInfo(
+ state = WIFI_STATE_DEFAULT,
+ isDefault = false,
+ primaryNetwork = WIFI_NETWORK_DEFAULT,
+ secondaryNetworks = emptyList(),
+ )
- override fun onNumSavedNetworksChanged() {}
-
- override fun onNumSavedSubscriptionsChanged() {}
-
- private fun send(
- newState: Int = current.state,
- newIsDefault: Boolean = current.isDefault,
- newPrimaryNetwork: WifiNetworkModel = current.primaryNetwork,
- newSecondaryNetworks: List<WifiNetworkModel> =
- current.secondaryNetworks,
- ) {
- val new =
- WifiPickerTrackerInfo(
- newState,
- newIsDefault,
- newPrimaryNetwork,
- newSecondaryNetworks,
- )
- current = new
- trySend(new)
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> =
+ if (multiuserWifiPickerTrackerSupport()) {
+ selectedUserContext
+ .flatMapLatest { currentContext
+ -> // flatMapLatest because when selectedUserContext emits a new value, we want
+ // to
+ // re-create a whole flow
+ callbackFlow {
+ val callback =
+ object : WifiPickerTracker.WifiPickerTrackerCallback {
+ override fun onWifiEntriesChanged() {
+ val connectedEntry =
+ wifiPickerTracker.mergedOrPrimaryConnection
+ logOnWifiEntriesChanged(connectedEntry)
+
+ val activeNetworks =
+ wifiPickerTracker?.activeWifiEntries ?: emptyList()
+ val secondaryNetworks =
+ activeNetworks
+ .filter {
+ it != connectedEntry && !it.isPrimaryNetwork
+ }
+ .map { it.toWifiNetworkModel() }
+
+ // [WifiPickerTracker.connectedWifiEntry] will return the
+ // same
+ // instance but with updated internals. For example, when
+ // its
+ // validation status changes from false to true, the same
+ // instance is re-used but with the validated field updated.
+ //
+ // Because it's the same instance, the flow won't re-emit
+ // the
+ // value (even though the internals have changed). So, we
+ // need
+ // to transform it into our internal model immediately.
+ // [toWifiNetworkModel] always returns a new instance, so
+ // the
+ // flow is guaranteed to emit.
+ send(
+ newPrimaryNetwork =
+ connectedEntry?.toPrimaryWifiNetworkModel()
+ ?: WIFI_NETWORK_DEFAULT,
+ newSecondaryNetworks = secondaryNetworks,
+ newIsDefault = connectedEntry?.isDefaultNetwork ?: false,
+ )
+ }
+
+ override fun onWifiStateChanged() {
+ val state = wifiPickerTracker?.wifiState
+ logOnWifiStateChanged(state)
+ send(newState = state ?: WIFI_STATE_DEFAULT)
+ }
+
+ override fun onNumSavedNetworksChanged() {}
+
+ override fun onNumSavedSubscriptionsChanged() {}
+
+ private fun send(
+ newState: Int = current.state,
+ newIsDefault: Boolean = current.isDefault,
+ newPrimaryNetwork: WifiNetworkModel =
+ current.primaryNetwork,
+ newSecondaryNetworks: List<WifiNetworkModel> =
+ current.secondaryNetworks,
+ ) {
+ val new =
+ WifiPickerTrackerInfo(
+ newState,
+ newIsDefault,
+ newPrimaryNetwork,
+ newSecondaryNetworks,
+ )
+ current = new
+ trySend(new)
+ }
+ }
+ wifiPickerTracker =
+ wifiPickerTrackerFactory
+ .create(currentContext, lifecycle, callback, "WifiRepository")
+ .apply {
+ // By default, [WifiPickerTracker] will scan to see all
+ // available wifi networks in the area. Because SysUI only
+ // needs to display the **connected** network, we don't
+ // need scans to be running (and in fact, running scans is
+ // costly and should be avoided whenever possible).
+ this?.disableScanning()
+ }
+
+ // The lifecycle must be STARTED in order for the callback to receive
+ // events.
+ mainExecutor.execute {
+ lifecycle.currentState = Lifecycle.State.STARTED
+ }
+ awaitClose {
+ mainExecutor.execute {
+ lifecycle.currentState = Lifecycle.State.CREATED
+ }
+ }
}
- }
+ .stateIn(scope, SharingStarted.Eagerly, current)
+ }
+ .stateIn(scope, SharingStarted.Eagerly, current)
+ } else {
- wifiPickerTracker =
- wifiPickerTrackerFactory.create(lifecycle, callback, "WifiRepository").apply {
- // By default, [WifiPickerTracker] will scan to see all available wifi
- // networks in the area. Because SysUI only needs to display the
- // **connected** network, we don't need scans to be running (and in fact,
- // running scans is costly and should be avoided whenever possible).
- this?.disableScanning()
+ run {
+ var current =
+ WifiPickerTrackerInfo(
+ state = WIFI_STATE_DEFAULT,
+ isDefault = false,
+ primaryNetwork = WIFI_NETWORK_DEFAULT,
+ secondaryNetworks = emptyList(),
+ )
+ callbackFlow {
+ val callback =
+ object : WifiPickerTracker.WifiPickerTrackerCallback {
+ override fun onWifiEntriesChanged() {
+ val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection
+ logOnWifiEntriesChanged(connectedEntry)
+
+ val activeNetworks =
+ wifiPickerTracker?.activeWifiEntries ?: emptyList()
+ val secondaryNetworks =
+ activeNetworks
+ .filter { it != connectedEntry && !it.isPrimaryNetwork }
+ .map { it.toWifiNetworkModel() }
+
+ // [WifiPickerTracker.connectedWifiEntry] will return the same
+ // instance
+ // but with updated internals. For example, when its validation
+ // status
+ // changes from false to true, the same instance is re-used but
+ // with the
+ // validated field updated.
+ //
+ // Because it's the same instance, the flow won't re-emit the
+ // value
+ // (even though the internals have changed). So, we need to
+ // transform it
+ // into our internal model immediately. [toWifiNetworkModel]
+ // always
+ // returns a new instance, so the flow is guaranteed to emit.
+ send(
+ newPrimaryNetwork =
+ connectedEntry?.toPrimaryWifiNetworkModel()
+ ?: WIFI_NETWORK_DEFAULT,
+ newSecondaryNetworks = secondaryNetworks,
+ newIsDefault = connectedEntry?.isDefaultNetwork ?: false,
+ )
+ }
+
+ override fun onWifiStateChanged() {
+ val state = wifiPickerTracker?.wifiState
+ logOnWifiStateChanged(state)
+ send(newState = state ?: WIFI_STATE_DEFAULT)
+ }
+
+ override fun onNumSavedNetworksChanged() {}
+
+ override fun onNumSavedSubscriptionsChanged() {}
+
+ private fun send(
+ newState: Int = current.state,
+ newIsDefault: Boolean = current.isDefault,
+ newPrimaryNetwork: WifiNetworkModel = current.primaryNetwork,
+ newSecondaryNetworks: List<WifiNetworkModel> =
+ current.secondaryNetworks,
+ ) {
+ val new =
+ WifiPickerTrackerInfo(
+ newState,
+ newIsDefault,
+ newPrimaryNetwork,
+ newSecondaryNetworks,
+ )
+ current = new
+ trySend(new)
+ }
+ }
+
+ wifiPickerTracker =
+ wifiPickerTrackerFactory
+ .create(applicationContext, lifecycle, callback, "WifiRepository")
+ .apply {
+ // By default, [WifiPickerTracker] will scan to see all
+ // available wifi
+ // networks in the area. Because SysUI only needs to display the
+ // **connected** network, we don't need scans to be running (and
+ // in fact,
+ // running scans is costly and should be avoided whenever
+ // possible).
+ this?.disableScanning()
+ }
+ // The lifecycle must be STARTED in order for the callback to receive
+ // events.
+ mainExecutor.execute { lifecycle.currentState = Lifecycle.State.STARTED }
+ awaitClose {
+ mainExecutor.execute {
+ lifecycle.currentState = Lifecycle.State.CREATED
+ }
+ }
}
- // The lifecycle must be STARTED in order for the callback to receive events.
- mainExecutor.execute { lifecycle.currentState = Lifecycle.State.STARTED }
- awaitClose {
- mainExecutor.execute { lifecycle.currentState = Lifecycle.State.CREATED }
- }
+ .stateIn(scope, SharingStarted.Eagerly, current)
}
- .stateIn(scope, SharingStarted.Eagerly, current)
- }
+ }
override val isWifiEnabled: StateFlow<Boolean> =
wifiPickerTrackerInfo
@@ -185,11 +329,7 @@ constructor(
wifiPickerTrackerInfo
.map { it.primaryNetwork }
.distinctUntilChanged()
- .logDiffsForTable(
- tableLogger,
- columnPrefix = "",
- initialValue = WIFI_NETWORK_DEFAULT,
- )
+ .logDiffsForTable(tableLogger, columnPrefix = "", initialValue = WIFI_NETWORK_DEFAULT)
.stateIn(scope, SharingStarted.Eagerly, WIFI_NETWORK_DEFAULT)
override val secondaryNetworks: StateFlow<List<WifiNetworkModel>> =
@@ -348,7 +488,7 @@ constructor(
TAG,
LogLevel.DEBUG,
{ str1 = prettyPrintActivity(activity) },
- { "onActivityChanged: $str1" }
+ { "onActivityChanged: $str1" },
)
}
@@ -379,13 +519,15 @@ constructor(
/** The currently primary wifi network. */
val primaryNetwork: WifiNetworkModel,
/** The current secondary network(s), if any. Specifically excludes the primary network. */
- val secondaryNetworks: List<WifiNetworkModel>
+ val secondaryNetworks: List<WifiNetworkModel>,
)
@SysUISingleton
class Factory
@Inject
constructor(
+ @Application private val applicationContext: Context,
+ private val userRepository: UserRepository,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
@Background private val bgDispatcher: CoroutineDispatcher,
@@ -395,6 +537,8 @@ constructor(
) {
fun create(wifiManager: WifiManager): WifiRepositoryImpl {
return WifiRepositoryImpl(
+ applicationContext,
+ userRepository,
scope,
mainExecutor,
bgDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
deleted file mode 100644
index 0d36b48e9099..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2014 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.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-import android.widget.TextClock;
-
-import com.android.systemui.res.R;
-
-/**
- * Container for a clock which has two separate views for the clock itself and AM/PM indicator. This
- * is used to scale the clock independently of AM/PM.
- */
-public class SplitClockView extends LinearLayout {
-
- private TextClock mTimeView;
- private TextClock mAmPmView;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_TIME_CHANGED.equals(action)
- || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
- || Intent.ACTION_LOCALE_CHANGED.equals(action)
- || Intent.ACTION_CONFIGURATION_CHANGED.equals(action)
- || Intent.ACTION_USER_SWITCHED.equals(action)) {
- updatePatterns();
- }
- }
- };
-
- public SplitClockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTimeView = findViewById(R.id.time_view);
- mAmPmView = findViewById(R.id.am_pm_view);
- mTimeView.setShowCurrentUserTime(true);
- mAmPmView.setShowCurrentUserTime(true);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
-
- updatePatterns();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getContext().unregisterReceiver(mIntentReceiver);
- }
-
- private void updatePatterns() {
- String formatString = DateFormat.getTimeFormatString(getContext(),
- ActivityManager.getCurrentUser());
- int index = getAmPmPartEndIndex(formatString);
- String timeString;
- String amPmString;
- if (index == -1) {
- timeString = formatString;
- amPmString = "";
- } else {
- timeString = formatString.substring(0, index);
- amPmString = formatString.substring(index);
- }
- mTimeView.setFormat12Hour(timeString);
- mTimeView.setFormat24Hour(timeString);
- mTimeView.setContentDescriptionFormat12Hour(formatString);
- mTimeView.setContentDescriptionFormat24Hour(formatString);
- mAmPmView.setFormat12Hour(amPmString);
- mAmPmView.setFormat24Hour(amPmString);
- }
-
- /**
- * @return the index where the AM/PM part starts at the end in {@code formatString} including
- * leading white spaces or {@code -1} if no AM/PM part is found or {@code formatString}
- * doesn't end with AM/PM part
- */
- private static int getAmPmPartEndIndex(String formatString) {
- boolean hasAmPm = false;
- int length = formatString.length();
- for (int i = length - 1; i >= 0; i--) {
- char c = formatString.charAt(i);
- boolean isAmPm = c == 'a';
- boolean isWhitespace = Character.isWhitespace(c);
- if (isAmPm) {
- hasAmPm = true;
- }
- if (isAmPm || isWhitespace) {
- continue;
- }
- if (i == length - 1) {
-
- // First character was not AM/PM and not whitespace, so it's not ending with AM/PM.
- return -1;
- } else {
-
- // If we have AM/PM at all, return last index, or -1 to indicate that it's not
- // ending with AM/PM.
- return hasAmPm ? i + 1 : -1;
- }
- }
-
- // Only AM/PM and whitespaces? The whole string is AM/PM. Else: Only whitespaces in the
- // string.
- return hasAmPm ? 0 : -1;
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index c7bd5a1bb9a8..9187e3c43380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -206,12 +206,14 @@ public interface StatusBarPolicyModule {
@SysUISingleton
@Provides
static AccessPointControllerImpl provideAccessPointControllerImpl(
+ @Application Context context,
UserManager userManager,
UserTracker userTracker,
@Main Executor mainExecutor,
WifiPickerTrackerFactory wifiPickerTrackerFactory
) {
AccessPointControllerImpl controller = new AccessPointControllerImpl(
+ context,
userManager,
userTracker,
mainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index 3f6ef16e2e5e..36d64a9b405e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -35,11 +35,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.compose.ComposeInitializer;
-import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
+import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
/**
- * Status bar view.
+ * Status bar view
* We now extend WindowRootView so that we can host Compose views
*/
public class StatusBarWindowView extends FrameLayout {
@@ -64,7 +64,7 @@ public class StatusBarWindowView extends FrameLayout {
public void onAttachedToWindow() {
super.onAttachedToWindow();
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
ComposeInitializer.INSTANCE.onAttachedToWindow(this);
}
}
@@ -73,7 +73,7 @@ public class StatusBarWindowView extends FrameLayout {
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (StatusBarSimpleFragment.isEnabled()) {
+ if (StatusBarRootModernization.isEnabled()) {
ComposeInitializer.INSTANCE.onDetachedFromWindow(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 9c8ef0421888..1c3fece1beb1 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -242,10 +242,16 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
}
};
- private int getLatestWallpaperType(int userId) {
- return mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, userId)
- > mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, userId)
- ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM;
+ private int getDefaultWallpaperColorsSource(int userId) {
+ if (com.android.systemui.shared.Flags.newCustomizationPickerUi()) {
+ // The wallpaper colors source is always the home wallpaper.
+ return WallpaperManager.FLAG_SYSTEM;
+ } else {
+ // The wallpaper colors source is based on the last set wallpaper.
+ return mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, userId)
+ > mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, userId)
+ ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM;
+ }
}
private boolean isSeedColorSet(JSONObject jsonObject, WallpaperColors newWallpaperColors) {
@@ -279,9 +285,9 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags, int userId) {
final int currentUser = mUserTracker.getUserId();
final boolean hadWallpaperColors = mCurrentColors.get(userId) != null;
- int latestWallpaperType = getLatestWallpaperType(userId);
- boolean eventForLatestWallpaper = (flags & latestWallpaperType) != 0;
- if (eventForLatestWallpaper) {
+ int wallpaperColorsSource = getDefaultWallpaperColorsSource(userId);
+ boolean wallpaperColorsNeedUpdate = (flags & wallpaperColorsSource) != 0;
+ if (wallpaperColorsNeedUpdate) {
mCurrentColors.put(userId, wallpaperColors);
if (DEBUG) Log.d(TAG, "got new colors: " + wallpaperColors + " where: " + flags);
}
@@ -328,7 +334,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
boolean userChoseLockScreenColor = COLOR_SOURCE_LOCK.equals(wallpaperPickerColorSource);
boolean preserveLockScreenColor = isDestinationHomeOnly && userChoseLockScreenColor;
- if (!userChosePresetColor && !preserveLockScreenColor && eventForLatestWallpaper
+ if (!userChosePresetColor && !preserveLockScreenColor && wallpaperColorsNeedUpdate
&& !isSeedColorSet(jsonObject, wallpaperColors)) {
mSkipSettingChange = true;
if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
@@ -494,7 +500,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
// Upon boot, make sure we have the most up to date colors
Runnable updateColors = () -> {
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
- getLatestWallpaperType(mUserTracker.getUserId()));
+ getDefaultWallpaperColorsSource(mUserTracker.getUserId()));
Runnable applyColors = () -> {
if (DEBUG) Log.d(TAG, "Boot colors: " + systemColor);
mCurrentColors.put(mUserTracker.getUserId(), systemColor);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
index 03499cbdccb2..885a2b0d7305 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
@@ -19,6 +19,7 @@ import android.view.View
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
@@ -41,7 +42,7 @@ class UnfoldTransitionInteractor
@Inject
constructor(
private val repository: UnfoldTransitionRepository,
- private val configurationInteractor: ConfigurationInteractor,
+ @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor,
) {
/** Returns availability of fold/unfold transitions on the device */
val isAvailable: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 493aa8c11b18..ad97b21ea60b 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -19,12 +19,18 @@ package com.android.systemui.user.data.repository
import android.annotation.SuppressLint
import android.annotation.UserIdInt
+import android.app.admin.DevicePolicyManager
import android.content.Context
+import android.content.IntentFilter
import android.content.pm.UserInfo
+import android.content.res.Resources
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -38,6 +44,7 @@ import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -49,11 +56,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@@ -100,6 +108,12 @@ interface UserRepository {
/** Whether refresh users should be paused. */
var isRefreshUsersPaused: Boolean
+ /** Whether logout for secondary users is enabled by admin device policy. */
+ val isSecondaryUserLogoutEnabled: StateFlow<Boolean>
+
+ /** Whether logout into system user is enabled. */
+ val isLogoutToSystemUserEnabled: StateFlow<Boolean>
+
/** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */
fun refreshUsers()
@@ -109,6 +123,12 @@ interface UserRepository {
fun isUserSwitcherEnabled(): Boolean
+ /** Performs logout logout for secondary users. */
+ suspend fun logOutSecondaryUser()
+
+ /** Performs logout into the system user. */
+ suspend fun logOutToSystemUser()
+
/**
* Returns the user ID of the "main user" of the device. This user may have access to certain
* features which are limited to at most one user. There will never be more than one main user
@@ -131,12 +151,16 @@ class UserRepositoryImpl
@Inject
constructor(
@Application private val appContext: Context,
+ @Main private val resources: Resources,
private val manager: UserManager,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val globalSettings: GlobalSettings,
private val tracker: UserTracker,
+ private val devicePolicyManager: DevicePolicyManager,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val statusBarService: IStatusBarService,
) : UserRepository {
private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
@@ -147,7 +171,7 @@ constructor(
SETTING_SIMPLE_USER_SWITCHER,
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.USER_SWITCHER_ENABLED,
- ),
+ )
)
.onStart { emit(Unit) } // Forces an initial update.
.map { getSettings() }
@@ -163,6 +187,7 @@ constructor(
override var mainUserId: Int = UserHandle.USER_NULL
private set
+
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_NULL
private set
@@ -221,12 +246,73 @@ constructor(
.stateIn(
applicationScope,
SharingStarted.Eagerly,
- initialValue = SelectedUserModel(tracker.userInfo, currentSelectionStatus)
+ initialValue = SelectedUserModel(tracker.userInfo, currentSelectionStatus),
)
}
override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
+ /** Whether the secondary user logout is enabled by the admin device policy. */
+ private val isSecondaryUserLogoutSupported: Flow<Boolean> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
+ ) { intent, _ ->
+ if (
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED == intent.action
+ ) {
+ Unit
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
+ .onStart { emit(Unit) }
+ .map { _ -> devicePolicyManager.isLogoutEnabled() }
+ .flowOn(backgroundDispatcher)
+
+ @SuppressLint("MissingPermission")
+ override val isSecondaryUserLogoutEnabled: StateFlow<Boolean> =
+ selectedUser
+ .flatMapLatestConflated { selectedUser ->
+ if (selectedUser.isEligibleForLogout()) {
+ isSecondaryUserLogoutSupported
+ } else {
+ flowOf(false)
+ }
+ }
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+ @SuppressLint("MissingPermission")
+ override val isLogoutToSystemUserEnabled: StateFlow<Boolean> =
+ selectedUser
+ .flatMapLatestConflated { selectedUser ->
+ if (selectedUser.isEligibleForLogout()) {
+ flowOf(
+ resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+ )
+ } else {
+ flowOf(false)
+ }
+ }
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+ @SuppressLint("MissingPermission")
+ override suspend fun logOutSecondaryUser() {
+ if (isSecondaryUserLogoutEnabled.value) {
+ withContext(backgroundDispatcher) { devicePolicyManager.logoutUser() }
+ }
+ }
+
+ override suspend fun logOutToSystemUser() {
+ // TODO(b/377493351) : start using proper logout API once it is available.
+ // Using reboot is a temporary solution.
+ if (isLogoutToSystemUserEnabled.value) {
+ withContext(backgroundDispatcher) { statusBarService.reboot(false) }
+ }
+ }
+
@SuppressLint("MissingPermission")
override fun refreshUsers() {
applicationScope.launch {
@@ -262,45 +348,53 @@ constructor(
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
- val isSimpleUserSwitcher =
- globalSettings.getInt(
- SETTING_SIMPLE_USER_SWITCHER,
- if (
- appContext.resources.getBoolean(
- com.android.internal.R.bool.config_expandLockScreenUserSwitcher
- )
- ) {
- 1
- } else {
- 0
- },
- ) != 0
-
- val isAddUsersFromLockscreen =
- globalSettings.getInt(
- Settings.Global.ADD_USERS_WHEN_LOCKED,
- 0,
- ) != 0
-
- val isUserSwitcherEnabled =
- globalSettings.getInt(
- Settings.Global.USER_SWITCHER_ENABLED,
- if (
- appContext.resources.getBoolean(
- com.android.internal.R.bool.config_showUserSwitcherByDefault
- )
- ) {
- 1
- } else {
- 0
- },
- ) != 0
-
- UserSwitcherSettingsModel(
- isSimpleUserSwitcher = isSimpleUserSwitcher,
- isAddUsersFromLockscreen = isAddUsersFromLockscreen,
- isUserSwitcherEnabled = isUserSwitcherEnabled,
- )
+ if (
+ // TODO(b/378068979): remove once login screen-specific logic
+ // is implemented at framework level.
+ appContext.resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+ ) {
+ UserSwitcherSettingsModel(
+ isSimpleUserSwitcher = false,
+ isAddUsersFromLockscreen = false,
+ isUserSwitcherEnabled = false,
+ )
+ } else {
+ val isSimpleUserSwitcher =
+ globalSettings.getInt(
+ SETTING_SIMPLE_USER_SWITCHER,
+ if (
+ appContext.resources.getBoolean(
+ com.android.internal.R.bool.config_expandLockScreenUserSwitcher
+ )
+ ) {
+ 1
+ } else {
+ 0
+ },
+ ) != 0
+
+ val isAddUsersFromLockscreen =
+ globalSettings.getInt(Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0
+
+ val isUserSwitcherEnabled =
+ globalSettings.getInt(
+ Settings.Global.USER_SWITCHER_ENABLED,
+ if (
+ appContext.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ )
+ ) {
+ 1
+ } else {
+ 0
+ },
+ ) != 0
+ UserSwitcherSettingsModel(
+ isSimpleUserSwitcher = isSimpleUserSwitcher,
+ isAddUsersFromLockscreen = isAddUsersFromLockscreen,
+ isUserSwitcherEnabled = isUserSwitcherEnabled,
+ )
+ }
}
}
@@ -309,3 +403,11 @@ constructor(
@VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
}
}
+
+fun SelectedUserModel.isEligibleForLogout(): Boolean {
+ // TODO(b/206032495): should call mDevicePolicyManager.getLogoutUserId() instead of
+ // hardcode it to USER_SYSTEM so it properly supports headless system user mode
+ // (and then call mDevicePolicyManager.clearLogoutUser() after switched)
+ return selectionStatus == SelectionStatus.SELECTION_COMPLETE &&
+ userInfo.id != android.os.UserHandle.USER_SYSTEM
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
index a7983605eac9..bcbd679b35eb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
@@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.UserManager
import android.provider.Settings.Global.USER_SWITCHER_ENABLED
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -40,7 +41,6 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
interface UserSwitcherRepository {
@@ -67,6 +67,9 @@ constructor(
private val showUserSwitcherForSingleUser =
context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)
+ private val userSwitchingMustGoThroughLoginScreen =
+ context.resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+
override val isEnabled: Flow<Boolean> = conflatedCallbackFlow {
suspend fun updateState() {
trySendWithFailureLogging(isUserSwitcherEnabled(), TAG)
@@ -135,7 +138,13 @@ constructor(
private suspend fun isUserSwitcherEnabled(): Boolean {
return withContext(bgDispatcher) {
- userManager.isUserSwitcherEnabled(showUserSwitcherForSingleUser)
+ // TODO(b/378068979): remove once login screen-specific logic
+ // is implemented at framework level.
+ if (userSwitchingMustGoThroughLoginScreen) {
+ false
+ } else {
+ userManager.isUserSwitcherEnabled(showUserSwitcherForSingleUser)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt
new file mode 100644
index 000000000000..f2dd25fecf08
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.domain.interactor
+
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+/** Encapsulates business logic to for the logout. */
+@SysUISingleton
+class UserLogoutInteractor
+@Inject
+constructor(
+ private val userRepository: UserRepository,
+ @Application private val applicationScope: CoroutineScope,
+) {
+
+ val isLogoutEnabled: StateFlow<Boolean> =
+ combine(
+ userRepository.isSecondaryUserLogoutEnabled,
+ userRepository.isLogoutToSystemUserEnabled,
+ Boolean::or,
+ )
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+ fun logOut() {
+ applicationScope.launch {
+ if (userRepository.isSecondaryUserLogoutEnabled.value) {
+ userRepository.logOutSecondaryUser()
+ } else if (userRepository.isLogoutToSystemUserEnabled.value) {
+ userRepository.logOutToSystemUser()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index 3662c78efb16..163288b25b28 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -32,6 +32,7 @@ import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.UiEventLogger
import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
@@ -81,7 +82,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
@@ -109,7 +109,7 @@ constructor(
private val guestUserInteractor: GuestUserInteractor,
private val uiEventLogger: UiEventLogger,
private val userRestrictionChecker: UserRestrictionChecker,
- private val processWrapper: ProcessWrapper
+ private val processWrapper: ProcessWrapper,
) {
/**
* Defines interface for classes that can be notified when the state of users on the device is
@@ -137,11 +137,10 @@ constructor(
/** List of current on-device users to select from. */
val users: Flow<List<UserModel>>
get() =
- combine(
+ combine(userInfos, repository.selectedUserInfo, repository.userSwitcherSettings) {
userInfos,
- repository.selectedUserInfo,
- repository.userSwitcherSettings,
- ) { userInfos, selectedUserInfo, settings ->
+ selectedUserInfo,
+ settings ->
toUserModels(
userInfos = userInfos,
selectedUserId = selectedUserInfo.id,
@@ -157,7 +156,7 @@ constructor(
toUserModel(
userInfo = selectedUserInfo,
selectedUserId = selectedUserId,
- canSwitchUsers = canSwitchUsers(selectedUserId)
+ canSwitchUsers = canSwitchUsers(selectedUserId),
)
}
@@ -211,7 +210,7 @@ constructor(
manager,
repository,
settings.isUserSwitcherEnabled,
- canAccessUserSwitcher
+ canAccessUserSwitcher,
)
if (canCreateUsers) {
@@ -238,7 +237,7 @@ constructor(
if (
UserActionsUtil.canManageUsers(
repository,
- settings.isUserSwitcherEnabled
+ settings.isUserSwitcherEnabled,
)
) {
add(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
@@ -248,18 +247,14 @@ constructor(
.flowOn(backgroundDispatcher)
val userRecords: StateFlow<ArrayList<UserRecord>> =
- combine(
+ combine(userInfos, repository.selectedUserInfo, actions, repository.userSwitcherSettings) {
userInfos,
- repository.selectedUserInfo,
- actions,
- repository.userSwitcherSettings,
- ) { userInfos, selectedUserInfo, actionModels, settings ->
+ selectedUserInfo,
+ actionModels,
+ settings ->
ArrayList(
userInfos.map {
- toRecord(
- userInfo = it,
- selectedUserId = selectedUserInfo.id,
- )
+ toRecord(userInfo = it, selectedUserId = selectedUserInfo.id)
} +
actionModels.map {
toRecord(
@@ -298,7 +293,8 @@ constructor(
val isGuestUserResetting: Boolean = guestUserInteractor.isGuestUserResetting
/** Whether to enable the user chip in the status bar */
- val isStatusBarUserChipEnabled: Boolean = repository.isStatusBarUserChipEnabled
+ val isStatusBarUserChipEnabled: Boolean
+ get() = repository.isStatusBarUserChipEnabled
private val _dialogShowRequests = MutableStateFlow<ShowDialogRequestModel?>(null)
val dialogShowRequests: Flow<ShowDialogRequestModel?> = _dialogShowRequests.asStateFlow()
@@ -467,10 +463,8 @@ constructor(
when (action) {
UserActionModel.ENTER_GUEST_MODE -> {
uiEventLogger.log(MultiUserActionsEvent.CREATE_GUEST_FROM_USER_SWITCHER)
- guestUserInteractor.createAndSwitchTo(
- this::showDialog,
- this::dismissDialog,
- ) { userId ->
+ guestUserInteractor.createAndSwitchTo(this::showDialog, this::dismissDialog) {
+ userId ->
selectUser(userId, dialogShower)
}
}
@@ -481,7 +475,7 @@ constructor(
activityStarter.startActivity(
CreateUserActivity.createIntentForStart(
applicationContext,
- keyguardInteractor.isKeyguardShowing()
+ keyguardInteractor.isKeyguardShowing(),
),
/* dismissShade= */ true,
/* animationController */ null,
@@ -523,17 +517,14 @@ constructor(
)
}
- fun removeGuestUser(
- @UserIdInt guestUserId: Int,
- @UserIdInt targetUserId: Int,
- ) {
+ fun removeGuestUser(@UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int) {
applicationScope.launch {
guestUserInteractor.remove(
guestUserId = guestUserId,
targetUserId = targetUserId,
::showDialog,
::dismissDialog,
- ::switchUser
+ ::switchUser,
)
}
}
@@ -570,10 +561,7 @@ constructor(
}
}
- private suspend fun toRecord(
- userInfo: UserInfo,
- selectedUserId: Int,
- ): UserRecord {
+ private suspend fun toRecord(userInfo: UserInfo, selectedUserId: Int): UserRecord {
return LegacyUserDataHelper.createRecord(
context = applicationContext,
manager = manager,
@@ -595,10 +583,7 @@ constructor(
actionType = action,
isRestricted = isRestricted,
isSwitchToEnabled =
- canSwitchUsers(
- selectedUserId = selectedUserId,
- isAction = true,
- ) &&
+ canSwitchUsers(selectedUserId = selectedUserId, isAction = true) &&
// If the user is auto-created is must not be currently resetting.
!(isGuestUserAutoCreated && isGuestUserResetting),
userRestrictionChecker = userRestrictionChecker,
@@ -623,10 +608,7 @@ constructor(
}
}
- private suspend fun onBroadcastReceived(
- intent: Intent,
- previousUserInfo: UserInfo?,
- ) {
+ private suspend fun onBroadcastReceived(intent: Intent, previousUserInfo: UserInfo?) {
val shouldRefreshAllUsers =
when (intent.action) {
Intent.ACTION_LOCALE_CHANGED -> true
@@ -645,10 +627,8 @@ constructor(
Intent.ACTION_USER_INFO_CHANGED -> true
Intent.ACTION_USER_UNLOCKED -> {
// If we unlocked the system user, we should refresh all users.
- intent.getIntExtra(
- Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL,
- ) == UserHandle.USER_SYSTEM
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) ==
+ UserHandle.USER_SYSTEM
}
else -> true
}
@@ -668,20 +648,14 @@ constructor(
// Disconnect from the old secondary user's service
val secondaryUserId = repository.secondaryUserId
if (secondaryUserId != UserHandle.USER_NULL) {
- applicationContext.stopServiceAsUser(
- intent,
- UserHandle.of(secondaryUserId),
- )
+ applicationContext.stopServiceAsUser(intent, UserHandle.of(secondaryUserId))
repository.secondaryUserId = UserHandle.USER_NULL
}
// Connect to the new secondary user's service (purely to ensure that a persistent
// SystemUI application is created for that user)
if (userId != processWrapper.myUserHandle().identifier && !processWrapper.isSystemUser) {
- applicationContext.startServiceAsUser(
- intent,
- UserHandle.of(userId),
- )
+ applicationContext.startServiceAsUser(intent, UserHandle.of(userId))
repository.secondaryUserId = userId
}
}
@@ -732,7 +706,7 @@ constructor(
private suspend fun toUserModel(
userInfo: UserInfo,
selectedUserId: Int,
- canSwitchUsers: Boolean
+ canSwitchUsers: Boolean,
): UserModel {
val userId = userInfo.id
val isSelected = userId == selectedUserId
@@ -740,11 +714,7 @@ constructor(
UserModel(
id = userId,
name = Text.Loaded(userInfo.name),
- image =
- getUserImage(
- isGuest = true,
- userId = userId,
- ),
+ image = getUserImage(isGuest = true, userId = userId),
isSelected = isSelected,
isSelectable = canSwitchUsers,
isGuest = true,
@@ -753,11 +723,7 @@ constructor(
UserModel(
id = userId,
name = Text.Loaded(userInfo.name),
- image =
- getUserImage(
- isGuest = false,
- userId = userId,
- ),
+ image = getUserImage(isGuest = false, userId = userId),
isSelected = isSelected,
isSelectable = canSwitchUsers || isSelected,
isGuest = false,
@@ -765,10 +731,7 @@ constructor(
}
}
- private suspend fun canSwitchUsers(
- selectedUserId: Int,
- isAction: Boolean = false,
- ): Boolean {
+ private suspend fun canSwitchUsers(selectedUserId: Int, isAction: Boolean = false): Boolean {
val isHeadlessSystemUserMode =
withContext(backgroundDispatcher) { headlessSystemUserMode.isHeadlessSystemUserMode() }
// Whether menu item should be active. True if item is a user or if any user has
@@ -785,7 +748,7 @@ constructor(
.getUsers(
/* excludePartial= */ true,
/* excludeDying= */ true,
- /* excludePreCreated= */ true
+ /* excludePreCreated= */ true,
)
.any { user ->
user.id != UserHandle.USER_SYSTEM &&
@@ -794,10 +757,7 @@ constructor(
}
@SuppressLint("UseCompatLoadingForDrawables")
- private suspend fun getUserImage(
- isGuest: Boolean,
- userId: Int,
- ): Drawable {
+ private suspend fun getUserImage(isGuest: Boolean, userId: Int): Drawable {
if (isGuest) {
return checkNotNull(
applicationContext.getDrawable(com.android.settingslib.R.drawable.ic_account_circle)
@@ -823,13 +783,13 @@ constructor(
return UserIcons.getDefaultUserIcon(
applicationContext.resources,
userId,
- /* light= */ false
+ /* light= */ false,
)
}
private fun canCreateGuestUser(
settings: UserSwitcherSettingsModel,
- canAccessUserSwitcher: Boolean
+ canAccessUserSwitcher: Boolean,
): Boolean {
return guestUserInteractor.isGuestUserAutoCreated ||
UserActionsUtil.canCreateGuest(
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
index 2c425b199b4e..53c2d888922b 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
@@ -30,11 +30,10 @@ import kotlinx.coroutines.flow.mapLatest
@OptIn(ExperimentalCoroutinesApi::class)
class StatusBarUserChipViewModel
@Inject
-constructor(
- interactor: UserSwitcherInteractor,
-) {
+constructor(private val interactor: UserSwitcherInteractor) {
/** Whether the status bar chip ui should be available */
- val chipEnabled: Boolean = interactor.isStatusBarUserChipEnabled
+ val chipEnabled: Boolean
+ get() = interactor.isStatusBarUserChipEnabled
/** Whether or not the chip should be showing, based on the number of users */
val isChipVisible: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 315912406b6d..63a5b3f1e6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -20,6 +20,7 @@ import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -35,7 +36,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.plus
/** A class allowing Java classes to collect on Kotlin flows. */
@SysUISingleton
@@ -102,6 +103,22 @@ fun <T> collectFlow(
}
}
+/**
+ * Collect information for the given [flow], calling [consumer] for each emitted event on the
+ * specified [collectContext].
+ *
+ * Collection will continue until the given [scope] is cancelled.
+ */
+@JvmOverloads
+fun <T> collectFlow(
+ scope: CoroutineScope,
+ collectContext: CoroutineContext = scope.coroutineContext,
+ flow: Flow<T>,
+ consumer: Consumer<T>,
+): Job {
+ return scope.plus(collectContext).launch { flow.collect { consumer.accept(it) } }
+}
+
fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
return combine(flow1, flow2, bifunction)
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
index d509b2da482e..f36c335e0f44 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
@@ -16,9 +16,6 @@
package com.android.systemui.util.settings;
-import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository;
-import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl;
-
import dagger.Binds;
import dagger.Module;
@@ -39,9 +36,4 @@ public interface SettingsUtilModule {
/** Bind GlobalSettingsImpl to GlobalSettings. */
@Binds
GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl);
-
- /** Bind UserAwareSecureSettingsRepositoryImpl to UserAwareSecureSettingsRepository. */
- @Binds
- UserAwareSecureSettingsRepository bindsUserAwareSecureSettingsRepository(
- UserAwareSecureSettingsRepositoryImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt
index d3e50803b5d5..bc3726d362e2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt
@@ -19,52 +19,26 @@ package com.android.systemui.util.settings.repository
import android.provider.Settings
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxy
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
/**
* Repository for observing values of [Settings.Secure] for the currently active user. That means
* when user is switched and the new user has different value, flow will emit new value.
*/
-interface UserAwareSecureSettingsRepository {
-
- /**
- * Emits boolean value of the setting for active user. Also emits starting value when
- * subscribed.
- * See: [SettingsProxy.getBool].
- */
- fun boolSettingForActiveUser(name: String, defaultValue: Boolean = false): Flow<Boolean>
-}
-
+// TODO: b/377244768 - Make internal once call sites inject SecureSettingsRepository instead.
@SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
-class UserAwareSecureSettingsRepositoryImpl @Inject constructor(
- private val secureSettings: SecureSettings,
- private val userRepository: UserRepository,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
-) : UserAwareSecureSettingsRepository {
-
- override fun boolSettingForActiveUser(name: String, defaultValue: Boolean): Flow<Boolean> =
- userRepository.selectedUserInfo
- .flatMapLatest { userInfo -> settingObserver(name, defaultValue, userInfo.id) }
- .distinctUntilChanged()
- .flowOn(backgroundDispatcher)
-
- private fun settingObserver(name: String, defaultValue: Boolean, userId: Int): Flow<Boolean> {
- return secureSettings
- .observerFlow(userId, name)
- .onStart { emit(Unit) }
- .map { secureSettings.getBoolForUser(name, defaultValue, userId) }
- }
-} \ No newline at end of file
+class UserAwareSecureSettingsRepository
+@Inject
+constructor(
+ secureSettings: SecureSettings,
+ userRepository: UserRepository,
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ @Background bgContext: CoroutineContext,
+) :
+ UserAwareSettingsRepository(secureSettings, userRepository, backgroundDispatcher, bgContext),
+ SecureSettingsRepository
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt
new file mode 100644
index 000000000000..49a0f14d6b3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSettingsRepository.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings.repository
+
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import com.android.systemui.util.settings.UserSettingsProxy
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+
+/**
+ * Repository for observing values of a [UserSettingsProxy], for the currently active user. That
+ * means that when the user is switched and the new user has a different value, the flow will emit
+ * the new value.
+ */
+// TODO: b/377244768 - Make internal when UserAwareSecureSettingsRepository can be made internal.
+@OptIn(ExperimentalCoroutinesApi::class)
+abstract class UserAwareSettingsRepository(
+ private val userSettings: UserSettingsProxy,
+ private val userRepository: UserRepository,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Background private val bgContext: CoroutineContext,
+) {
+
+ fun boolSetting(name: String, defaultValue: Boolean): Flow<Boolean> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { userInfo ->
+ settingObserver(name, userInfo.id) {
+ userSettings.getBoolForUser(name, defaultValue, userInfo.id)
+ }
+ }
+ .distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
+
+ fun intSetting(name: String, defaultValue: Int): Flow<Int> {
+ return userRepository.selectedUserInfo
+ .flatMapLatest { userInfo ->
+ settingObserver(name, userInfo.id) {
+ userSettings.getIntForUser(name, defaultValue, userInfo.id)
+ }
+ }
+ .distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
+ }
+
+ private fun <T> settingObserver(name: String, userId: Int, settingsReader: () -> T): Flow<T> {
+ return userSettings
+ .observerFlow(userId, name)
+ .onStart { emit(Unit) }
+ .map { settingsReader.invoke() }
+ }
+
+ suspend fun setInt(name: String, value: Int) {
+ withContext(bgContext) {
+ userSettings.putIntForUser(name, value, userRepository.getSelectedUserInfo().id)
+ }
+ }
+
+ suspend fun getInt(name: String, defaultValue: Int): Int {
+ return withContext(bgContext) {
+ userSettings.getIntForUser(name, defaultValue, userRepository.getSelectedUserInfo().id)
+ }
+ }
+
+ suspend fun getString(name: String): String? {
+ return withContext(bgContext) {
+ userSettings.getStringForUser(name, userRepository.getSelectedUserInfo().id)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepository.kt
new file mode 100644
index 000000000000..4b01ded16495
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSystemSettingsRepository.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings.repository
+
+import android.provider.Settings
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SystemSettings
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
+
+/**
+ * Repository for observing values of [Settings.Secure] for the currently active user. That means
+ * when user is switched and the new user has different value, flow will emit new value.
+ */
+// TODO: b/377244768 - Make internal once call sites inject SystemSettingsRepository instead.
+class UserAwareSystemSettingsRepository(
+ systemSettings: SystemSettings,
+ userRepository: UserRepository,
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ @Background bgContext: CoroutineContext,
+) :
+ UserAwareSettingsRepository(systemSettings, userRepository, backgroundDispatcher, bgContext),
+ SystemSettingsRepository
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 617aaa71d2d3..d5b8597e36ed 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -53,7 +53,9 @@ interface AudioModule {
fun provideAudioManagerIntentsReceiver(
@Application context: Context,
@Application coroutineScope: CoroutineScope,
- ): AudioManagerEventsReceiver = AudioManagerEventsReceiverImpl(context, coroutineScope)
+ @Background coroutineContext: CoroutineContext,
+ ): AudioManagerEventsReceiver =
+ AudioManagerEventsReceiverImpl(context, coroutineScope, coroutineContext)
@Provides
@SysUISingleton
@@ -82,7 +84,7 @@ interface AudioModule {
localBluetoothManager: LocalBluetoothManager?,
@Application coroutineScope: CoroutineScope,
@Background coroutineContext: CoroutineContext,
- volumeLogger: VolumeLogger
+ volumeLogger: VolumeLogger,
): AudioSharingRepository =
if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
AudioSharingRepositoryImpl(
@@ -90,7 +92,7 @@ interface AudioModule {
localBluetoothManager,
coroutineScope,
coroutineContext,
- volumeLogger
+ volumeLogger,
)
} else {
AudioSharingRepositoryEmptyImpl()
@@ -111,8 +113,7 @@ interface AudioModule {
@Provides
@SysUISingleton
- fun provideAudioSystemRepository(
- @Application context: Context,
- ): AudioSystemRepository = AudioSystemRepositoryImpl(context)
+ fun provideAudioSystemRepository(@Application context: Context): AudioSystemRepository =
+ AudioSystemRepositoryImpl(context)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 4fc9a7c9ae01..5c0cc8150d70 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -18,9 +18,13 @@ package com.android.systemui.volume.dialog
import android.app.Dialog
import android.content.Context
+import android.graphics.PixelFormat
import android.os.Bundle
import android.view.MotionEvent
+import android.view.ViewGroup
+import android.view.WindowManager
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder
@@ -32,10 +36,34 @@ constructor(
@Application context: Context,
private val viewBinder: VolumeDialogViewBinder,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
-) : Dialog(context) {
+) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
+
+ init {
+ with(window!!) {
+ addFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+ WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ )
+ addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
+ setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ setWindowAnimations(-1)
+ setFormat(PixelFormat.TRANSLUCENT)
+
+ attributes =
+ attributes.apply {
+ title = "VolumeDialog" // Not the same as Window#setTitle
+ }
+ setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ }
+ setCanceledOnTouchOutside(true)
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setContentView(R.layout.volume_dialog)
viewBinder.bind(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
index f7ad3205f3dd..fb157958a630 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
@@ -16,8 +16,10 @@
package com.android.systemui.volume.dialog.dagger
+import com.android.systemui.volume.dialog.dagger.module.VolumeDialogModule
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import dagger.BindsInstance
import dagger.Subcomponent
import kotlinx.coroutines.CoroutineScope
@@ -27,7 +29,7 @@ import kotlinx.coroutines.CoroutineScope
* [com.android.systemui.volume.dialog.VolumeDialogPlugin] and lives alongside it.
*/
@VolumeDialogScope
-@Subcomponent(modules = [])
+@Subcomponent(modules = [VolumeDialogModule::class])
interface VolumeDialogComponent {
/**
@@ -40,6 +42,8 @@ interface VolumeDialogComponent {
@VolumeDialogScope fun volumeDialog(): com.android.systemui.volume.dialog.VolumeDialog
+ fun sliderComponentFactory(): VolumeDialogSliderComponent.Factory
+
@Subcomponent.Factory
interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt
new file mode 100644
index 000000000000..025e269b70b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.dagger.module
+
+import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository
+import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+/** Dagger module for volume dialog code in the volume package */
+@Module
+interface VolumeDialogModule {
+
+ @Binds
+ fun bindVolumeDialogRingerFeedbackRepository(
+ ringerFeedbackRepository: VolumeDialogRingerFeedbackRepositoryImpl
+ ): VolumeDialogRingerFeedbackRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.kt
new file mode 100644
index 000000000000..263972b937d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.data.repository
+
+import android.content.Context
+import com.android.systemui.Prefs
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+interface VolumeDialogRingerFeedbackRepository {
+
+ /** gets number of shown toasts */
+ suspend fun getToastCount(): Int
+
+ /** updates number of shown toasts */
+ suspend fun updateToastCount(toastCount: Int)
+}
+
+class VolumeDialogRingerFeedbackRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationContext: Context,
+ @Background val backgroundDispatcher: CoroutineDispatcher,
+) : VolumeDialogRingerFeedbackRepository {
+
+ override suspend fun getToastCount(): Int =
+ withContext(backgroundDispatcher) {
+ return@withContext Prefs.getInt(
+ applicationContext,
+ Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT,
+ 0,
+ )
+ }
+
+ override suspend fun updateToastCount(toastCount: Int) {
+ withContext(backgroundDispatcher) {
+ Prefs.putInt(applicationContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, toastCount + 1)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
index 281e57fec855..b83613ba4f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt
@@ -26,6 +26,7 @@ import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.plugins.VolumeDialogController
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
+import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository
import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
import javax.inject.Inject
@@ -45,6 +46,7 @@ constructor(
volumeDialogStateInteractor: VolumeDialogStateInteractor,
private val controller: VolumeDialogController,
private val audioSystemRepository: AudioSystemRepository,
+ private val ringerFeedbackRepository: VolumeDialogRingerFeedbackRepository,
) {
val ringerModel: Flow<VolumeDialogRingerModel> =
@@ -84,4 +86,12 @@ constructor(
fun scheduleTouchFeedback() {
controller.scheduleTouchFeedback()
}
+
+ suspend fun getToastCount(): Int {
+ return ringerFeedbackRepository.getToastCount()
+ }
+
+ suspend fun updateToastCount(toastCount: Int) {
+ ringerFeedbackRepository.updateToastCount(toastCount)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index d4da226152f3..e040638324ac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -16,17 +16,23 @@
package com.android.systemui.volume.dialog.ringer.ui.viewmodel
+import android.content.Context
import android.media.AudioAttributes
import android.media.AudioManager.RINGER_MODE_NORMAL
import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
import android.os.VibrationEffect
+import android.widget.Toast
+import com.android.internal.R as internalR
+import com.android.settingslib.Utils
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ringer.domain.VolumeDialogRingerInteractor
import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel
import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
@@ -40,26 +46,37 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+private const val SHOW_RINGER_TOAST_COUNT = 12
class VolumeDialogRingerDrawerViewModel
@AssistedInject
constructor(
+ @Application private val applicationContext: Context,
@VolumeDialog private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val interactor: VolumeDialogRingerInteractor,
private val vibrator: VibratorHelper,
private val volumeDialogLogger: VolumeDialogLogger,
+ private val visibilityInteractor: VolumeDialogVisibilityInteractor,
) {
private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)
val ringerViewModel: StateFlow<RingerViewModelState> =
combine(interactor.ringerModel, drawerState) { ringerModel, state ->
+ level = ringerModel.level
+ levelMax = ringerModel.levelMax
ringerModel.toViewModel(state)
}
.flowOn(backgroundDispatcher)
.stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)
+ // Level and Maximum level of Ring Stream.
+ private var level = -1
+ private var levelMax = -1
+
// Vibration attributes.
private val sonificiationVibrationAttributes =
AudioAttributes.Builder()
@@ -71,8 +88,10 @@ constructor(
if (drawerState.value is RingerDrawerState.Open) {
Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
provideTouchFeedback(ringerMode)
+ maybeShowToast(ringerMode)
interactor.setRingerMode(ringerMode)
}
+ visibilityInteractor.resetDismissTimeout()
drawerState.value =
when (drawerState.value) {
is RingerDrawerState.Initial -> {
@@ -201,6 +220,46 @@ constructor(
}
}
+ private fun maybeShowToast(ringerMode: RingerMode) {
+ coroutineScope.launch {
+ val seenToastCount = interactor.getToastCount()
+ if (seenToastCount > SHOW_RINGER_TOAST_COUNT) {
+ return@launch
+ }
+
+ val toastText =
+ when (ringerMode.value) {
+ RINGER_MODE_NORMAL -> {
+ if (level != -1 && levelMax != -1) {
+ applicationContext.getString(
+ R.string.volume_dialog_ringer_guidance_ring,
+ Utils.formatPercentage(level.toLong(), levelMax.toLong()),
+ )
+ } else {
+ null
+ }
+ }
+
+ RINGER_MODE_SILENT ->
+ applicationContext.getString(
+ internalR.string.volume_dialog_ringer_guidance_silent
+ )
+
+ RINGER_MODE_VIBRATE ->
+ applicationContext.getString(
+ internalR.string.volume_dialog_ringer_guidance_vibrate
+ )
+
+ else ->
+ applicationContext.getString(
+ internalR.string.volume_dialog_ringer_guidance_vibrate
+ )
+ }
+ toastText?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show() }
+ interactor.updateToastCount(seenToastCount)
+ }
+ }
+
@AssistedFactory
interface Factory {
fun create(): VolumeDialogRingerDrawerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
new file mode 100644
index 000000000000..538ee47915a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.dagger
+
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+/**
+ * This component hosts all the stuff, that Volume Dialog sliders need. It's recreated alongside
+ * each slider view.
+ */
+@VolumeDialogSliderScope
+@Subcomponent
+interface VolumeDialogSliderComponent {
+
+ fun sliderViewBinder(): VolumeDialogSliderViewBinder
+
+ @Subcomponent.Factory
+ interface Factory {
+
+ fun create(@BindsInstance sliderType: VolumeDialogSliderType): VolumeDialogSliderComponent
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt
new file mode 100644
index 000000000000..9f5e0f6ba1aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderScope.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.dagger
+
+import javax.inject.Scope
+
+/**
+ * Volume Panel Slider dependency injection scope. This scope is created for each of the volume
+ * sliders in the dialog.
+ */
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+@Scope
+annotation class VolumeDialogSliderScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index 876bf2c4a154..2967fe8ca906 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -17,22 +17,21 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
import com.android.systemui.plugins.VolumeDialogController
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/** Operates a state of particular slider of the Volume Dialog. */
+@VolumeDialogSliderScope
class VolumeDialogSliderInteractor
-@AssistedInject
+@Inject
constructor(
- @Assisted private val sliderType: VolumeDialogSliderType,
+ private val sliderType: VolumeDialogSliderType,
volumeDialogStateInteractor: VolumeDialogStateInteractor,
private val volumeDialogController: VolumeDialogController,
) {
@@ -56,11 +55,4 @@ constructor(
setActiveStream(sliderType.audioStream)
}
}
-
- @VolumeDialogScope
- @AssistedFactory
- interface Factory {
-
- fun create(sliderType: VolumeDialogSliderType): VolumeDialogSliderInteractor
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 3bf8c54cb9d8..1c231b521bae 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -24,16 +24,14 @@ import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.launchIn
@@ -41,10 +39,11 @@ import kotlinx.coroutines.flow.onEach
private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L
+@VolumeDialogSliderScope
class VolumeDialogSliderViewBinder
-@AssistedInject
+@Inject
constructor(
- @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel,
+ private val viewModelFactory: VolumeDialogSliderViewModel.Factory,
private val jankListenerFactory: JankListenerFactory,
) {
@@ -58,7 +57,7 @@ constructor(
viewModel(
traceName = "VolumeDialogSliderViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelProvider() },
+ factory = { viewModelFactory.create() },
) { viewModel ->
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
@@ -85,15 +84,6 @@ constructor(
)
}
}
-
- @AssistedFactory
- @VolumeDialogScope
- interface Factory {
-
- fun create(
- viewModelProvider: () -> VolumeDialogSliderViewModel
- ): VolumeDialogSliderViewBinder
- }
}
private suspend fun Slider.setValueAnimated(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 0a4e3f481e88..a17c1e541b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -28,7 +28,6 @@ import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
-import kotlin.math.abs
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -50,15 +49,17 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory)
) { viewModel ->
viewModel.sliders
.onEach { uiModel ->
- uiModel.sliderViewBinder.bind(volumeDialog)
+ uiModel.sliderComponent.sliderViewBinder().bind(volumeDialog)
- val floatingSliderViewBinders = uiModel.floatingSliderViewBinders
+ val floatingSliderViewBinders = uiModel.floatingSliderComponent
floatingSlidersContainer.ensureChildCount(
viewLayoutId = R.layout.volume_dialog_slider_floating,
count = floatingSliderViewBinders.size,
)
- floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
- viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ floatingSliderViewBinders.fastForEachIndexed { index, sliderComponent ->
+ sliderComponent
+ .sliderViewBinder()
+ .bind(floatingSlidersContainer.getChildAt(index))
}
}
.launchIn(this)
@@ -76,7 +77,7 @@ private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int)
}
childCountDelta < 0 -> {
val inflater = LayoutInflater.from(context)
- repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+ repeat(-childCountDelta) { inflater.inflate(viewLayoutId, this, true) }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index ea0b49d5294e..cf04d45d54ff 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -22,7 +22,6 @@ import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
-import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
@@ -53,7 +52,7 @@ private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
class VolumeDialogSliderViewModel
@AssistedInject
constructor(
- @Assisted private val interactor: VolumeDialogSliderInteractor,
+ private val interactor: VolumeDialogSliderInteractor,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@VolumeDialog private val coroutineScope: CoroutineScope,
private val systemClock: SystemClock,
@@ -90,11 +89,11 @@ constructor(
private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
+ private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
+
@AssistedFactory
interface Factory {
- fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
+ fun create(): VolumeDialogSliderViewModel
}
-
- private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index b5b292fa4a66..d1972231d373 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -17,16 +17,13 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
-import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -36,29 +33,21 @@ class VolumeDialogSlidersViewModel
constructor(
@VolumeDialog coroutineScope: CoroutineScope,
private val slidersInteractor: VolumeDialogSlidersInteractor,
- private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
- private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
- private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
+ private val sliderComponentFactory: VolumeDialogSliderComponent.Factory,
) {
val sliders: Flow<VolumeDialogSliderUiModel> =
slidersInteractor.sliders
- .distinctUntilChanged()
.map { slidersModel ->
VolumeDialogSliderUiModel(
- sliderViewBinder = createSliderViewBinder(slidersModel.slider),
- floatingSliderViewBinders =
- slidersModel.floatingSliders.map(::createSliderViewBinder),
+ sliderComponent = sliderComponentFactory.create(slidersModel.slider),
+ floatingSliderComponent =
+ slidersModel.floatingSliders.map(sliderComponentFactory::create),
)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
- private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
- sliderViewBinderFactory.create {
- sliderViewModelFactory.create(sliderInteractorFactory.create(type))
- }
-
@AssistedFactory
interface Factory {
@@ -68,6 +57,6 @@ constructor(
/** Models slider ui */
data class VolumeDialogSliderUiModel(
- val sliderViewBinder: VolumeDialogSliderViewBinder,
- val floatingSliderViewBinders: List<VolumeDialogSliderViewBinder>,
+ val sliderComponent: VolumeDialogSliderComponent,
+ val floatingSliderComponent: List<VolumeDialogSliderComponent>,
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 78eabb27abab..d9a945cfedfd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -17,21 +17,22 @@
package com.android.systemui.volume.dialog.ui.binder
import android.app.Dialog
-import android.graphics.Color
-import android.graphics.PixelFormat
-import android.graphics.drawable.ColorDrawable
+import android.graphics.Rect
+import android.graphics.Region
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
-import android.view.Window
-import android.view.WindowManager
+import android.view.ViewTreeObserver
+import android.view.ViewTreeObserver.InternalInsetsInfo
+import android.widget.FrameLayout
+import androidx.annotation.GravityInt
import com.android.internal.view.RotationPolicy
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
+import com.android.systemui.util.children
import com.android.systemui.volume.SystemUIInterpolators
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
@@ -52,6 +53,8 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
/** Binds the root view of the Volume Dialog. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -61,65 +64,41 @@ class VolumeDialogViewBinder
constructor(
private val volumeResources: VolumeDialogResources,
private val gravityViewModel: VolumeDialogGravityViewModel,
- private val viewModelFactory: VolumeDialogViewModel.Factory,
+ private val dialogViewModelFactory: VolumeDialogViewModel.Factory,
private val jankListenerFactory: JankListenerFactory,
private val tracer: VolumeTracer,
- @VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
private val slidersViewBinder: VolumeDialogSlidersViewBinder,
private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
) {
fun bind(dialog: Dialog) {
- setupDialog(dialog)
- val view: View = dialog.requireViewById(R.id.volume_dialog_container)
- view.alpha = 0f
- view.repeatWhenAttached {
- view.viewModel(
+ // Root view of the Volume Dialog.
+ val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root)
+ // Volume Dialog container view that contains the dialog itself without the floating sliders
+ val container: View = root.requireViewById(R.id.volume_dialog_container)
+ container.alpha = 0f
+ container.repeatWhenAttached {
+ root.viewModel(
traceName = "VolumeDialogViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
+ factory = { dialogViewModelFactory.create() },
) { viewModel ->
+ animateVisibility(container, dialog, viewModel.dialogVisibilityModel)
+
viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
+ gravityViewModel.dialogGravity
+ .onEach { container.setLayoutGravity(it) }
+ .launchIn(this)
- animateVisibility(view, dialog, viewModel.dialogVisibilityModel)
+ launch { root.viewTreeObserver.computeInternalInsetsListener(root) }
awaitCancellation()
}
}
- volumeDialogRingerViewBinder.bind(view)
- slidersViewBinder.bind(view)
- settingsButtonViewBinder.bind(view)
- }
-
- /** Configures [Window] for the [Dialog]. */
- private fun setupDialog(dialog: Dialog) {
- with(dialog.window!!) {
- clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
- addFlags(
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
- WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
- WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- )
- addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
-
- requestFeature(Window.FEATURE_NO_TITLE)
- setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
- setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
- setWindowAnimations(-1)
- setFormat(PixelFormat.TRANSLUCENT)
-
- attributes =
- attributes.apply {
- title = "VolumeDialog" // Not the same as Window#setTitle
- }
- setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-
- gravityViewModel.dialogGravity.onEach { setGravity(it) }.launchIn(coroutineScope)
- }
- dialog.setContentView(R.layout.volume_dialog)
- dialog.setCanceledOnTouchOutside(true)
+ volumeDialogRingerViewBinder.bind(root)
+ slidersViewBinder.bind(root)
+ settingsButtonViewBinder.bind(root)
}
private fun CoroutineScope.animateVisibility(
@@ -209,4 +188,33 @@ constructor(
}
animator.suspendAnimate(jankListenerFactory.dismiss(this, duration))
}
+
+ private suspend fun ViewTreeObserver.computeInternalInsetsListener(viewGroup: ViewGroup) =
+ suspendCancellableCoroutine<Unit> { continuation ->
+ val listener =
+ ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
+ viewGroup.fillTouchableBounds(inoutInfo)
+ }
+ addOnComputeInternalInsetsListener(listener)
+ continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) }
+ }
+
+ private fun ViewGroup.fillTouchableBounds(internalInsetsInfo: InternalInsetsInfo) {
+ for (child in children) {
+ val boundsRect = Rect()
+ internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION)
+
+ child.getBoundsInWindow(boundsRect, false)
+ internalInsetsInfo.touchableRegion.op(boundsRect, Region.Op.UNION)
+ }
+ val boundsRect = Rect()
+ getBoundsInWindow(boundsRect, false)
+ }
+
+ private fun View.setLayoutGravity(@GravityInt newGravity: Int) {
+ val frameLayoutParams =
+ layoutParams as? FrameLayout.LayoutParams
+ ?: error("View must be a child of a FrameLayout")
+ layoutParams = frameLayoutParams.apply { gravity = newGravity }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 9be669f3df0c..869a6a2e87d5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -18,16 +18,19 @@ package com.android.systemui.volume.dialog.ui.viewmodel
import android.content.Context
import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import com.android.systemui.volume.dialog.shared.model.streamLabel
-import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
/** Provides a state for the Volume Dialog. */
@@ -38,18 +41,21 @@ constructor(
private val context: Context,
dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
- private val volumeDialogSliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
+ volumeDialogStateInteractor: VolumeDialogStateInteractor,
) {
val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
dialogVisibilityInteractor.dialogVisibility
val dialogTitle: Flow<String> =
- volumeDialogSlidersInteractor.sliders.flatMapLatest { slidersModel ->
- val interactor = volumeDialogSliderInteractorFactory.create(slidersModel.slider)
- interactor.slider.map { sliderModel ->
- context.getString(R.string.volume_dialog_title, sliderModel.streamLabel(context))
+ combine(
+ volumeDialogStateInteractor.volumeDialogState,
+ volumeDialogSlidersInteractor.sliders.map { it.slider },
+ ) { state: VolumeDialogStateModel, sliderType: VolumeDialogSliderType ->
+ state.streamModels[sliderType.audioStream]?.let { model ->
+ context.getString(R.string.volume_dialog_title, model.streamLabel(context))
+ }
}
- }
+ .filterNotNull()
@AssistedFactory
interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
index dacd6c78b034..b9f47d7ad110 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
@@ -22,15 +22,17 @@ import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
import android.os.Handler
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.flowOn
interface MediaControllerInteractor {
@@ -43,14 +45,16 @@ class MediaControllerInteractorImpl
@Inject
constructor(
@Background private val backgroundHandler: Handler,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
) : MediaControllerInteractor {
override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> {
return conflatedCallbackFlow {
- val callback = MediaControllerCallbackProducer(this)
- mediaController.registerCallback(callback, backgroundHandler)
- awaitClose { mediaController.unregisterCallback(callback) }
- }
+ val callback = MediaControllerCallbackProducer(this)
+ mediaController.registerCallback(callback, backgroundHandler)
+ awaitClose { mediaController.unregisterCallback(callback) }
+ }
+ .flowOn(backgroundCoroutineContext)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index aa07cfd26bdb..b3848a6d7817 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -20,10 +20,12 @@ import android.content.pm.PackageManager
import android.media.VolumeProvider
import android.media.session.MediaController
import android.util.Log
+import androidx.annotation.WorkerThread
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.concurrency.Execution
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
@@ -62,6 +64,7 @@ constructor(
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
private val mediaControllerInteractor: MediaControllerInteractor,
+ private val execution: Execution,
) {
private val activeMediaControllers: Flow<MediaControllers> =
@@ -82,9 +85,10 @@ constructor(
.map {
MediaDeviceSessions(
local = it.local?.mediaDeviceSession(),
- remote = it.remote?.mediaDeviceSession()
+ remote = it.remote?.mediaDeviceSession(),
)
}
+ .flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSessions(null, null))
/** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */
@@ -115,55 +119,43 @@ constructor(
val currentConnectedDevice: Flow<MediaDevice?> =
localMediaRepository.flatMapLatest { it.currentConnectedDevice }.distinctUntilChanged()
- private suspend fun getApplicationLabel(packageName: String): CharSequence? {
- return try {
- withContext(backgroundCoroutineContext) {
- val appInfo =
- packageManager.getApplicationInfo(
- packageName,
- PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
- )
- appInfo.loadLabel(packageManager)
- }
- } catch (e: PackageManager.NameNotFoundException) {
- Log.e(TAG, "Unable to find info for package: $packageName")
- null
- }
- }
-
/** Finds local and remote media controllers. */
- private fun getMediaControllers(
- controllers: Collection<MediaController>,
- ): MediaControllers {
- var localController: MediaController? = null
- var remoteController: MediaController? = null
- val remoteMediaSessions: MutableSet<String> = mutableSetOf()
- for (controller in controllers) {
- val playbackInfo: MediaController.PlaybackInfo = controller.playbackInfo ?: continue
- when (playbackInfo.playbackType) {
- MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE -> {
- // MediaController can't be local if there is a remote one for the same package
- if (localController?.packageName.equals(controller.packageName)) {
- localController = null
+ private suspend fun getMediaControllers(
+ controllers: Collection<MediaController>
+ ): MediaControllers =
+ withContext(backgroundCoroutineContext) {
+ var localController: MediaController? = null
+ var remoteController: MediaController? = null
+ val remoteMediaSessions: MutableSet<String> = mutableSetOf()
+ for (controller in controllers) {
+ val playbackInfo: MediaController.PlaybackInfo = controller.playbackInfo ?: continue
+ when (playbackInfo.playbackType) {
+ MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE -> {
+ // MediaController can't be local if there is a remote one for the same
+ // package
+ if (localController?.packageName.equals(controller.packageName)) {
+ localController = null
+ }
+ if (!remoteMediaSessions.contains(controller.packageName)) {
+ remoteMediaSessions.add(controller.packageName)
+ remoteController = chooseController(remoteController, controller)
+ }
}
- if (!remoteMediaSessions.contains(controller.packageName)) {
- remoteMediaSessions.add(controller.packageName)
- remoteController = chooseController(remoteController, controller)
+ MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL -> {
+ if (controller.packageName in remoteMediaSessions) continue
+ localController = chooseController(localController, controller)
}
}
- MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL -> {
- if (controller.packageName in remoteMediaSessions) continue
- localController = chooseController(localController, controller)
- }
}
+ MediaControllers(local = localController, remote = remoteController)
}
- return MediaControllers(local = localController, remote = remoteController)
- }
+ @WorkerThread
private fun chooseController(
currentController: MediaController?,
newController: MediaController,
): MediaController {
+ require(!execution.isMainThread())
if (currentController == null) {
return newController
}
@@ -175,12 +167,26 @@ constructor(
return currentController
}
- private suspend fun MediaController.mediaDeviceSession(): MediaDeviceSession? {
+ @WorkerThread
+ private fun MediaController.mediaDeviceSession(): MediaDeviceSession? {
+ require(!execution.isMainThread())
+ val applicationLabel =
+ try {
+ packageManager
+ .getApplicationInfo(
+ packageName,
+ PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER,
+ )
+ .loadLabel(packageManager)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Unable to find info for package: $packageName")
+ null
+ } ?: return null
return MediaDeviceSession(
packageName = packageName,
sessionToken = sessionToken,
canAdjustVolume = playbackInfo.volumeControl != VolumeProvider.VOLUME_CONTROL_FIXED,
- appLabel = getApplicationLabel(packageName) ?: return null
+ appLabel = applicationLabel,
)
}
@@ -195,10 +201,7 @@ constructor(
.onStart { emit(this@stateChanges) }
}
- private data class MediaControllers(
- val local: MediaController?,
- val remote: MediaController?,
- )
+ private data class MediaControllers(val local: MediaController?, val remote: MediaController?)
private companion object {
const val TAG = "MediaOutputInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 53e6b4f82b7e..761993b3cda7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -330,13 +330,19 @@ public class WalletScreenController implements
QAWalletCardViewInfo(Context context, WalletCard walletCard) {
mWalletCard = walletCard;
Icon cardImageIcon = mWalletCard.getCardImage();
- if (cardImageIcon.getType() == Icon.TYPE_URI) {
- mCardDrawable = null;
- } else {
+ if (cardImageIcon.getType() == Icon.TYPE_BITMAP
+ || cardImageIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
mCardDrawable = mWalletCard.getCardImage().loadDrawable(context);
+ } else {
+ mCardDrawable = null;
}
Icon icon = mWalletCard.getCardIcon();
- mIconDrawable = icon == null ? null : icon.loadDrawable(context);
+ if (icon != null && (icon.getType() == Icon.TYPE_BITMAP
+ || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP)) {
+ mIconDrawable = icon.loadDrawable(context);
+ } else {
+ mIconDrawable = null;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 1a399341f12c..ca9b866e2d18 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -283,6 +283,11 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
return mCardLabel;
}
+ @VisibleForTesting
+ ImageView getIcon() {
+ return mIcon;
+ }
+
@Nullable
private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
Drawable icon = walletCard.getIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 8039e00159f0..073781e6101d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -271,6 +271,12 @@ public final class WMShell implements
// No op.
}
}, mSysUiMainExecutor);
+ pip.addOnIsInPipStateChangedListener((isInPip) -> {
+ if (!isInPip) {
+ mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING, false)
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ }
+ });
mSysUiState.addCallback(sysUiStateFlag -> {
mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json
index aa8044515ea2..aa8044515ea2 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json
index a840d3cb1225..7abff2c74531 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenLaunching_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimation_whenLaunching_withSpring.json",
+ "goldenIdentifier": "backgroundAnimation_whenLaunching_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimation_whenLaunching[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimation_whenLaunching_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json
index aa8044515ea2..aa8044515ea2 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json
index a840d3cb1225..561961145ca1 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withFade_whenReturning_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimation_whenReturning_withSpring.json",
+ "goldenIdentifier": "backgroundAnimation_whenReturning_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimation_whenReturning[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimation_whenReturning_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json
index 7f623575fef4..7f623575fef4 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json
index 18eedd450751..825190ba7a32 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenLaunching_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimationWithoutFade_whenLaunching_withSpring.json",
+ "goldenIdentifier": "backgroundAnimationWithoutFade_whenLaunching_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimationWithoutFade_whenLaunching[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimationWithoutFade_whenLaunching_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json
index 98005c53f6e0..98005c53f6e0 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withAnimator.json
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json
index 18eedd450751..63c263175122 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationTimeSeries_withoutFade_whenReturning_withSpring.json
@@ -33,118 +33,118 @@
"bottom": 0
},
{
- "left": 94,
- "top": 284,
- "right": 206,
+ "left": 104,
+ "top": 285,
+ "right": 215,
"bottom": 414
},
{
- "left": 83,
- "top": 251,
- "right": 219,
+ "left": 92,
+ "top": 252,
+ "right": 227,
"bottom": 447
},
{
- "left": 70,
- "top": 212,
- "right": 234,
- "bottom": 485
+ "left": 77,
+ "top": 213,
+ "right": 242,
+ "bottom": 486
},
{
- "left": 57,
- "top": 173,
- "right": 250,
- "bottom": 522
+ "left": 63,
+ "top": 175,
+ "right": 256,
+ "bottom": 524
},
{
- "left": 46,
- "top": 139,
- "right": 264,
- "bottom": 555
+ "left": 50,
+ "top": 141,
+ "right": 269,
+ "bottom": 558
},
{
- "left": 36,
- "top": 109,
- "right": 276,
- "bottom": 584
+ "left": 40,
+ "top": 112,
+ "right": 279,
+ "bottom": 587
},
{
- "left": 28,
- "top": 84,
- "right": 285,
- "bottom": 608
+ "left": 31,
+ "top": 88,
+ "right": 288,
+ "bottom": 611
},
{
- "left": 21,
- "top": 65,
- "right": 293,
- "bottom": 627
+ "left": 23,
+ "top": 68,
+ "right": 296,
+ "bottom": 631
},
{
- "left": 16,
- "top": 49,
- "right": 300,
- "bottom": 642
+ "left": 18,
+ "top": 53,
+ "right": 301,
+ "bottom": 646
},
{
- "left": 12,
- "top": 36,
- "right": 305,
- "bottom": 653
+ "left": 13,
+ "top": 41,
+ "right": 306,
+ "bottom": 658
},
{
- "left": 9,
- "top": 27,
- "right": 308,
- "bottom": 662
+ "left": 10,
+ "top": 31,
+ "right": 309,
+ "bottom": 667
},
{
"left": 7,
- "top": 20,
+ "top": 24,
"right": 312,
- "bottom": 669
+ "bottom": 673
},
{
"left": 5,
- "top": 14,
+ "top": 18,
"right": 314,
- "bottom": 675
+ "bottom": 678
},
{
"left": 4,
- "top": 11,
+ "top": 13,
"right": 315,
- "bottom": 678
+ "bottom": 681
},
{
"left": 3,
- "top": 8,
+ "top": 10,
"right": 316,
- "bottom": 681
+ "bottom": 684
},
{
"left": 2,
- "top": 5,
+ "top": 7,
"right": 317,
- "bottom": 684
+ "bottom": 685
},
{
"left": 1,
- "top": 4,
+ "top": 5,
"right": 318,
- "bottom": 685
+ "bottom": 687
},
{
"left": 1,
- "top": 3,
+ "top": 4,
"right": 318,
- "bottom": 686
+ "bottom": 688
},
{
"left": 0,
- "top": 2,
+ "top": 3,
"right": 319,
- "bottom": 687
+ "bottom": 688
}
]
},
@@ -371,5 +371,14 @@
0
]
}
- ]
+ ],
+ "\/\/metadata": {
+ "goldenRepoPath": "frameworks\/base\/packages\/SystemUI\/tests\/goldens\/backgroundAnimationWithoutFade_whenReturning_withSpring.json",
+ "goldenIdentifier": "backgroundAnimationWithoutFade_whenReturning_withSpring",
+ "testClassName": "TransitionAnimatorTest",
+ "testMethodName": "backgroundAnimationWithoutFade_whenReturning[true]",
+ "deviceLocalPath": "\/data\/user\/0\/com.android.systemui.tests\/files\/platform_screenshots",
+ "result": "FAILED",
+ "videoLocation": "TransitionAnimatorTest\/backgroundAnimationWithoutFade_whenReturning_withSpring.actual.mp4"
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index a95735e56f04..82cfab6fde06 100644
--- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -56,7 +56,6 @@ import java.util.Collections;
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase {
-
private static final String TAG = "AAA++VerifyTest";
private static final Class[] BASE_CLS_TO_INCLUDE = {
@@ -149,6 +148,9 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC
*/
private boolean isTestClass(Class<?> loadedClass) {
try {
+ if (loadedClass.getAnnotation(SkipSysuiVerification.class) != null) {
+ return false;
+ }
if (Modifier.isAbstract(loadedClass.getModifiers())) {
logDebug(String.format("Skipping abstract class %s: not a test",
loadedClass.getName()));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 2b167e4c5da4..65b62737b692 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -56,6 +56,7 @@ import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.plugins.clocks.ZenData.ZenMode
import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ZenModeController
@@ -128,6 +129,7 @@ class ClockEventControllerTest : SysuiTestCase() {
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var zenModeController: ZenModeController
private var zenModeControllerCallback: ZenModeController.Callback? = null
@@ -153,6 +155,7 @@ class ClockEventControllerTest : SysuiTestCase() {
.thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
whenever(smallClockController.theme).thenReturn(ThemeConfig(true, null))
whenever(largeClockController.theme).thenReturn(ThemeConfig(true, null))
+ whenever(userTracker.userId).thenReturn(1)
zenModeRepository.addMode(MANUAL_DND_INACTIVE)
@@ -177,6 +180,7 @@ class ClockEventControllerTest : SysuiTestCase() {
withDeps.featureFlags,
zenModeController,
kosmos.zenModeInteractor,
+ userTracker,
)
underTest.clock = clock
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 3d9eb53d436a..a39ca5de787d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -103,6 +103,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.service.dreams.IDreamManager;
import android.service.trust.TrustAgentService;
@@ -129,6 +130,7 @@ import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.keyguard.logging.SimLogger;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -190,6 +192,7 @@ import platform.test.runner.parameterized.Parameters;
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
+@EnableFlags(Flags.FLAG_USER_ENCRYPTED_SOURCE)
public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private static final String PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY =
"test_app_fp_listen_on_occluding_activity";
@@ -1292,12 +1295,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testIsUserUnlocked() {
+ when(mUserManager.isUserUnlocked(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// mUserManager will report the user as unlocked on @Before
assertThat(
mKeyguardUpdateMonitor.isUserUnlocked(mSelectedUserInteractor.getSelectedUserId()))
.isTrue();
// Invalid user should not be unlocked.
int randomUser = 99;
+ when(mUserManager.isUserUnlocked(randomUser)).thenReturn(false);
assertThat(mKeyguardUpdateMonitor.isUserUnlocked(randomUser)).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index c6e4e0db8945..fa88f6207c49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -46,7 +46,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
import android.window.InputTransferToken;
@@ -1030,10 +1029,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
callback,
sysUiState,
secureSettings,
- scvhSupplier,
- sfVsyncFrameProvider,
- WindowManagerGlobal::getWindowSession,
- viewCaptureAwareWindowManager);
+ scvhSupplier);
mSpyController = Mockito.mock(WindowMagnificationController.class);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
deleted file mode 100644
index 9b09ec2ec55f..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ /dev/null
@@ -1,1594 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.accessibility;
-
-import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowInsets.Type.systemGestures;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.AdditionalAnswers.returnsSecondArg;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static java.util.Arrays.asList;
-
-import android.animation.ValueAnimator;
-import android.annotation.IdRes;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.provider.Settings;
-import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.util.Size;
-import android.view.AttachedSurfaceControl;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewRootImpl;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.widget.FrameLayout;
-import android.window.InputTransferToken;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.util.FakeSharedPreferences;
-import com.android.systemui.util.leak.ReferenceTestUtils;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.utils.os.FakeHandler;
-
-import com.google.common.util.concurrent.AtomicDouble;
-
-import org.junit.After;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Supplier;
-
-@LargeTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidJUnit4.class)
-@EnableFlags(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
-public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase {
-
- @Rule
- // NOTE: pass 'null' to allow this test advances time on the main thread.
- public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(/* test= */ null);
-
- private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
- @Mock
- private MirrorWindowControl mMirrorWindowControl;
- @Mock
- private WindowMagnifierCallback mWindowMagnifierCallback;
- @Mock
- IRemoteMagnificationAnimationCallback mAnimationCallback;
- @Mock
- IRemoteMagnificationAnimationCallback mAnimationCallback2;
-
- private SurfaceControl.Transaction mTransaction;
- @Mock
- private SecureSettings mSecureSettings;
- @Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
-
- private long mWaitAnimationDuration;
- private long mWaitBounceEffectDuration;
-
- private Handler mHandler;
- private TestableWindowManager mWindowManager;
- private SysUiState mSysUiState;
- private Resources mResources;
- private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
- private WindowMagnificationController mWindowMagnificationController;
- private Instrumentation mInstrumentation;
- private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
- private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
- private View mSpyView;
- private View.OnTouchListener mTouchListener;
-
- private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
-
- // This list contains all SurfaceControlViewHosts created during a given test. If the
- // magnification window is recreated during a test, the list will contain more than a single
- // element.
- private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>();
- // The most recently created SurfaceControlViewHost.
- private SurfaceControlViewHost mSurfaceControlViewHost;
- private KosmosJavaAdapter mKosmos;
- private FakeSharedPreferences mSharedPreferences;
-
- /**
- * return whether window magnification is supported for current test context.
- */
- private boolean isWindowModeSupported() {
- return getContext().getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mContext = spy(mContext);
- mKosmos = new KosmosJavaAdapter(this);
- mContext = Mockito.spy(getContext());
- mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- mWindowManager = spy(new TestableWindowManager(wm));
-
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
- mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin());
- mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
- when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
- returnsSecondArg());
- when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
- returnsSecondArg());
-
- mResources = getContext().getOrCreateTestableResources().getResources();
- // prevent the config orientation from undefined, which may cause config.diff method
- // neglecting the orientation update.
- if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) {
- mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
- }
-
- // Using the animation duration in WindowMagnificationAnimationController for testing.
- mWaitAnimationDuration = mResources.getInteger(
- com.android.internal.R.integer.config_longAnimTime);
- // Using the bounce effect duration in WindowMagnificationController for testing.
- mWaitBounceEffectDuration = mResources.getInteger(
- com.android.internal.R.integer.config_shortAnimTime);
-
- mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
- mContext, mValueAnimator);
- Supplier<SurfaceControlViewHost> scvhSupplier = () -> {
- mSurfaceControlViewHost = spy(new SurfaceControlViewHost(
- mContext, mContext.getDisplay(), new InputTransferToken(),
- "WindowMagnification"));
- ViewRootImpl viewRoot = mock(ViewRootImpl.class);
- when(mSurfaceControlViewHost.getRootSurfaceControl()).thenReturn(viewRoot);
- mSurfaceControlViewHosts.add(mSurfaceControlViewHost);
- return mSurfaceControlViewHost;
- };
- mTransaction = spy(new SurfaceControl.Transaction());
- mSharedPreferences = new FakeSharedPreferences();
- when(mContext.getSharedPreferences(
- eq("window_magnification_preferences"), anyInt()))
- .thenReturn(mSharedPreferences);
- mWindowMagnificationController =
- new WindowMagnificationController(
- mContext,
- mHandler,
- mWindowMagnificationAnimationController,
- mMirrorWindowControl,
- mTransaction,
- mWindowMagnifierCallback,
- mSysUiState,
- mSecureSettings,
- scvhSupplier,
- /* sfVsyncFrameProvider= */ null,
- /* globalWindowSessionSupplier= */ null,
- mViewCaptureAwareWindowManager);
-
- verify(mMirrorWindowControl).setWindowDelegate(
- any(MirrorWindowControl.MirrorWindowDelegate.class));
- mSpyView = Mockito.spy(new View(mContext));
- doAnswer((invocation) -> {
- mTouchListener = invocation.getArgument(0);
- return null;
- }).when(mSpyView).setOnTouchListener(
- any(View.OnTouchListener.class));
-
- // skip test if window magnification is not supported to prevent fail results. (b/279820875)
- Assume.assumeTrue(isWindowModeSupported());
- }
-
- @After
- public void tearDown() {
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.deleteWindowMagnification();
- });
- mValueAnimator.cancel();
- }
-
- @Test
- public void initWindowMagnificationController_checkAllowDiagonalScrollingWithSecureSettings() {
- verify(mSecureSettings).getIntForUser(
- eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
- /* def */ eq(1), /* userHandle= */ anyInt());
- assertThat(mWindowMagnificationController.isDiagonalScrollingEnabled()).isTrue();
- }
-
- @Test
- public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- verify(mMirrorWindowControl).showControl();
- verify(mWindowMagnifierCallback,
- timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeastOnce()).onWindowMagnifierBoundsChanged(
- eq(mContext.getDisplayId()), any(Rect.class));
- }
-
- @Test
- public void enableWindowMagnification_notifySourceBoundsChanged() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
- /* magnificationFrameOffsetRatioY= */ 0, null));
-
- // Waits for the surface created
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
- (eq(mContext.getDisplayId())), any());
- }
-
- @Test
- public void enableWindowMagnification_disabled_notifySourceBoundsChanged() {
- enableWindowMagnification_notifySourceBoundsChanged();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.deleteWindowMagnification(null));
- Mockito.reset(mWindowMagnifierCallback);
-
- enableWindowMagnification_notifySourceBoundsChanged();
- }
-
- @Test
- public void enableWindowMagnification_withAnimation_schedulesFrame() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
- 10, /* magnificationFrameOffsetRatioX= */ 0,
- /* magnificationFrameOffsetRatioY= */ 0,
- Mockito.mock(IRemoteMagnificationAnimationCallback.class));
- });
- advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
-
- verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
- eq(Surface.ROTATION_0));
- }
-
- @Test
- public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, 0, 0, null);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifier(10, 10);
- });
-
- final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
- verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
- (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertThat(mWindowMagnificationController.getCenterX())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
- assertThat(mWindowMagnificationController.getCenterY())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
- }
-
- @Test
- public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
- // Wait for Rects updated.
- waitForIdleSync();
-
- List<Rect> rects = mSurfaceControlViewHost.getView().getSystemGestureExclusionRects();
- assertThat(rects).isNotEmpty();
- }
-
- @Ignore("The default window size should be constrained after fixing b/288056772")
- @Test
- public void enableWindowMagnification_LargeScreen_windowSizeIsConstrained() {
- final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
- mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- final int halfScreenSize = screenSize / 2;
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- // The frame size should be the half of smaller value of window height/width unless it
- //exceed the max frame size.
- assertThat(params.width).isLessThan(halfScreenSize);
- assertThat(params.height).isLessThan(halfScreenSize);
- }
-
- @Test
- public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN,
- Float.NaN));
-
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.deleteWindowMagnification());
-
- verify(mMirrorWindowControl).destroyControl();
- verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController);
- }
-
- @Test
- public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- bounds.bottom);
- });
- ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.deleteWindowMagnification();
- });
-
- verify(mMirrorWindowControl).destroyControl();
- assertThat(hasMagnificationOverlapFlag()).isFalse();
- }
-
- @Test
- public void deleteWindowMagnification_notifySourceBoundsChanged() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN,
- Float.NaN));
-
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.deleteWindowMagnification());
-
- // The first time is for notifying magnification enabled and the second time is for
- // notifying magnification disabled.
- verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged(
- (eq(mContext.getDisplayId())), any());
- }
-
- @Test
- public void moveMagnifier_schedulesFrame() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- waitForIdleSync();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.moveWindowMagnifier(100f, 100f));
-
- verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(),
- eq(Surface.ROTATION_0));
- }
-
- @Test
- public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
- throws RemoteException {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, 0, 0, null);
- });
-
- final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
- final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
-
- reset(mWindowMagnifierCallback);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- targetCenterX, targetCenterY, mAnimationCallback);
- });
- advanceTimeBy(mWaitAnimationDuration);
-
- verify(mAnimationCallback, times(1)).onResult(eq(true));
- verify(mAnimationCallback, never()).onResult(eq(false));
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertThat(mWindowMagnificationController.getCenterX())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
- assertThat(mWindowMagnificationController.getCenterY())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
- assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(targetCenterX);
- assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(targetCenterY);
- }
-
- @Test
- public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
- throws RemoteException {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
- Float.NaN, 0, 0, null);
- });
-
- final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
- final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
-
- reset(mWindowMagnifierCallback);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 10, centerY + 10, mAnimationCallback);
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 20, centerY + 20, mAnimationCallback);
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 30, centerY + 30, mAnimationCallback);
- mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 40, centerY + 40, mAnimationCallback2);
- });
- advanceTimeBy(mWaitAnimationDuration);
-
- // only the last one callback will return true
- verify(mAnimationCallback2).onResult(eq(true));
- // the others will return false
- verify(mAnimationCallback, times(3)).onResult(eq(false));
- verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
- .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
- assertThat(mWindowMagnificationController.getCenterX())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
- assertThat(mWindowMagnificationController.getCenterY())
- .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
- assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(centerX + 40);
- assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(centerY + 40);
- }
-
- @Test
- public void setScale_enabled_expectedValueAndUpdateStateDescription() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(2.0f,
- Float.NaN, Float.NaN));
-
- mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
-
- assertThat(mWindowMagnificationController.getScale()).isEqualTo(3.0f);
- final View mirrorView = mSurfaceControlViewHost.getView();
- assertThat(mirrorView).isNotNull();
- assertThat(mirrorView.getStateDescription().toString()).contains("300");
- }
-
- @Test
- public void onConfigurationChanged_disabled_withoutException() {
- Display display = Mockito.spy(mContext.getDisplay());
- when(display.getRotation()).thenReturn(Surface.ROTATION_90);
- when(mContext.getDisplay()).thenReturn(display);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
- }
-
- @Test
- public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
- final int newRotation = simulateRotateTheDevice();
- final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
- final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
- final float displayWidth = windowBounds.width();
- final PointF magnifiedCenter = new PointF(center, center + 5f);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- magnifiedCenter.x, magnifiedCenter.y);
- // Get the center again in case the center we set is out of screen.
- magnifiedCenter.set(mWindowMagnificationController.getCenterX(),
- mWindowMagnificationController.getCenterY());
- });
- // Rotate the window clockwise 90 degree.
- windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
- windowBounds.right);
- mWindowManager.setWindowBounds(windowBounds);
-
- mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
- ActivityInfo.CONFIG_ORIENTATION));
-
- assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
- final PointF expectedCenter = new PointF(magnifiedCenter.y,
- displayWidth - magnifiedCenter.x);
- final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
- mWindowMagnificationController.getCenterY());
- assertThat(actualCenter).isEqualTo(expectedCenter);
- }
-
- @Test
- public void onOrientationChanged_disabled_updateDisplayRotation() {
- final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
- // Rotate the window clockwise 90 degree.
- windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
- windowBounds.right);
- mWindowManager.setWindowBounds(windowBounds);
- final int newRotation = simulateRotateTheDevice();
-
- mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
- ActivityInfo.CONFIG_ORIENTATION));
-
- assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
- }
-
- @Test
- public void onScreenSizeAndDensityChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
- // The default position is at the center of the screen.
- final float expectedRatio = 0.5f;
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen size and density change
- mContext.getResources().getConfiguration().smallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // The ratio of center to window size should be the same.
- assertThat(mWindowMagnificationController.getCenterX() / testWindowBounds.width())
- .isEqualTo(expectedRatio);
- assertThat(mWindowMagnificationController.getCenterY() / testWindowBounds.height())
- .isEqualTo(expectedRatio);
- }
-
- @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
- @Test
- public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
- int newSmallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- int windowFrameSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
- mSharedPreferences
- .edit()
- .putString(String.valueOf(newSmallestScreenWidthDp),
- preferredWindowSize.toString())
- .commit();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen density and size change
- mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // wait for rect update
- waitForIdleSync();
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // The width and height of the view include the magnification frame and the margins.
- assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- }
-
- @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
- @Test
- public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() {
- int newSmallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- int windowFrameSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
- mSharedPreferences
- .edit()
- .putString(String.valueOf(newSmallestScreenWidthDp),
- WindowMagnificationFrameSpec.serialize(
- WindowMagnificationSettings.MagnificationSize.CUSTOM,
- preferredWindowSize))
- .commit();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- // Screen density and size change
- mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
- final Rect testWindowBounds = new Rect(
- mWindowManager.getCurrentWindowMetrics().getBounds());
- testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
- testWindowBounds.right + 100, testWindowBounds.bottom + 100);
- mWindowManager.setWindowBounds(testWindowBounds);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- // wait for rect update
- waitForIdleSync();
- verify(mWindowMagnifierCallback).onWindowMagnifierBoundsRestored(
- eq(mContext.getDisplayId()),
- eq(WindowMagnificationSettings.MagnificationSize.CUSTOM));
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // The width and height of the view include the magnification frame and the margins.
- assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
- }
-
- @Test
- public void screenSizeIsChangedToLarge_enabled_defaultWindowSize() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
- final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
- // Screen size and density change
- mContext.getResources().getConfiguration().smallestScreenWidthDp =
- mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
- mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
- });
-
- final int defaultWindowSize =
- mWindowMagnificationController.getMagnificationWindowSizeFromIndex(
- WindowMagnificationSettings.MagnificationSize.MEDIUM);
- ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
-
- assertThat(params.width).isEqualTo(defaultWindowSize);
- assertThat(params.height).isEqualTo(defaultWindowSize);
- }
-
- @Test
- public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- Mockito.reset(mWindowManager);
- Mockito.reset(mMirrorWindowControl);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- });
-
- verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
- verify(mSurfaceControlViewHosts.get(0)).release();
- verify(mMirrorWindowControl).destroyControl();
- verify(mSurfaceControlViewHosts.get(1)).setView(any(), any());
- verify(mMirrorWindowControl).showControl();
- }
-
- @Test
- public void onDensityChanged_disabled_updateDimensions() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- });
-
- verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
- }
-
- @Test
- public void initializeA11yNode_enabled_expectedValues() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
- Float.NaN);
- });
- final View mirrorView = mSurfaceControlViewHost.getView();
- assertThat(mirrorView).isNotNull();
- final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
-
- mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
-
- assertThat(nodeInfo.getContentDescription()).isNotNull();
- assertThat(nodeInfo.getStateDescription().toString()).contains("250");
- assertThat(nodeInfo.getActionList()).containsExactlyElementsIn(asList(
- new AccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
- mContext.getResources().getString(
- R.string.magnification_open_settings_click_label)),
- new AccessibilityAction(R.id.accessibility_action_zoom_in,
- mContext.getString(R.string.accessibility_control_zoom_in)),
- new AccessibilityAction(R.id.accessibility_action_zoom_out,
- mContext.getString(R.string.accessibility_control_zoom_out)),
- new AccessibilityAction(R.id.accessibility_action_move_right,
- mContext.getString(R.string.accessibility_control_move_right)),
- new AccessibilityAction(R.id.accessibility_action_move_left,
- mContext.getString(R.string.accessibility_control_move_left)),
- new AccessibilityAction(R.id.accessibility_action_move_down,
- mContext.getString(R.string.accessibility_control_move_down)),
- new AccessibilityAction(R.id.accessibility_action_move_up,
- mContext.getString(R.string.accessibility_control_move_up))));
- }
-
- @Test
- public void performA11yActions_visible_expectedResults() {
- final int displayId = mContext.getDisplayId();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(1.5f, Float.NaN,
- Float.NaN);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null))
- .isTrue();
- // Minimum scale is 1.0.
- verify(mWindowMagnifierCallback).onPerformScaleAction(
- eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
-
- assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null))
- .isTrue();
- verify(mWindowMagnifierCallback).onPerformScaleAction(
- eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
-
- // TODO: Verify the final state when the mirror surface is visible.
- assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null))
- .isTrue();
- assertThat(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null))
- .isTrue();
- assertThat(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null))
- .isTrue();
- assertThat(
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null))
- .isTrue();
- verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
-
- assertThat(mirrorView.performAccessibilityAction(
- AccessibilityAction.ACTION_CLICK.getId(), null)).isTrue();
- verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
- }
-
- @Test
- public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
- final int displayId = mContext.getDisplayId();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
- Float.NaN);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
-
- verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
- }
-
- @Test
- public void windowMagnifierEditMode_performA11yClickAction_exitEditMode() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- View closeButton = getInternalView(R.id.close_button);
- View bottomRightCorner = getInternalView(R.id.bottom_right_corner);
- View bottomLeftCorner = getInternalView(R.id.bottom_left_corner);
- View topRightCorner = getInternalView(R.id.top_right_corner);
- View topLeftCorner = getInternalView(R.id.top_left_corner);
-
- assertThat(closeButton.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(topRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(topLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- mInstrumentation.runOnMainSync(() ->
- mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
- null));
-
- assertThat(closeButton.getVisibility()).isEqualTo(View.GONE);
- assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.GONE);
- assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.GONE);
- assertThat(topRightCorner.getVisibility()).isEqualTo(View.GONE);
- assertThat(topLeftCorner.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
-
- public void windowWidthIsNotMax_performA11yActionIncreaseWidth_windowWidthIncreased() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = (int) (windowBounds.width() * 0.8);
- final int startingHeight = (int) (windowBounds.height() * 0.8);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_increase_window_width, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window width includes the magnifier frame and the margin. Increasing the window size
- // will be increasing the amount of the frame size only.
- int newWindowWidth =
- (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
- assertThat(actualWindowHeight.get()).isEqualTo(startingHeight);
- }
-
- @Test
- public void windowHeightIsNotMax_performA11yActionIncreaseHeight_windowHeightIncreased() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = (int) (windowBounds.width() * 0.8);
- final int startingHeight = (int) (windowBounds.height() * 0.8);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_increase_window_height, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window height includes the magnifier frame and the margin. Increasing the window size
- // will be increasing the amount of the frame size only.
- int newWindowHeight =
- (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(startingWidth);
- assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
- }
-
- @Test
- public void windowWidthIsMax_noIncreaseWindowWidthA11yAction() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = windowBounds.width();
- final int startingHeight = windowBounds.height();
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_increase_window_width,
- mContext.getString(
- R.string.accessibility_control_increase_window_width)));
- }
-
- @Test
- public void windowHeightIsMax_noIncreaseWindowHeightA11yAction() {
- final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- final int startingWidth = windowBounds.width();
- final int startingHeight = windowBounds.height();
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_increase_window_height, null));
- }
-
- @Test
- public void windowWidthIsNotMin_performA11yActionDecreaseWidth_windowWidthDecreased() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = (int) (mMinWindowSize * 1.1);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_decrease_window_width, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window width includes the magnifier frame and the margin. Decreasing the window size
- // will be decreasing the amount of the frame size only.
- int newWindowWidth =
- (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
- assertThat(actualWindowHeight.get()).isEqualTo(startingSize);
- }
-
- @Test
- public void windowHeightIsNotMin_performA11yActionDecreaseHeight_windowHeightDecreased() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = (int) (mMinWindowSize * 1.1);
- final float changeWindowSizeAmount = mContext.getResources().getFraction(
- R.fraction.magnification_resize_window_size_amount,
- /* base= */ 1,
- /* pbase= */ 1);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mirrorView.performAccessibilityAction(
- R.id.accessibility_action_decrease_window_height, null);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- // Window height includes the magnifier frame and the margin. Decreasing the window size
- // will be decreasing the amount of the frame size only.
- int newWindowHeight =
- (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
- + 2 * mirrorSurfaceMargin;
- assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
- assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
- }
-
- @Test
- public void windowWidthIsMin_noDecreaseWindowWidthA11yAction() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = mMinWindowSize;
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_decrease_window_width, null));
- }
-
- @Test
- public void windowHeightIsMin_noDecreaseWindowHeightA11yAction() {
- int mMinWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int startingSize = mMinWindowSize;
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
- final AccessibilityNodeInfo accessibilityNodeInfo =
- mirrorView.createAccessibilityNodeInfo();
- assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
- new AccessibilityAction(
- R.id.accessibility_action_decrease_window_height, null));
- }
-
- @Test
- public void enableWindowMagnification_hasA11yWindowTitle() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- assertThat(getAccessibilityWindowTitle()).isEqualTo(getContext().getResources().getString(
- com.android.internal.R.string.android_system_label));
- }
-
- @Test
- public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(0.9f, Float.NaN,
- Float.NaN);
- });
-
- assertThat(mWindowMagnificationController.getScale()).isEqualTo(Float.NaN);
- }
-
- @Test
- public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
- // the config orientation should not be undefined, since it would cause config.diff
- // returning 0 and thus the orientation changed would not be detected
- assertThat(mResources.getConfiguration().orientation).isNotEqualTo(ORIENTATION_UNDEFINED);
-
- final Configuration config = mResources.getConfiguration();
- config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
- : ORIENTATION_LANDSCAPE;
- final int newRotation = simulateRotateTheDevice();
-
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
- }
-
- @Test
- public void enableWindowMagnification_registerComponentCallback() {
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN,
- Float.NaN));
-
- verify(mContext).registerComponentCallbacks(mWindowMagnificationController);
- }
-
- @Test
- public void onLocaleChanged_enabled_updateA11yWindowTitle() {
- final String newA11yWindowTitle = "new a11y window title";
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
- final TestableResources testableResources = getContext().getOrCreateTestableResources();
- testableResources.addOverride(com.android.internal.R.string.android_system_label,
- newA11yWindowTitle);
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
- });
-
- assertThat(getAccessibilityWindowTitle()).isEqualTo(newA11yWindowTitle);
- }
-
- @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
- @Test
- public void onSingleTap_enabled_scaleAnimates() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.onSingleTap(mSpyView);
- });
-
- final View mirrorView = mSurfaceControlViewHost.getView();
-
- final AtomicDouble maxScaleX = new AtomicDouble();
- advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
- // For some reason the fancy way doesn't compile...
- // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
- final double oldMax = maxScaleX.get();
- final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
- assertThat(maxScaleX.compareAndSet(oldMax, newMax)).isTrue();
- });
-
- assertThat(maxScaleX.get()).isGreaterThan(1.0);
- }
-
- @Test
- public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
-
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
- });
-
- ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
- }
-
- @Test
- public void moveWindowMagnificationToRightEdge_dragHandleMovesToLeftAndUpdatesTapExcludeRegion()
- throws RemoteException {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
- // Verifying two times in: (1) enable window magnification (2) reposition drag handle
- verify(viewRoot, times(2)).setTouchableRegion(any());
-
- View dragButton = getInternalView(R.id.drag_handle);
- FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
- assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.LEFT);
- }
-
- @Test
- public void moveWindowMagnificationToLeftEdge_dragHandleMovesToRightAndUpdatesTapExcludeRegion()
- throws RemoteException {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- setSystemGestureInsets();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0);
- });
- // Wait for Region updated.
- waitForIdleSync();
-
- AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl();
- // Verifying one times in: (1) enable window magnification
- verify(viewRoot).setTouchableRegion(any());
-
- View dragButton = getInternalView(R.id.drag_handle);
- FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
- assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.RIGHT);
- }
-
- @Test
- public void setMinimumWindowSize_enabled_expectedWindowSize() {
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int expectedWindowHeight = minimumWindowSize;
- final int expectedWindowWidth = minimumWindowSize;
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
-
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
- assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
- }
-
- @Test
- public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final int expectedWindowHeight = minimumWindowSize;
- final int expectedWindowWidth = minimumWindowSize;
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
- mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
- assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
- }
-
- @Test
- public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
- minimumWindowSize - 10);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(minimumWindowSize);
- assertThat(actualWindowWidth.get()).isEqualTo(minimumWindowSize);
- }
-
- @Test
- public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
- actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(bounds.height());
- assertThat(actualWindowWidth.get()).isEqualTo(bounds.width());
- }
-
- @Test
- public void changeMagnificationSize_expectedWindowSize() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
- final float magnificationScaleLarge = 2.5f;
- final int initSize = Math.min(bounds.width(), bounds.height()) / 3;
- final int magnificationSize = (int) (initSize * magnificationScaleLarge)
- - (int) (initSize * magnificationScaleLarge) % 2;
-
- final int expectedWindowHeight = magnificationSize;
- final int expectedWindowWidth = magnificationSize;
-
- mInstrumentation.runOnMainSync(
- () ->
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.changeMagnificationSize(
- WindowMagnificationSettings.MagnificationSize.LARGE);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
- assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
- }
-
- @Test
- public void editModeOnDragCorner_resizesWindow() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
- final int startingSize = (int) (bounds.width() / 2);
-
- mInstrumentation.runOnMainSync(
- () ->
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- });
-
- waitForIdleSync();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController
- .onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
-
- assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
- assertThat(actualWindowWidth.get()).isEqualTo(startingSize + 2);
- }
-
- @Test
- public void editModeOnDragEdge_resizesWindowInOnlyOneDirection() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
- final int startingSize = (int) (bounds.width() / 2f);
-
- mInstrumentation.runOnMainSync(
- () ->
- mWindowMagnificationController.updateWindowMagnificationInternal(
- Float.NaN, Float.NaN, Float.NaN));
-
- final AtomicInteger actualWindowHeight = new AtomicInteger();
- final AtomicInteger actualWindowWidth = new AtomicInteger();
-
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.setWindowSize(startingSize, startingSize);
- mWindowMagnificationController.setEditMagnifierSizeMode(true);
- mWindowMagnificationController
- .onDrag(getInternalView(R.id.bottom_handle), 2f, 1f);
- actualWindowHeight.set(
- mSurfaceControlViewHost.getView().getLayoutParams().height);
- actualWindowWidth.set(
- mSurfaceControlViewHost.getView().getLayoutParams().width);
- });
- assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
- assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
- }
-
- @Test
- public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
-
- final int minimumWindowSize = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
- Float.NaN, Float.NaN));
-
- final AtomicInteger magnificationCenterX = new AtomicInteger();
- final AtomicInteger magnificationCenterY = new AtomicInteger();
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
- minimumWindowSize, bounds.right, bounds.bottom);
- magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
- magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
- });
-
- assertThat(magnificationCenterX.get()).isLessThan(bounds.right);
- assertThat(magnificationCenterY.get()).isLessThan(bounds.bottom);
- }
-
- @Test
- public void performSingleTap_DragHandle() {
- final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- mInstrumentation.runOnMainSync(
- () -> {
- mWindowMagnificationController.updateWindowMagnificationInternal(
- 1.5f, bounds.centerX(), bounds.centerY());
- });
- View dragButton = getInternalView(R.id.drag_handle);
-
- // Perform a single-tap
- final long downTime = SystemClock.uptimeMillis();
- dragButton.dispatchTouchEvent(
- obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100));
- dragButton.dispatchTouchEvent(
- obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
-
- verify(mSurfaceControlViewHost).setView(any(View.class), any());
- }
-
- private <T extends View> T getInternalView(@IdRes int idRes) {
- View mirrorView = mSurfaceControlViewHost.getView();
- T view = mirrorView.findViewById(idRes);
- assertThat(view).isNotNull();
- return view;
- }
-
- private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
- float y) {
- return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
- }
-
- private String getAccessibilityWindowTitle() {
- final View mirrorView = mSurfaceControlViewHost.getView();
- if (mirrorView == null) {
- return null;
- }
- WindowManager.LayoutParams layoutParams =
- (WindowManager.LayoutParams) mirrorView.getLayoutParams();
- return layoutParams.accessibilityTitle.toString();
- }
-
- private boolean hasMagnificationOverlapFlag() {
- return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
- }
-
- private void setSystemGestureInsets() {
- final WindowInsets testInsets = new WindowInsets.Builder()
- .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
- .build();
- mWindowManager.setWindowInsets(testInsets);
- }
-
- private int updateMirrorSurfaceMarginDimension() {
- return mContext.getResources().getDimensionPixelSize(
- R.dimen.magnification_mirror_surface_margin);
- }
-
- @Surface.Rotation
- private int simulateRotateTheDevice() {
- final Display display = Mockito.spy(mContext.getDisplay());
- final int currentRotation = display.getRotation();
- final int newRotation = (currentRotation + 1) % 4;
- when(display.getRotation()).thenReturn(newRotation);
- when(mContext.getDisplay()).thenReturn(display);
- return newRotation;
- }
-
- // advance time based on the device frame refresh rate
- private void advanceTimeBy(long timeDelta) {
- advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null);
- }
-
- // advance time based on the device frame refresh rate, and trigger runnable on each refresh
- private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) {
- final float frameRate = mContext.getDisplay().getRefreshRate();
- final int timeSlot = (int) (1000 / frameRate);
- int round = (int) Math.ceil((double) timeDelta / timeSlot);
- for (; round >= 0; round--) {
- mInstrumentation.runOnMainSync(() -> {
- mAnimatorTestRule.advanceTimeBy(timeSlot);
- if (runnableOnEachRefresh != null) {
- runnableOnEachRefresh.run();
- }
- });
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 071acfa44650..a1f59c2cc2b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -20,18 +20,20 @@ import android.animation.AnimatorRuleRecordingSpec
import android.animation.AnimatorTestRuleToolkit
import android.animation.MotionControl
import android.animation.recordMotion
+import android.graphics.Color
+import android.graphics.PointF
import android.graphics.drawable.GradientDrawable
import android.platform.test.annotations.MotionTest
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.runOnMainThreadAndWaitForIdleSync
import kotlin.test.assertTrue
import org.junit.Rule
import org.junit.Test
@@ -47,13 +49,25 @@ import platform.test.screenshot.PathConfig
@SmallTest
@MotionTest
@RunWith(ParameterizedAndroidJunit4::class)
-class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
+class TransitionAnimatorTest(
+ private val fadeWindowBackgroundLayer: Boolean,
+ private val isLaunching: Boolean,
+ private val useSpring: Boolean,
+) : SysuiTestCase() {
companion object {
private const val GOLDENS_PATH = "frameworks/base/packages/SystemUI/tests/goldens"
- @get:Parameters(name = "{0}")
+ @get:Parameters(name = "fadeBackground={0}, isLaunching={1}, useSpring={2}")
@JvmStatic
- val useSpringValues = booleanArrayOf(false, true).toList()
+ val parameterValues = buildList {
+ booleanArrayOf(true, false).forEach { fadeBackground ->
+ booleanArrayOf(true, false).forEach { isLaunching ->
+ booleanArrayOf(true, false).forEach { useSpring ->
+ add(arrayOf(fadeBackground, isLaunching, useSpring))
+ }
+ }
+ }
+ }
}
private val kosmos = Kosmos()
@@ -66,127 +80,104 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
ActivityTransitionAnimator.SPRING_TIMINGS,
ActivityTransitionAnimator.SPRING_INTERPOLATORS,
)
- private val withSpring =
+ private val fade =
+ if (fadeWindowBackgroundLayer) {
+ "withFade"
+ } else {
+ "withoutFade"
+ }
+ private val direction =
+ if (isLaunching) {
+ "whenLaunching"
+ } else {
+ "whenReturning"
+ }
+ private val mode =
if (useSpring) {
- "_withSpring"
+ "withSpring"
} else {
- ""
+ "withAnimator"
}
@get:Rule(order = 1) val activityRule = ActivityScenarioRule(EmptyTestActivity::class.java)
@get:Rule(order = 2) val animatorTestRule = android.animation.AnimatorTestRule(this)
@get:Rule(order = 3)
val motionRule =
- MotionTestRule(AnimatorTestRuleToolkit(animatorTestRule, kosmos.testScope), pathManager)
-
- @Test
- fun backgroundAnimation_whenLaunching() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = true).apply {
- getInstrumentation().runOnMainSync { start() }
- }
-
- val recordedMotion = recordMotion(backgroundLayer, animator)
-
- motionRule
- .assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimation_whenLaunching$withSpring")
- }
-
- @Test
- fun backgroundAnimation_whenReturning() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = false).apply {
- getInstrumentation().runOnMainSync { start() }
- }
-
- val recordedMotion = recordMotion(backgroundLayer, animator)
-
- motionRule
- .assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimation_whenReturning$withSpring")
- }
-
- @Test
- fun backgroundAnimationWithoutFade_whenLaunching() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = true, fadeWindowBackgroundLayer = false)
- .apply { getInstrumentation().runOnMainSync { start() } }
-
- val recordedMotion = recordMotion(backgroundLayer, animator)
-
- motionRule
- .assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenLaunching$withSpring")
- }
+ MotionTestRule(
+ AnimatorTestRuleToolkit(animatorTestRule, kosmos.testScope) { activityRule.scenario },
+ pathManager,
+ )
@Test
- fun backgroundAnimationWithoutFade_whenReturning() {
- val backgroundLayer = GradientDrawable().apply { alpha = 0 }
- val animator =
- setUpTest(backgroundLayer, isLaunching = false, fadeWindowBackgroundLayer = false)
- .apply { getInstrumentation().runOnMainSync { start() } }
+ fun backgroundAnimationTimeSeries() {
+ val transitionContainer = createScene()
+ val backgroundLayer = createBackgroundLayer()
+ val animation = createAnimation(transitionContainer, backgroundLayer)
- val recordedMotion = recordMotion(backgroundLayer, animator)
+ val recordedMotion = record(backgroundLayer, animation)
motionRule
.assertThat(recordedMotion)
- .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenReturning$withSpring")
+ .timeSeriesMatchesGolden("backgroundAnimationTimeSeries_${fade}_${direction}_$mode")
}
- private fun setUpTest(
- backgroundLayer: GradientDrawable,
- isLaunching: Boolean,
- fadeWindowBackgroundLayer: Boolean = true,
- ): TransitionAnimator.Animation {
+ private fun createScene(): ViewGroup {
lateinit var transitionContainer: ViewGroup
activityRule.scenario.onActivity { activity ->
- transitionContainer = FrameLayout(activity).apply { setBackgroundColor(0x00FF00) }
+ transitionContainer = FrameLayout(activity)
activity.setContentView(transitionContainer)
}
waitForIdleSync()
+ return transitionContainer
+ }
+ private fun createBackgroundLayer() =
+ GradientDrawable().apply {
+ setColor(Color.BLACK)
+ alpha = 0
+ }
+
+ private fun createAnimation(
+ transitionContainer: ViewGroup,
+ backgroundLayer: GradientDrawable,
+ ): TransitionAnimator.Animation {
val controller = TestController(transitionContainer, isLaunching)
- return transitionAnimator.createAnimation(
- controller,
- controller.createAnimatorState(),
- createEndState(transitionContainer),
- backgroundLayer,
- fadeWindowBackgroundLayer,
- useSpring = useSpring,
- )
- }
- private fun createEndState(container: ViewGroup): TransitionAnimator.State {
val containerLocation = IntArray(2)
- container.getLocationOnScreen(containerLocation)
- return TransitionAnimator.State(
- left = containerLocation[0],
- top = containerLocation[1],
- right = containerLocation[0] + 320,
- bottom = containerLocation[1] + 690,
- topCornerRadius = 0f,
- bottomCornerRadius = 0f,
- )
+ transitionContainer.getLocationOnScreen(containerLocation)
+ val endState =
+ TransitionAnimator.State(
+ left = containerLocation[0],
+ top = containerLocation[1],
+ right = containerLocation[0] + 320,
+ bottom = containerLocation[1] + 690,
+ topCornerRadius = 0f,
+ bottomCornerRadius = 0f,
+ )
+
+ val startVelocity =
+ if (useSpring) {
+ PointF(2500f, 30000f)
+ } else {
+ null
+ }
+
+ return transitionAnimator
+ .createAnimation(
+ controller,
+ controller.createAnimatorState(),
+ endState,
+ backgroundLayer,
+ fadeWindowBackgroundLayer,
+ startVelocity = startVelocity,
+ )
+ .apply { runOnMainThreadAndWaitForIdleSync { start() } }
}
- private fun recordMotion(
+ private fun record(
backgroundLayer: GradientDrawable,
animation: TransitionAnimator.Animation,
): RecordedMotion {
- fun record(motionControl: MotionControl, sampleIntervalMs: Long): RecordedMotion {
- return motionRule.recordMotion(
- AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) {
- feature(DrawableFeatureCaptures.bounds, "bounds")
- feature(DrawableFeatureCaptures.cornerRadii, "corner_radii")
- feature(DrawableFeatureCaptures.alpha, "alpha")
- }
- )
- }
-
val motionControl: MotionControl
val sampleIntervalMs: Long
if (useSpring) {
@@ -201,9 +192,13 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
sampleIntervalMs = 20L
}
- var recording: RecordedMotion? = null
- getInstrumentation().runOnMainSync { recording = record(motionControl, sampleIntervalMs) }
- return recording!!
+ return motionRule.recordMotion(
+ AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) {
+ feature(DrawableFeatureCaptures.bounds, "bounds")
+ feature(DrawableFeatureCaptures.cornerRadii, "corner_radii")
+ feature(DrawableFeatureCaptures.alpha, "alpha")
+ }
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index e1b8a1d9971c..91f9cce5b69b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -212,6 +212,20 @@ open class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testDimissOnLock() {
+ val container = initializeFingerprintContainer(addToView = true)
+ assertThat(container.parent).isNotNull()
+ val root = container.rootView
+
+ // Simulate sleep/lock invocation
+ container.onStartedGoingToSleep()
+ waitForIdleSync()
+
+ assertThat(container.parent).isNull()
+ assertThat(root.isAttachedToWindow).isFalse()
+ }
+
+ @Test
fun testCredentialPasswordDismissesOnBack() {
val container = initializeCredentialPasswordContainer(addToView = true)
assertThat(container.parent).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
index 9ace8e981077..387cc084f9cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
@@ -42,6 +42,7 @@ import com.android.systemui.motion.createSysUiComposeMotionTestRule
import com.android.systemui.res.R
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.testKosmos
+import kotlin.time.Duration.Companion.seconds
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -109,7 +110,7 @@ class BouncerContentTest : SysuiTestCase() {
@Test
fun doubleClick_swapSide() =
- motionTestRule.runTest {
+ motionTestRule.runTest(timeout = 30.seconds) {
val motion =
recordMotion(
content = { BouncerContentUnderTest() },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
index 088bb02512b5..768f1dc8b78c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.haptics.msdl.bouncerHapticPlayer
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.motion.createSysUiComposeMotionTestRule
import com.android.systemui.testKosmos
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.takeWhile
@@ -71,7 +72,7 @@ class PatternBouncerTest : SysuiTestCase() {
@Test
fun entryAnimation() =
- motionTestRule.runTest {
+ motionTestRule.runTest(timeout = 30.seconds) {
val motion =
recordMotion(
content = { play -> if (play) PatternBouncerUnderTest() },
@@ -89,7 +90,7 @@ class PatternBouncerTest : SysuiTestCase() {
@Test
fun animateFailure() =
- motionTestRule.runTest {
+ motionTestRule.runTest(timeout = 30.seconds) {
val failureAnimationMotionControl =
MotionControl(
delayReadyToPlay = {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 6061063db903..562481567536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -20,6 +20,7 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.systemui.Flags.FLAG_CLIPBOARD_SHARED_TRANSITIONS;
import static com.android.systemui.Flags.FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE;
+import static com.android.systemui.Flags.FLAG_SHOW_CLIPBOARD_INDICATION;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED;
@@ -121,6 +122,24 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private class FakeClipboardIndicationProvider implements ClipboardIndicationProvider {
+ private ClipboardIndicationCallback mIndicationCallback;
+
+ public void notifyIndicationTextChanged(CharSequence indicationText) {
+ if (mIndicationCallback != null) {
+ mIndicationCallback.onIndicationTextChanged(indicationText);
+ }
+ }
+
+ @Override
+ public void getIndicationText(ClipboardIndicationCallback callback) {
+ mIndicationCallback = callback;
+ }
+ }
+
+ private FakeClipboardIndicationProvider mClipboardIndicationProvider =
+ new FakeClipboardIndicationProvider();
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -156,6 +175,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
mExecutor,
mClipboardImageLoader,
mClipboardTransitionExecutor,
+ mClipboardIndicationProvider,
mUiEventLogger);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -305,6 +325,17 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(FLAG_SHOW_CLIPBOARD_INDICATION)
+ public void test_onIndicationTextChanged_setIndicationTextCorrectly() {
+ initController();
+ mOverlayController.setClipData(mSampleClipData, "");
+
+ mClipboardIndicationProvider.notifyIndicationTextChanged("copied");
+
+ verify(mClipboardOverlayView).setIndicationText("copied");
+ }
+
+ @Test
@DisableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
public void test_viewCallbacks_onShareTapped_sharedTransitionsOff() {
initController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 96f4a60271d2..b4c69529741e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -46,7 +46,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -222,7 +221,6 @@ public class DozeTriggersTest extends SysuiTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_NOTIFICATION_PULSING_FIX)
public void testOnNotification_alreadyPulsing_notificationNotSuppressed() {
// GIVEN device is pulsing
Runnable pulseSuppressListener = mock(Runnable.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index df50f765349c..24bca70fd41f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -31,7 +31,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -80,6 +79,7 @@ import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserLogoutInteractor;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.FakeGlobalSettings;
@@ -106,7 +106,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs;
@Mock private AudioManager mAudioManager;
- @Mock private DevicePolicyManager mDevicePolicyManager;
@Mock private LockPatternUtils mLockPatternUtils;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private TelephonyListenerManager mTelephonyListenerManager;
@@ -140,6 +139,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogTransitionAnimator mDialogTransitionAnimator;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock private UserLogoutInteractor mLogoutInteractor;
@Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@@ -166,7 +166,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
mAudioManager,
- mDevicePolicyManager,
mLockPatternUtils,
mBroadcastDispatcher,
mTelephonyListenerManager,
@@ -198,6 +197,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mDialogTransitionAnimator,
mSelectedUserInteractor,
+ mLogoutInteractor,
mInteractor);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index fb376ce3ca40..3ddd4b58211d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -289,6 +289,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
}
verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor))
mediaControllerFactory.setControllerForToken(session.sessionToken, controller)
+ whenever(controller.sessionToken).thenReturn(session.sessionToken)
whenever(controller.transportControls).thenReturn(transportControls)
whenever(controller.playbackInfo).thenReturn(playbackInfo)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
@@ -1599,6 +1600,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testTooManyCompactActions_isTruncated() {
// GIVEN a notification where too many compact actions were specified
@@ -1635,6 +1637,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
.isEqualTo(LegacyMediaDataManagerImpl.MAX_COMPACT_ACTIONS)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testTooManyNotificationActions_isTruncated() {
// GIVEN a notification where too many notification actions are added
@@ -1670,6 +1673,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
.isEqualTo(LegacyMediaDataManagerImpl.MAX_NOTIFICATION_ACTIONS)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_noState_usesNotification() {
val desc = "Notification Action"
@@ -1703,6 +1707,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(mediaDataCaptor.value!!.actions[0]!!.contentDescription).isEqualTo(desc)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_hasPrevNext() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
@@ -1746,6 +1751,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[1])
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
@@ -1778,6 +1784,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[3])
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_connecting() {
val stateActions = PlaybackState.ACTION_PLAY
@@ -1797,6 +1804,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
.isEqualTo(context.getString(R.string.controls_media_button_connecting))
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_reservedSpace() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
@@ -1835,6 +1843,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(actions.reservePrev).isTrue()
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_playPause_hasButton() {
val stateActions = PlaybackState.ACTION_PLAY_PAUSE
@@ -1998,6 +2007,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(mediaDataCaptor.value.semanticActions).isNotNull()
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackStateNull_Pause_keyExists_callsListener() {
whenever(controller.playbackState).thenReturn(null)
@@ -2056,6 +2066,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
assertThat(mediaDataCaptor.value.isClearable).isFalse()
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testRetain_notifPlayer_notifRemoved_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
@@ -2086,6 +2097,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
@@ -2104,6 +2116,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 7d364bd832f2..e5483c0980c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -103,7 +103,6 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -113,6 +112,7 @@ import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.capture
import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
@@ -312,6 +312,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
}
verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor))
mediaControllerFactory.setControllerForToken(session.sessionToken, controller)
+ whenever(controller.sessionToken).thenReturn(session.sessionToken)
whenever(controller.transportControls).thenReturn(transportControls)
whenever(controller.playbackInfo).thenReturn(playbackInfo)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
@@ -596,7 +597,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
fun testOnNotificationAdded_emptyTitle_hasPlaceholder() {
// When the manager has a notification with an empty title, and the app is not
// required to include a non-empty title
- val mockPackageManager = mock(PackageManager::class.java)
+ val mockPackageManager = mock<PackageManager>()
context.setMockPackageManager(mockPackageManager)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
whenever(controller.metadata)
@@ -626,7 +627,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
fun testOnNotificationAdded_blankTitle_hasPlaceholder() {
// GIVEN that the manager has a notification with a blank title, and the app is not
// required to include a non-empty title
- val mockPackageManager = mock(PackageManager::class.java)
+ val mockPackageManager = mock<PackageManager>()
context.setMockPackageManager(mockPackageManager)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
whenever(controller.metadata)
@@ -656,7 +657,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() {
// When the app sets the metadata title fields to empty strings, but does include a
// non-blank notification title
- val mockPackageManager = mock(PackageManager::class.java)
+ val mockPackageManager = mock<PackageManager>()
context.setMockPackageManager(mockPackageManager)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
whenever(controller.metadata)
@@ -1610,6 +1611,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testTooManyCompactActions_isTruncated() {
// GIVEN a notification where too many compact actions were specified
@@ -1646,6 +1648,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
.isEqualTo(MediaDataProcessor.MAX_COMPACT_ACTIONS)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testTooManyNotificationActions_isTruncated() {
// GIVEN a notification where too many notification actions are added
@@ -1681,6 +1684,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
.isEqualTo(MediaDataProcessor.MAX_NOTIFICATION_ACTIONS)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_noState_usesNotification() {
val desc = "Notification Action"
@@ -1714,6 +1718,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(mediaDataCaptor.value!!.actions[0]!!.contentDescription).isEqualTo(desc)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_hasPrevNext() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
@@ -1757,6 +1762,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[1])
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
@@ -1789,6 +1795,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[3])
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_connecting() {
val stateActions = PlaybackState.ACTION_PLAY
@@ -1874,6 +1881,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
.isNotEqualTo(firstSemanticActions.prevOrCustom?.icon)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_reservedSpace() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
@@ -1912,6 +1920,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(actions.reservePrev).isTrue()
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackActions_playPause_hasButton() {
val stateActions = PlaybackState.ACTION_PLAY_PAUSE
@@ -2074,6 +2083,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(mediaDataCaptor.value.semanticActions).isNotNull()
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testPlaybackStateNull_Pause_keyExists_callsListener() {
whenever(controller.playbackState).thenReturn(null)
@@ -2132,6 +2142,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(mediaDataCaptor.value.isClearable).isFalse()
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testRetain_notifPlayer_notifRemoved_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
@@ -2162,6 +2173,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
@@ -2180,6 +2192,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3)
@Test
fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index b3bd7d15cbf3..c7beb158c2de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -23,7 +23,6 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityManager
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewUiEventLogger
@@ -43,7 +42,6 @@ class FakeMediaTttChipControllerReceiver(
dumpManager: DumpManager,
powerManager: PowerManager,
mainHandler: Handler,
- mediaTttFlags: MediaTttFlags,
uiEventLogger: MediaTttReceiverUiEventLogger,
viewUtil: ViewUtil,
wakeLockBuilder: WakeLock.Builder,
@@ -62,7 +60,6 @@ class FakeMediaTttChipControllerReceiver(
dumpManager,
powerManager,
mainHandler,
- mediaTttFlags,
uiEventLogger,
viewUtil,
wakeLockBuilder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 9afa5ad1dd43..378dd452d030 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -37,7 +37,6 @@ import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -55,7 +54,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -66,32 +64,18 @@ import org.mockito.MockitoAnnotations
class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private lateinit var controllerReceiver: MediaTttChipControllerReceiver
- @Mock
- private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var applicationInfo: ApplicationInfo
- @Mock
- private lateinit var logger: MediaTttReceiverLogger
- @Mock
- private lateinit var accessibilityManager: AccessibilityManager
- @Mock
- private lateinit var configurationController: ConfigurationController
- @Mock
- private lateinit var dumpManager: DumpManager
- @Mock
- private lateinit var mediaTttFlags: MediaTttFlags
- @Mock
- private lateinit var powerManager: PowerManager
- @Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
- private lateinit var windowManager: WindowManager
- @Mock
- private lateinit var commandQueue: CommandQueue
- @Mock
- private lateinit var rippleController: MediaTttReceiverRippleController
- @Mock
- private lateinit var lazyViewCapture: Lazy<ViewCapture>
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+ @Mock private lateinit var logger: MediaTttReceiverLogger
+ @Mock private lateinit var accessibilityManager: AccessibilityManager
+ @Mock private lateinit var configurationController: ConfigurationController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var powerManager: PowerManager
+ @Mock private lateinit var viewUtil: ViewUtil
+ @Mock private lateinit var windowManager: WindowManager
+ @Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var rippleController: MediaTttReceiverRippleController
+ @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -106,14 +90,17 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
- whenever(packageManager.getApplicationInfo(
- eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
- )).thenReturn(applicationInfo)
+ whenever(
+ packageManager.getApplicationInfo(
+ eq(PACKAGE_NAME),
+ any<PackageManager.ApplicationInfoFlags>(),
+ )
+ )
+ .thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
fakeClock = FakeSystemClock()
@@ -127,27 +114,31 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
- viewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(windowManager,
- lazyViewCapture, isViewCaptureEnabled = false)
- controllerReceiver = FakeMediaTttChipControllerReceiver(
- commandQueue,
- context,
- logger,
- viewCaptureAwareWindowManager,
- fakeExecutor,
- accessibilityManager,
- configurationController,
- dumpManager,
- powerManager,
- Handler.getMain(),
- mediaTttFlags,
- receiverUiEventLogger,
- viewUtil,
- fakeWakeLockBuilder,
- fakeClock,
- rippleController,
- temporaryViewUiEventLogger,
- )
+ viewCaptureAwareWindowManager =
+ ViewCaptureAwareWindowManager(
+ windowManager,
+ lazyViewCapture,
+ isViewCaptureEnabled = false,
+ )
+ controllerReceiver =
+ FakeMediaTttChipControllerReceiver(
+ commandQueue,
+ context,
+ logger,
+ viewCaptureAwareWindowManager,
+ fakeExecutor,
+ accessibilityManager,
+ configurationController,
+ dumpManager,
+ powerManager,
+ Handler.getMain(),
+ receiverUiEventLogger,
+ viewUtil,
+ fakeWakeLockBuilder,
+ fakeClock,
+ rippleController,
+ temporaryViewUiEventLogger,
+ )
controllerReceiver.start()
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
@@ -156,48 +147,18 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
}
@Test
- fun commandQueueCallback_flagOff_noCallbackAdded() {
- reset(commandQueue)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(false)
-
- controllerReceiver = MediaTttChipControllerReceiver(
- commandQueue,
- context,
- logger,
- viewCaptureAwareWindowManager,
- FakeExecutor(FakeSystemClock()),
- accessibilityManager,
- configurationController,
- dumpManager,
- powerManager,
- Handler.getMain(),
- mediaTttFlags,
- receiverUiEventLogger,
- viewUtil,
- fakeWakeLockBuilder,
- fakeClock,
- rippleController,
- temporaryViewUiEventLogger,
- )
- controllerReceiver.start()
-
- verify(commandQueue, never()).addCallback(any())
- }
-
- @Test
fun commandQueueCallback_closeToSender_triggersChip() {
val appName = "FakeAppName"
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
/* appIcon= */ null,
- appName
+ appName,
)
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
- MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER.id
- )
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER.id)
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@@ -207,45 +168,44 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
routeInfo,
null,
- null
+ null,
)
verify(windowManager, never()).addView(any(), any())
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
- MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER.id
- )
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER.id)
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@Test
fun commandQueueCallback_transferToReceiverSucceeded_noChipShown() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null,
+ null,
)
verify(windowManager, never()).addView(any(), any())
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(
MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_SUCCEEDED.id
- )
+ )
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@Test
fun commandQueueCallback_transferToReceiverFailed_noChipShown() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ routeInfo,
+ null,
+ null,
)
verify(windowManager, never()).addView(any(), any())
- assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(
- MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED.id
- )
+ assertThat(uiEventLoggerFake.eventId(0))
+ .isEqualTo(MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_TRANSFER_TO_RECEIVER_FAILED.id)
assertThat(uiEventLoggerFake.logs[0].instanceId).isNotNull()
}
@@ -255,14 +215,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
routeInfo,
null,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -276,14 +236,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
null,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -297,14 +257,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
null,
- null
+ null,
)
assertThat(uiEventLoggerFake[0].instanceId).isEqualTo(uiEventLoggerFake[1].instanceId)
@@ -316,14 +276,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_TRANSFER_TO_RECEIVER_FAILED,
routeInfo,
null,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -334,19 +294,19 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun commandQueueCallback_closeThenFar_wakeLockAcquiredThenReleased() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo,
+ null,
+ null,
)
assertThat(fakeWakeLock.isHeld).isTrue()
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -355,10 +315,10 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun commandQueueCallback_closeThenFar_wakeLockNeverAcquired() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
- StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
- routeInfo,
- null,
- null
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -370,7 +330,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
routeInfo,
null,
- null
+ null,
)
verify(logger).logStateChange(any(), any(), any())
@@ -391,10 +351,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
val view = getChipView()
assertThat(view.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(view.getAppIconView().contentDescription)
- .isEqualTo(context.getString(
- R.string.media_transfer_receiver_content_description_with_app_name,
- APP_NAME,
- ))
+ .isEqualTo(
+ context.getString(
+ R.string.media_transfer_receiver_content_description_with_app_name,
+ APP_NAME,
+ )
+ )
}
@Test
@@ -463,7 +425,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
null,
- APP_NAME
+ APP_NAME,
)
verify(windowManager, never()).addView(any(), any())
@@ -476,10 +438,11 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
}
private fun getChipReceiverInfo(packageName: String?): ChipReceiverInfo {
- val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
- .addFeature("feature")
- .setClientPackageName(packageName)
- .build()
+ val routeInfo =
+ MediaRoute2Info.Builder("id", "Test route name")
+ .addFeature("feature")
+ .setClientPackageName(packageName)
+ .build()
return ChipReceiverInfo(
routeInfo,
null,
@@ -495,7 +458,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private const val APP_NAME = "Fake app name"
private const val PACKAGE_NAME = "com.android.systemui"
-private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
- .addFeature("feature")
- .setClientPackageName(PACKAGE_NAME)
- .build()
+private val routeInfo =
+ MediaRoute2Info.Builder("id", "Test route name")
+ .addFeature("feature")
+ .setClientPackageName(PACKAGE_NAME)
+ .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index b4cad6bb057b..c90ac5993c31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -41,7 +41,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
@@ -95,7 +94,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var chipbarLogger: ChipbarLogger
@Mock private lateinit var logger: MediaTttSenderLogger
- @Mock private lateinit var mediaTttFlags: MediaTttFlags
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var powerManager: PowerManager
@Mock private lateinit var viewUtil: ViewUtil
@@ -118,7 +116,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
@@ -127,7 +124,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
whenever(
packageManager.getApplicationInfo(
eq(PACKAGE_NAME),
- any<PackageManager.ApplicationInfoFlags>()
+ any<PackageManager.ApplicationInfoFlags>(),
)
)
.thenReturn(applicationInfo)
@@ -148,8 +145,11 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
ChipbarCoordinator(
context,
chipbarLogger,
- ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
- isViewCaptureEnabled = false),
+ ViewCaptureAwareWindowManager(
+ windowManager,
+ lazyViewCapture,
+ isViewCaptureEnabled = false,
+ ),
fakeExecutor,
accessibilityManager,
configurationController,
@@ -174,7 +174,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -183,30 +182,11 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
}
@Test
- fun commandQueueCallback_flagOff_noCallbackAdded() {
- reset(commandQueue)
- whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(false)
- underTest =
- MediaTttSenderCoordinator(
- chipbarCoordinator,
- commandQueue,
- context,
- dumpManager,
- logger,
- mediaTttFlags,
- uiEventLogger,
- )
- underTest.start()
-
- verify(commandQueue, never()).addCallback(any())
- }
-
- @Test
fun commandQueueCallback_almostCloseToStartCast_triggersCorrectChip() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -220,13 +200,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -249,7 +223,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -263,13 +237,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -277,7 +245,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -291,13 +259,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -320,7 +282,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -334,13 +296,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(0))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -350,7 +306,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -364,13 +320,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED.id)
verify(vibratorHelper, never())
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -380,7 +330,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
// Event index 2 since initially displaying the triggered chip would also log two events.
@@ -397,7 +347,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- /* undoCallback= */ null
+ /* undoCallback= */ null,
)
val chipbarView = getChipbarView()
@@ -452,7 +402,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -466,13 +416,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED.id)
verify(vibratorHelper, never())
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -481,7 +425,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- /* undoCallback= */ null
+ /* undoCallback= */ null,
)
val chipbarView = getChipbarView()
@@ -538,7 +482,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -553,13 +497,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -567,13 +505,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
reset(vibratorHelper)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
routeInfo,
- null
+ null,
)
val chipbarView = getChipbarView()
@@ -588,13 +526,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.eventId(2))
.isEqualTo(MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED.id)
verify(vibratorHelper)
- .vibrate(
- any(),
- any(),
- any<VibrationEffect>(),
- any(),
- any<VibrationAttributes>(),
- )
+ .vibrate(any(), any(), any<VibrationEffect>(), any(), any<VibrationAttributes>())
}
@Test
@@ -602,7 +534,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
verify(windowManager, never()).addView(any(), any())
@@ -615,13 +547,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -635,7 +567,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
assertThat(fakeWakeLock.isHeld).isTrue()
@@ -643,7 +575,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -654,7 +586,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
assertThat(fakeWakeLock.isHeld).isFalse()
@@ -672,7 +604,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -680,7 +612,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -692,7 +624,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -700,7 +632,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -713,14 +645,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
reset(windowManager)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -733,14 +665,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
reset(windowManager)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -752,7 +684,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -760,7 +692,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -772,7 +704,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -780,7 +712,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -792,7 +724,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -800,7 +732,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -812,7 +744,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
routeInfo,
- null
+ null,
)
verify(windowManager).addView(any(), any())
reset(windowManager)
@@ -820,7 +752,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
routeInfo,
- null
+ null,
)
verify(logger).logInvalidStateTransitionError(any(), any())
@@ -925,7 +857,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
routeInfo,
- null
+ null,
)
verify(logger).logStateChange(any(), any(), any())
@@ -936,13 +868,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -959,13 +891,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -983,13 +915,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1007,13 +939,13 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
- null
+ null,
)
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1051,7 +983,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1091,7 +1023,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
fakeExecutor.runAllReady()
@@ -1116,7 +1048,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1144,7 +1075,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1178,7 +1108,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1211,7 +1140,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1230,7 +1158,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
routeInfo,
- null
+ null,
)
// THEN the media coordinator unregisters the listener
@@ -1248,7 +1176,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
context,
dumpManager,
logger,
- mediaTttFlags,
uiEventLogger,
)
underTest.start()
@@ -1549,7 +1476,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
private fun ChipStateSender.getExpectedStateText(
- otherDeviceName: String = OTHER_DEVICE_NAME,
+ otherDeviceName: String = OTHER_DEVICE_NAME
): String? {
return this.getChipTextString(context, otherDeviceName).loadText(context)
}
@@ -1560,7 +1487,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null
+ null,
)
}
@@ -1570,7 +1497,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null
+ null,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 8a6df1cbb4de..d88d69da5e59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -63,6 +63,7 @@ class DragAndDropTest : SysuiTestCase() {
listState = listState,
otherTiles = listOf(),
columns = 4,
+ largeTilesSpan = 4,
modifier = Modifier.fillMaxSize(),
onRemoveTile = {},
onSetTiles = onSetTiles,
@@ -75,7 +76,7 @@ class DragAndDropTest : SysuiTestCase() {
@Test
fun draggedTile_shouldDisappear() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) {
tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
@@ -101,7 +102,7 @@ class DragAndDropTest : SysuiTestCase() {
@Test
fun draggedTile_shouldChangePosition() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) {
tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
@@ -128,7 +129,7 @@ class DragAndDropTest : SysuiTestCase() {
@Test
fun draggedTileOut_shouldBeRemoved() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) {
tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
@@ -153,7 +154,7 @@ class DragAndDropTest : SysuiTestCase() {
@Test
fun draggedNewTileIn_shouldBeAdded() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) {
tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index d9c1d998798c..fac5ecb49027 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -62,6 +62,7 @@ class ResizingTest : SysuiTestCase() {
listState = listState,
otherTiles = listOf(),
columns = 4,
+ largeTilesSpan = 4,
modifier = Modifier.fillMaxSize(),
onRemoveTile = {},
onSetTiles = {},
@@ -74,7 +75,7 @@ class ResizingTest : SysuiTestCase() {
@Test
fun toggleIconTile_shouldBeLarge() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
}
@@ -90,7 +91,7 @@ class ResizingTest : SysuiTestCase() {
@Test
fun toggleLargeTile_shouldBeIcon() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
}
@@ -106,7 +107,7 @@ class ResizingTest : SysuiTestCase() {
@Test
fun resizedLarge_shouldBeIcon() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
}
@@ -126,7 +127,7 @@ class ResizingTest : SysuiTestCase() {
@Test
fun resizedIcon_shouldBeLarge() {
var tiles by mutableStateOf(TestEditTiles)
- val listState = EditTileListState(tiles, 4)
+ val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
composeRule.setContent {
EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 3bfde68def50..909680866d20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -59,6 +59,7 @@ import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
+import com.android.wm.shell.back.BackAnimation
import com.android.wm.shell.sysui.ShellInterface
import com.google.common.util.concurrent.MoreExecutors
import java.util.Optional
@@ -120,6 +121,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
private lateinit var unfoldTransitionProgressForwarder:
Optional<UnfoldTransitionProgressForwarder>
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var backAnimation: Optional<BackAnimation>
@Before
fun setUp() {
@@ -289,6 +291,7 @@ class OverviewProxyServiceTest : SysuiTestCase() {
dumpManager,
unfoldTransitionProgressForwarder,
broadcastDispatcher,
+ backAnimation,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 963973588236..991f78a3147c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -27,7 +27,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.SessionCreationSource
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
@@ -56,7 +55,6 @@ import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -150,8 +148,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
@Test
fun screenCaptureDisabledDialog_isShown_whenFunctionalityIsDisabled() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
- .thenReturn(true)
whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
.thenReturn(true)
@@ -170,48 +166,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() {
}
@Test
- fun screenCapturePermissionDialog_isShown_correctly() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
- .thenReturn(false)
- whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
- .thenReturn(false)
- whenever(state.hasUserApprovedScreenRecording).thenReturn(false)
-
- val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
- screenRecordSwitch.isChecked = true
-
- bgExecutor.runAllReady()
- mainExecutor.runAllReady()
-
- verify(mediaProjectionMetricsLogger)
- .notifyProjectionInitiated(
- anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
- )
- verify(factory, times(2)).create(any(SystemUIDialog.Delegate::class.java))
- }
-
- @Test
- fun noDialogsAreShown_forScreenRecord_whenApprovalIsAlreadyGiven() {
- whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
- .thenReturn(false)
- whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
- .thenReturn(false)
-
- val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
- screenRecordSwitch.isChecked = true
-
- bgExecutor.runAllReady()
-
- verify(mediaProjectionMetricsLogger)
- .notifyProjectionInitiated(
- anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
- )
- verify(factory, never()).create()
- }
-
- @Test
fun startButton_isDisabled_beforeIssueTypeIsSelected() {
assertThat(dialog.getButton(Dialog.BUTTON_POSITIVE).isEnabled).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 6b16e78436d4..afff4858499a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -42,15 +42,11 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -79,10 +75,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private ScreenCaptureDevicePolicyResolver mDevicePolicyResolver;
@Mock
- private DialogTransitionAnimator mDialogTransitionAnimator;
- @Mock
- private ActivityStarter mActivityStarter;
- @Mock
private UserTracker mUserTracker;
@Mock
private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
@@ -92,10 +84,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private SystemUIDialog mScreenCaptureDisabledDialog;
@Mock
- private ScreenRecordDialogDelegate.Factory mScreenRecordDialogFactory;
- @Mock
- private ScreenRecordDialogDelegate mScreenRecordDialogDelegate;
- @Mock
private ScreenRecordPermissionDialogDelegate.Factory
mScreenRecordPermissionDialogDelegateFactory;
@Mock
@@ -103,7 +91,6 @@ public class RecordingControllerTest extends SysuiTestCase {
@Mock
private SystemUIDialog mScreenRecordSystemUIDialog;
- private FakeFeatureFlags mFeatureFlags;
private RecordingController mController;
private static final int USER_ID = 10;
@@ -114,12 +101,8 @@ public class RecordingControllerTest extends SysuiTestCase {
Context spiedContext = spy(mContext);
when(spiedContext.getUserId()).thenReturn(TEST_USER_ID);
- mFeatureFlags = new FakeFeatureFlags();
when(mScreenCaptureDisabledDialogDelegate.createSysUIDialog())
.thenReturn(mScreenCaptureDisabledDialog);
- when(mScreenRecordDialogFactory.create(any(), any()))
- .thenReturn(mScreenRecordDialogDelegate);
- when(mScreenRecordDialogDelegate.createDialog()).thenReturn(mScreenRecordSystemUIDialog);
when(mScreenRecordPermissionDialogDelegateFactory.create(any(), any(), anyInt(), any()))
.thenReturn(mScreenRecordPermissionDialogDelegate);
when(mScreenRecordPermissionDialogDelegate.createDialog())
@@ -127,13 +110,11 @@ public class RecordingControllerTest extends SysuiTestCase {
mController = new RecordingController(
mMainExecutor,
mBroadcastDispatcher,
- mFeatureFlags,
() -> mDevicePolicyResolver,
mUserTracker,
new RecordingControllerLogger(logcatLogBuffer("RecordingControllerTest")),
mMediaProjectionMetricsLogger,
mScreenCaptureDisabledDialogDelegate,
- mScreenRecordDialogFactory,
mScreenRecordPermissionDialogDelegateFactory
);
mController.addCallback(mCallback);
@@ -236,46 +217,19 @@ public class RecordingControllerTest extends SysuiTestCase {
}
@Test
- public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
- when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
-
- Dialog dialog =
- mController.createScreenRecordDialog(
- mContext,
- mFeatureFlags,
- mDialogTransitionAnimator,
- mActivityStarter,
- /* onStartRecordingClicked= */ null);
-
- assertThat(dialog).isSameInstanceAs(mScreenRecordSystemUIDialog);
- assertThat(mScreenRecordPermissionDialogDelegate)
- .isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
- }
-
- @Test
- public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ public void testScreenCapturingNotAllowed_returnsDevicePolicyDialog() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
- Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
- mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+ Dialog dialog = mController.createScreenRecordDialog(/* onStartRecordingClicked= */ null);
assertThat(dialog).isEqualTo(mScreenCaptureDisabledDialog);
}
@Test
- public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ public void testScreenCapturingAllowed_returnsNullDevicePolicyDialog() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
- Dialog dialog =
- mController.createScreenRecordDialog(
- mContext,
- mFeatureFlags,
- mDialogTransitionAnimator,
- mActivityStarter,
- /* onStartRecordingClicked= */ null);
+ Dialog dialog = mController.createScreenRecordDialog(/* onStartRecordingClicked= */ null);
assertThat(dialog).isSameInstanceAs(mScreenRecordSystemUIDialog);
assertThat(mScreenRecordPermissionDialogDelegate)
@@ -283,12 +237,10 @@ public class RecordingControllerTest extends SysuiTestCase {
}
@Test
- public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
- mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ public void testScreenCapturingAllowed_logsProjectionInitiated() {
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
- mController.createScreenRecordDialog(mContext, mFeatureFlags,
- mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+ mController.createScreenRecordDialog(/* onStartRecordingClicked= */ null);
verify(mMediaProjectionMetricsLogger)
.notifyProjectionInitiated(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
index 7709a65712a1..0ab4cd05fea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
@@ -21,7 +21,6 @@ import android.graphics.Insets
import android.graphics.Rect
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
@@ -76,9 +75,8 @@ class PolicyRequestProcessorTest {
/** Tests applying CaptureParameters with 'IsolatedTask' CaptureType */
@Test
- @EnableFlags(Flags.FLAG_SCREENSHOT_POLICY_SPLIT_AND_DESKTOP_MODE)
fun testProcess_newPolicy_isolatedTask() = runTest {
- val taskImage = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ val taskImage = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
/* Create a policy request processor with no capture policies */
val requestProcessor =
@@ -96,9 +94,15 @@ class PolicyRequestProcessorTest {
requestProcessor.modify(
screenshotRequest,
CaptureParameters(
- IsolatedTask(taskId = TASK_ID, taskBounds = null),
- ComponentName.unflattenFromString(FILES),
- UserHandle.of(WORK),
+ type = IsolatedTask(taskId = 100, taskBounds = Rect(0, 100, 200, 200)),
+ contentTask =
+ TaskReference(
+ taskId = 1001,
+ component = ComponentName.unflattenFromString(FILES)!!,
+ owner = UserHandle.CURRENT,
+ bounds = Rect(100, 100, 200, 200),
+ ),
+ owner = UserHandle.of(WORK),
),
)
@@ -112,14 +116,13 @@ class PolicyRequestProcessorTest {
.that(result.topComponent)
.isEqualTo(ComponentName.unflattenFromString(FILES))
- assertWithMessage("Task ID").that(result.taskId).isEqualTo(TASK_ID)
+ assertWithMessage("Task ID").that(result.taskId).isEqualTo(1001)
}
/** Tests applying CaptureParameters with 'FullScreen' CaptureType */
@Test
- @EnableFlags(Flags.FLAG_SCREENSHOT_POLICY_SPLIT_AND_DESKTOP_MODE)
fun testProcess_newPolicy_fullScreen() = runTest {
- val screenImage = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ val screenImage = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
/* Create a policy request processor with no capture policies */
val requestProcessor =
@@ -136,7 +139,17 @@ class PolicyRequestProcessorTest {
val result =
requestProcessor.modify(
screenshotRequest,
- CaptureParameters(FullScreen(displayId = 0), defaultComponent, defaultOwner),
+ CaptureParameters(
+ type = FullScreen(displayId = 0),
+ contentTask =
+ TaskReference(
+ taskId = 1234,
+ component = defaultComponent,
+ owner = UserHandle.CURRENT,
+ bounds = Rect(1, 2, 3, 4),
+ ),
+ owner = defaultOwner,
+ ),
)
assertWithMessage("The result bitmap").that(result.bitmap).isSameInstanceAs(screenImage)
@@ -149,7 +162,11 @@ class PolicyRequestProcessorTest {
.that(result.topComponent)
.isEqualTo(defaultComponent)
- assertWithMessage("Task ID").that(result.taskId).isEqualTo(-1)
+ assertWithMessage("The bounds of the screenshot")
+ .that(result.originalScreenBounds)
+ .isEqualTo(Rect(0, 0, 100, 100))
+
+ assertWithMessage("Task ID").that(result.taskId).isEqualTo(1234)
}
/** Tests behavior when no policies are applied */
@@ -230,7 +247,7 @@ class PolicyRequestProcessorTest {
policy = "",
reason = "",
parameters =
- CaptureParameters(
+ LegacyCaptureParameters(
IsolatedTask(taskId = 0, taskBounds = null),
null,
UserHandle.CURRENT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
index ae976a0ea703..9fb752a11f56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.settings
import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+import android.hardware.display.DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS
import android.hardware.display.DisplayManagerGlobal
import android.os.Handler
import android.testing.AndroidTestingRunner
@@ -59,14 +59,14 @@ class DisplayTrackerImplTest : SysuiTestCase() {
DisplayManagerGlobal.getInstance(),
Display.DEFAULT_DISPLAY,
DisplayInfo(),
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
)
mSecondaryDisplay =
Display(
DisplayManagerGlobal.getInstance(),
Display.DEFAULT_DISPLAY + 1,
DisplayInfo(),
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS,
)
`when`(displayManager.displays).thenReturn(arrayOf(mDefaultDisplay, mSecondaryDisplay))
@@ -94,7 +94,12 @@ class DisplayTrackerImplTest : SysuiTestCase() {
fun registerBrightnessCallback_registersDisplayListener() {
tracker.addBrightnessChangeCallback(TestCallback(), executor)
verify(displayManager)
- .registerDisplayListener(any(), any(), eq(EVENT_FLAG_DISPLAY_BRIGHTNESS))
+ .registerDisplayListener(
+ any(),
+ any(),
+ eq(0L),
+ eq(PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS),
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index a8929a63a812..f870200c2b00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.logging;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -59,8 +57,6 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.logging.nano.Notifications;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
@@ -118,11 +114,6 @@ public class NotificationLoggerTest extends SysuiTestCase {
private final FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
private final PowerInteractor mPowerInteractor =
PowerInteractorFactory.create().getPowerInteractor();
- private final ActiveNotificationListRepository mActiveNotificationListRepository =
- new ActiveNotificationListRepository();
- private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
- new ActiveNotificationsInteractor(mActiveNotificationListRepository,
- StandardTestDispatcher(null, null));
private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
@@ -137,7 +128,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
mKeyguardRepository,
mHeadsUpManager,
mPowerInteractor,
- mActiveNotificationsInteractor,
+ mKosmos.getActiveNotificationsInteractor(),
() -> mKosmos.getSceneInteractor());
mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
deleted file mode 100644
index 625963f5ec7a..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * Copyright (C) 2018 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.statusbar.notification.row;
-
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
-
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutManager;
-import android.graphics.Color;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableLooper;
-import android.util.ArraySet;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
-import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository;
-import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
-import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.wmshell.BubblesManager;
-
-import kotlinx.coroutines.test.TestScope;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.Optional;
-
-/**
- * Tests for {@link NotificationGutsManager}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class NotificationGutsManagerTest extends SysuiTestCase {
- private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
-
- private NotificationChannel mTestNotificationChannel = new NotificationChannel(
- TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
-
- private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- private final TestScope mTestScope = mKosmos.getTestScope();
- private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
- private final FakeExecutor mExecutor = mKosmos.getFakeExecutor();
- private final Handler mHandler = mKosmos.getFakeExecutorHandler();
- private NotificationTestHelper mHelper;
- private NotificationGutsManager mGutsManager;
-
- @Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private MetricsLogger mMetricsLogger;
- @Mock private OnUserInteractionCallback mOnUserInteractionCallback;
- @Mock private NotificationPresenter mPresenter;
- @Mock private NotificationActivityStarter mNotificationActivityStarter;
- @Mock private NotificationListContainer mNotificationListContainer;
- @Mock private OnSettingsClickListener mOnSettingsClickListener;
- @Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private AccessibilityManager mAccessibilityManager;
- @Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private INotificationManager mINotificationManager;
- @Mock private IStatusBarService mBarService;
- @Mock private LauncherApps mLauncherApps;
- @Mock private ShortcutManager mShortcutManager;
- @Mock private ChannelEditorDialogController mChannelEditorDialogController;
- @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock private UserContextProvider mContextTracker;
- @Mock private BubblesManager mBubblesManager;
- @Mock private ShadeController mShadeController;
- @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
- @Mock private AssistantFeedbackController mAssistantFeedbackController;
- @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
- @Mock private StatusBarStateController mStatusBarStateController;
- @Mock private HeadsUpManager mHeadsUpManager;
- @Mock private ActivityStarter mActivityStarter;
-
- @Mock private UserManager mUserManager;
-
- private final ActiveNotificationListRepository mActiveNotificationListRepository =
- new ActiveNotificationListRepository();
- private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
- new ActiveNotificationsInteractor(mActiveNotificationListRepository,
- StandardTestDispatcher(null, null));
-
- private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
-
- @Before
- public void setUp() {
- allowTestableLooperAsMainThread();
- mHelper = new NotificationTestHelper(mContext, mDependency);
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
-
- mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
- mTestScope.getBackgroundScope(),
- new WindowRootViewVisibilityRepository(mBarService, mExecutor),
- new FakeKeyguardRepository(),
- mHeadsUpManager,
- PowerInteractorFactory.create().getPowerInteractor(),
- mActiveNotificationsInteractor,
- () -> mKosmos.getSceneInteractor()
- );
-
- mGutsManager = new NotificationGutsManager(
- mContext,
- mHandler,
- mHandler,
- mJavaAdapter,
- mAccessibilityManager,
- mHighPriorityProvider,
- mINotificationManager,
- mUserManager,
- mPeopleSpaceWidgetManager,
- mLauncherApps,
- mShortcutManager,
- mChannelEditorDialogController,
- mContextTracker,
- mAssistantFeedbackController,
- Optional.of(mBubblesManager),
- new UiEventLoggerFake(),
- mOnUserInteractionCallback,
- mShadeController,
- mWindowRootViewVisibilityInteractor,
- mNotificationLockscreenUserManager,
- mStatusBarStateController,
- mBarService,
- mDeviceProvisionedController,
- mMetricsLogger,
- mHeadsUpManager,
- mActivityStarter);
- mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
- mOnSettingsClickListener);
- mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mGutsManager.start();
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Test methods:
-
- @Test
- public void testOpenAndCloseGuts() {
- NotificationGuts guts = spy(new NotificationGuts(mContext));
- when(guts.post(any())).thenAnswer(invocation -> {
- mHandler.post(((Runnable) invocation.getArguments()[0]));
- return null;
- });
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing().when(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
-
- ExpandableNotificationRow realRow = createTestNotificationRow();
- NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
- ExpandableNotificationRow row = spy(realRow);
- when(row.getWindowToken()).thenReturn(new Binder());
- when(row.getGuts()).thenReturn(guts);
-
- assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
- assertEquals(View.INVISIBLE, guts.getVisibility());
- mExecutor.runAllReady();
- verify(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
- verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), true);
-
- assertEquals(View.VISIBLE, guts.getVisibility());
- mGutsManager.closeAndSaveGuts(false, false, true, 0, 0, false);
-
- verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
- verify(row, times(1)).setGutsView(any());
- mExecutor.runAllReady();
- verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false);
- }
-
- @Test
- public void testLockscreenShadeVisible_visible_gutsNotClosed() {
- // First, start out lockscreen or shade as not visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
- mTestScope.getTestScheduler().runCurrent();
-
- NotificationGuts guts = mock(NotificationGuts.class);
- mGutsManager.setExposedGuts(guts);
-
- // WHEN the lockscreen or shade becomes visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
- mTestScope.getTestScheduler().runCurrent();
-
- // THEN the guts are not closed
- verify(guts, never()).removeCallbacks(any());
- verify(guts, never()).closeControls(
- anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
- }
-
- @Test
- public void testLockscreenShadeVisible_notVisible_gutsClosed() {
- // First, start out lockscreen or shade as visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
- mTestScope.getTestScheduler().runCurrent();
-
- NotificationGuts guts = mock(NotificationGuts.class);
- mGutsManager.setExposedGuts(guts);
-
- // WHEN the lockscreen or shade is no longer visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
- mTestScope.getTestScheduler().runCurrent();
-
- // THEN the guts are closed
- verify(guts).removeCallbacks(any());
- verify(guts).closeControls(
- /* leavebehinds= */ eq(true),
- /* controls= */ eq(true),
- /* x= */ anyInt(),
- /* y= */ anyInt(),
- /* force= */ eq(true));
- }
-
- @Test
- public void testLockscreenShadeVisible_notVisible_listContainerReset() {
- // First, start out lockscreen or shade as visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
- mTestScope.getTestScheduler().runCurrent();
-
- // WHEN the lockscreen or shade is no longer visible
- mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
- mTestScope.getTestScheduler().runCurrent();
-
- // THEN the list container is reset
- verify(mNotificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean());
- }
-
- @Test
- public void testChangeDensityOrFontScale() {
- NotificationGuts guts = spy(new NotificationGuts(mContext));
- when(guts.post(any())).thenAnswer(invocation -> {
- mHandler.post(((Runnable) invocation.getArguments()[0]));
- return null;
- });
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing().when(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
-
- ExpandableNotificationRow realRow = createTestNotificationRow();
- NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
-
- ExpandableNotificationRow row = spy(realRow);
-
- when(row.getWindowToken()).thenReturn(new Binder());
- when(row.getGuts()).thenReturn(guts);
- doNothing().when(row).ensureGutsInflated();
-
- NotificationEntry realEntry = realRow.getEntry();
- NotificationEntry entry = spy(realEntry);
-
- when(entry.getRow()).thenReturn(row);
- when(entry.getGuts()).thenReturn(guts);
-
- assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
- mExecutor.runAllReady();
- verify(guts).openControls(
- anyInt(),
- anyInt(),
- anyBoolean(),
- any(Runnable.class));
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any());
-
- row.onDensityOrFontScaleChanged();
- mGutsManager.onDensityOrFontScaleChanged(entry);
-
- mExecutor.runAllReady();
-
- mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
-
- verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_mic() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_RECORD_AUDIO);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera_mic() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- ops.add(OP_RECORD_AUDIO);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera_mic_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- ops.add(OP_RECORD_AUDIO);
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_camera_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_CAMERA);
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
- }
-
- @Test
- public void testAppOpsSettingsIntent_mic_overlay() {
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(OP_RECORD_AUDIO);
- ops.add(OP_SYSTEM_ALERT_WINDOW);
- mGutsManager.startAppOpsSettingsActivity("", 0, ops, null);
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mNotificationActivityStarter, times(1))
- .startNotificationGutsIntent(captor.capture(), anyInt(), any());
- assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
- }
-
- @Test
- public void testInitializeNotificationInfoView_highPriority() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- final NotificationEntry entry = row.getEntry();
- modifyRanking(entry)
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- when(row.getIsNonblockable()).thenReturn(false);
- when(mHighPriorityProvider.isHighPriority(entry)).thenReturn(true);
- StatusBarNotification statusBarNotification = entry.getSbn();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(mOnUserInteractionCallback),
- eq(mChannelEditorDialogController),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- eq(entry),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- any(UiEventLogger.class),
- eq(false),
- eq(false),
- eq(true), /* wasShownHighPriority */
- eq(mAssistantFeedbackController),
- any(MetricsLogger.class));
- }
-
- @Test
- public void testInitializeNotificationInfoView_PassesAlongProvisionedState() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getEntry().getSbn();
- NotificationEntry entry = row.getEntry();
-
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
-
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(mOnUserInteractionCallback),
- eq(mChannelEditorDialogController),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- eq(entry),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- any(UiEventLogger.class),
- eq(true),
- eq(false),
- eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController),
- any(MetricsLogger.class));
- }
-
- @Test
- public void testInitializeNotificationInfoView_withInitialAction() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- modifyRanking(row.getEntry())
- .setUserSentiment(USER_SENTIMENT_NEGATIVE)
- .build();
- when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getEntry().getSbn();
- NotificationEntry entry = row.getEntry();
-
- mGutsManager.initializeNotificationInfo(row, notificationInfoView);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(mOnUserInteractionCallback),
- eq(mChannelEditorDialogController),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- eq(entry),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- any(UiEventLogger.class),
- eq(false),
- eq(false),
- eq(false), /* wasShownHighPriority */
- eq(mAssistantFeedbackController),
- any(MetricsLogger.class));
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Utility methods:
-
- private ExpandableNotificationRow createTestNotificationRow() {
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setColorized(true).setColor(Color.RED)
- .setFlag(Notification.FLAG_CAN_COLORIZE, true)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- try {
- ExpandableNotificationRow row = mHelper.createRow(nb.build());
- modifyRanking(row.getEntry())
- .setChannel(mTestNotificationChannel)
- .build();
- return row;
- } catch (Exception e) {
- fail();
- return null;
- }
- }
-
- private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
- NotificationMenuRowPlugin menuRow =
- new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- menuRow.createMenu(row, row.getEntry().getSbn());
-
- NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
- assertNotNull(menuItem);
- return menuItem;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6069b4409ec9..d2350bc82667 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.stack;
import static android.view.View.GONE;
import static android.view.WindowInsets.Type.ime;
+import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -54,6 +55,7 @@ import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.util.MathUtils;
@@ -64,13 +66,13 @@ import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.widget.TextView;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.ExpandHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.BrokenWithSceneContainer;
import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -118,16 +120,25 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
/**
* Tests for {@link NotificationStackScrollLayout}.
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return parameterizeSceneContainerFlag();
+ }
+
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
@@ -154,6 +165,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@Mock private AvalancheController mAvalancheController;
+ public NotificationStackScrollLayoutTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
@@ -353,6 +369,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
+ @DisableSceneContainer
public void updateStackEndHeightAndStackHeight_onlyUpdatesStackHeightDuringSwipeUp() {
final float expansionFraction = 0.5f;
mAmbientState.setStatusBarState(StatusBarState.KEYGUARD);
@@ -366,6 +383,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
+ @DisableSceneContainer
public void setPanelFlinging_updatesStackEndHeightOnlyOnFinish() {
final float expansionFraction = 0.5f;
mAmbientState.setStatusBarState(StatusBarState.KEYGUARD);
@@ -729,7 +747,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
- @DisableFlags({FooterViewRefactor.FLAG_NAME, ModesEmptyShadeFix.FLAG_NAME})
+ @DisableFlags({FooterViewRefactor.FLAG_NAME,
+ ModesEmptyShadeFix.FLAG_NAME,
+ NotifRedesignFooter.FLAG_NAME})
public void testReInflatesFooterViews() {
when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
clearInvocations(mStackScroller);
@@ -1429,6 +1449,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ @BrokenWithSceneContainer(bugId = 332732878) // because NSSL#mAnimationsEnabled is always true
public void testGenerateHeadsUpAnimation_isSeenInShade_noAnimation() {
// GIVEN NSSL is ready for HUN animations
Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
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 f472fd1019eb..b142fc2deea9 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
@@ -155,6 +155,7 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeLogger;
+import com.android.systemui.shade.StatusBarLongPressGestureDetector;
import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyboardShortcutListSearch;
@@ -174,8 +175,8 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
-import com.android.systemui.statusbar.core.StatusBarOrchestrator;
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -371,7 +372,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
@Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
@Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
- @Mock private StatusBarOrchestrator mStatusBarOrchestrator;
+ @Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
@@ -387,6 +388,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor =
mKosmos.getBrightnessMirrorShowingInteractor();
+
+ private final StatusBarModePerDisplayRepository mStatusBarModePerDisplayRepository =
+ mKosmos.getStatusBarModePerDisplayRepository();
private ScrimController mScrimController;
@Before
@@ -506,7 +510,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
mShadeController.setNotificationPresenter(mNotificationPresenter);
- when(mOperatorNameViewControllerFactory.create(any()))
+ when(mOperatorNameViewControllerFactory.create(any(), any()))
.thenReturn(mOperatorNameViewController);
when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
when(mUserTracker.getUserHandle()).thenReturn(
@@ -537,6 +541,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mAutoHideController,
new StatusBarInitializerImpl(
mStatusBarWindowController,
+ mStatusBarModePerDisplayRepository,
mCollapsedStatusBarFragmentProvider,
mock(StatusBarRootFactory.class),
mock(HomeStatusBarComponent.Factory.class),
@@ -602,6 +607,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mShadeController,
mWindowRootViewVisibilityInteractor,
mStatusBarKeyguardViewManager,
+ () -> mStatusBarLongPressGestureDetector,
mViewMediatorCallback,
mInitController,
new Handler(TestableLooper.get(this).getLooper()),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 638f195df00c..69efa87a9cac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -40,14 +40,13 @@ import com.android.systemui.flags.Flags
import com.android.systemui.plugins.fakeDarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.shade.LongPressGestureDetector
import com.android.systemui.shade.ShadeControllerImpl
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.StatusBarLongPressGestureDetector
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
-import com.android.systemui.statusbar.data.repository.statusBarContentInsetsProviderStore
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -98,7 +97,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Mock private lateinit var windowRootView: Provider<WindowRootView>
@Mock private lateinit var shadeLogger: ShadeLogger
@Mock private lateinit var viewUtil: ViewUtil
- @Mock private lateinit var longPressGestureDetector: LongPressGestureDetector
+ @Mock private lateinit var mStatusBarLongPressGestureDetector: StatusBarLongPressGestureDetector
private lateinit var statusBarWindowStateController: StatusBarWindowStateController
private lateinit var view: PhoneStatusBarView
@@ -395,7 +394,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
shadeControllerImpl,
shadeViewController,
panelExpansionInteractor,
- { longPressGestureDetector },
+ { mStatusBarLongPressGestureDetector },
windowRootView,
shadeLogger,
viewUtil,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d01c1ca36c4e..d1e4f646a382 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone.fragment;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
-import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
@@ -61,6 +60,7 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.core.StatusBarRootModernization;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -156,7 +156,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testDisableNone() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -167,7 +167,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -185,7 +185,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
// GIVEN the status bar hides the system info via disable flags, while there is no event
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -215,7 +215,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
// GIVEN the status bar hides the system info via disable flags, while there is no event
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -232,7 +232,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
// GIVEN the status bar is not disabled
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -248,7 +248,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
// GIVEN the status bar is not disabled
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -272,7 +272,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testDisableNotifications() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -290,7 +290,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME)
public void testDisableNotifications_doesNothingWhenFlagEnabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -308,7 +308,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testDisableClock() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -326,7 +326,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @EnableFlags(StatusBarRootModernization.FLAG_NAME)
public void testDisableClock_doesNothingWhenFlagEnabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -345,7 +345,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_shadeOpenAndShouldHide_everythingHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -363,7 +363,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_shadeOpenButNotShouldHide_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -382,7 +382,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
/** Regression test for b/279790651. */
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -410,7 +410,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_notTransitioningToOccluded_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -426,7 +426,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_isTransitioningToOccluded_everythingHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -442,7 +442,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -473,7 +473,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void disable_noOngoingCall_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -485,7 +485,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -498,7 +498,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -511,7 +511,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void disable_hasOngoingCallButAlsoHun_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -524,7 +524,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void disable_ongoingCallEnded_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -548,7 +548,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
@@ -565,7 +565,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME})
public void screenSharingChipsDisabled_ignoresNewCallback() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -599,7 +599,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void noOngoingActivity_chipHidden() {
resumeAndGetFragment();
@@ -617,7 +617,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
resumeAndGetFragment();
@@ -634,8 +634,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@EnableFlags({
FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
StatusBarNotifChips.FLAG_NAME,
- FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() {
+ StatusBarRootModernization.FLAG_NAME})
+ public void hasPrimaryOngoingActivity_viewsUnchangedWhenRootModernizationFlagOn() {
resumeAndGetFragment();
assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
@@ -660,7 +660,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
resumeAndGetFragment();
@@ -674,7 +674,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
resumeAndGetFragment();
@@ -689,7 +689,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -706,7 +706,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -724,7 +724,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -741,7 +741,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -759,7 +759,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
resumeAndGetFragment();
@@ -782,7 +782,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
resumeAndGetFragment();
@@ -805,7 +805,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void secondaryOngoingActivityEnded_chipHidden() {
resumeAndGetFragment();
@@ -828,7 +828,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
@@ -847,7 +847,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
@@ -866,7 +866,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME})
public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -899,7 +899,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -933,7 +933,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
resumeAndGetFragment();
@@ -947,7 +947,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void isHomeStatusBarAllowedByScene_true_everythingShown() {
resumeAndGetFragment();
@@ -961,7 +961,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -979,7 +979,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -997,7 +997,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
resumeAndGetFragment();
@@ -1011,7 +1011,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_isDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -1023,7 +1023,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_NotDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(false);
@@ -1035,7 +1035,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
@@ -1046,7 +1046,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
@@ -1100,7 +1100,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -1123,7 +1123,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -1159,7 +1159,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+ @DisableFlags(StatusBarRootModernization.FLAG_NAME)
public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -1184,9 +1184,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mKeyguardStateController = mock(KeyguardStateController.class);
mOperatorNameViewController = mock(OperatorNameViewController.class);
mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
- when(mOperatorNameViewControllerFactory.create(any()))
+ when(mOperatorNameViewControllerFactory.create(any(), any()))
.thenReturn(mOperatorNameViewController);
- when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager);
+ when(mIconManagerFactory.create(any(), any(), any())).thenReturn(mIconManager);
mSecureSettings = mock(SecureSettings.class);
mShadeExpansionStateManager = new ShadeExpansionStateManager();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
index c435d3d99680..37671e0bc175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
@@ -21,9 +21,9 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
@@ -38,7 +38,7 @@ private const val INITIAL_ALPHA = 1f
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+@DisableFlags(StatusBarRootModernization.FLAG_NAME)
class MultiSourceMinAlphaControllerTest : SysuiTestCase() {
private val view = View(context)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index c48898aad087..2e0b7c69f092 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -67,12 +67,12 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
@@ -96,6 +96,8 @@ import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -137,6 +139,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private val wifiPickerTrackerCallback =
argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>()
private val vcnTransportInfo = VcnTransportInfo.Builder().build()
+ private val userRepository = kosmos.fakeUserRepository
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -159,7 +162,14 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
logcatTableLogBuffer(kosmos, "test")
}
- whenever(wifiPickerTrackerFactory.create(any(), capture(wifiPickerTrackerCallback), any()))
+ whenever(
+ wifiPickerTrackerFactory.create(
+ any(),
+ any(),
+ capture(wifiPickerTrackerCallback),
+ any(),
+ )
+ )
.thenReturn(wifiPickerTracker)
// For convenience, set up the subscription info callbacks
@@ -188,6 +198,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
wifiRepository =
WifiRepositoryImpl(
+ mContext,
+ userRepository,
testScope.backgroundScope,
mainExecutor,
testDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 4d293b98c165..6326e73aec67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -102,6 +102,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
logBuffer = FakeLogBuffer.Factory.create(),
verboseLogBuffer = FakeLogBuffer.Factory.create(),
systemClock,
+ context.resources,
)
val connectionState by collectLastValue(underTest.connectionState)
@@ -267,11 +268,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun satelliteProvisioned_notSupported_defaultFalse() =
testScope.runTest {
// GIVEN satellite is not supported
- setUpRepo(
- uptime = MIN_UPTIME,
- satMan = satelliteManager,
- satelliteSupported = false,
- )
+ setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
assertThat(underTest.isSatelliteProvisioned.value).isFalse()
}
@@ -280,11 +277,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun satelliteProvisioned_supported_defaultFalse() =
testScope.runTest {
// GIVEN satellite is supported
- setUpRepo(
- uptime = MIN_UPTIME,
- satMan = satelliteManager,
- satelliteSupported = true,
- )
+ setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true)
// THEN default provisioned state is false
assertThat(underTest.isSatelliteProvisioned.value).isFalse()
@@ -323,6 +316,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
logBuffer = FakeLogBuffer.Factory.create(),
verboseLogBuffer = FakeLogBuffer.Factory.create(),
systemClock,
+ context.resources,
)
// WHEN we try to check for provisioned status
@@ -361,6 +355,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
logBuffer = FakeLogBuffer.Factory.create(),
verboseLogBuffer = FakeLogBuffer.Factory.create(),
systemClock,
+ context.resources,
)
// WHEN we try to check for provisioned status
@@ -445,11 +440,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() =
testScope.runTest {
// GIVEN satellite is supported
- setUpRepo(
- uptime = MIN_UPTIME,
- satMan = satelliteManager,
- satelliteSupported = true,
- )
+ setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true)
val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
@@ -487,11 +478,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun satelliteNotSupported_listenersAreNotRegistered() =
testScope.runTest {
// GIVEN satellite is not supported
- setUpRepo(
- uptime = MIN_UPTIME,
- satMan = satelliteManager,
- satelliteSupported = false,
- )
+ setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
// WHEN data is requested from the repo
val connectionState by collectLastValue(underTest.connectionState)
@@ -517,11 +504,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun satelliteNotSupported_registersCallbackForStateChanges() =
testScope.runTest {
// GIVEN satellite is not supported
- setUpRepo(
- uptime = MIN_UPTIME,
- satMan = satelliteManager,
- satelliteSupported = false,
- )
+ setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
runCurrent()
// THEN the repo registers for state changes of satellite support
@@ -577,11 +560,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun satelliteNotSupported_supportShowsUp_registersListeners() =
testScope.runTest {
// GIVEN satellite is not supported
- setUpRepo(
- uptime = MIN_UPTIME,
- satMan = satelliteManager,
- satelliteSupported = false,
- )
+ setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
runCurrent()
val callback =
@@ -610,11 +589,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
fun repoDoesNotCheckForSupportUntilMinUptime() =
testScope.runTest {
// GIVEN we init 100ms after sysui starts up
- setUpRepo(
- uptime = 100,
- satMan = satelliteManager,
- satelliteSupported = true,
- )
+ setUpRepo(uptime = 100, satMan = satelliteManager, satelliteSupported = true)
// WHEN data is requested
val connectionState by collectLastValue(underTest.connectionState)
@@ -726,6 +701,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
logBuffer = FakeLogBuffer.Factory.create(),
verboseLogBuffer = FakeLogBuffer.Factory.create(),
systemClock,
+ context.resources,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 44e1437b909e..d823bf57c824 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -16,13 +16,19 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
+import android.content.Context
+import android.content.pm.UserInfo
import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.UNKNOWN_SSID
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.LogBuffer
@@ -33,12 +39,10 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRep
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.fakeSystemClock
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType
@@ -56,7 +60,12 @@ import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
/**
* Note: Most of these tests are duplicates of [WifiRepositoryImplTest] tests.
@@ -67,8 +76,9 @@ import org.mockito.Mockito.verify
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class WifiRepositoryImplTest : SysuiTestCase() {
+class WifiRepositoryImplTest() : SysuiTestCase() {
private val kosmos = testKosmos()
+ private val userRepository = kosmos.fakeUserRepository
// Using lazy means that the class will only be constructed once it's fetched. Because the
// repository internally sets some values on construction, we need to set up some test
@@ -76,6 +86,8 @@ class WifiRepositoryImplTest : SysuiTestCase() {
// inside each test case without needing to manually recreate the repository.
private val underTest: WifiRepositoryImpl by lazy {
WifiRepositoryImpl(
+ mContext,
+ userRepository,
testScope.backgroundScope,
executor,
dispatcher,
@@ -101,7 +113,8 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Before
fun setUp() {
- whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor), any()))
+ userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
+ whenever(wifiPickerTrackerFactory.create(any(), any(), capture(callbackCaptor), any()))
.thenReturn(wifiPickerTracker)
}
@@ -1203,6 +1216,95 @@ class WifiRepositoryImplTest : SysuiTestCase() {
assertThat(latest).isEmpty()
}
+ // TODO(b/371586248): This test currently require currentUserContext to be public for testing,
+ // this needs to
+ // be updated to capture the argument instead so currentUserContext can be private.
+ @Test
+ @EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
+ fun oneUserVerifyCreatingWifiPickerTracker_multiuserFlagEnabled() =
+ testScope.runTest {
+ val primaryUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(
+ UserHandle.of(PRIMARY_USER_ID),
+ primaryUserMockContext,
+ )
+
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ runCurrent()
+ val currentUserContext by collectLastValue(underTest.selectedUserContext)
+
+ assertThat(currentUserContext).isEqualTo(primaryUserMockContext)
+ verify(wifiPickerTrackerFactory).create(any(), any(), any(), any())
+ }
+
+ // TODO(b/371586248): This test currently require currentUserContext to be public for testing,
+ // this needs to
+ // be updated to capture the argument instead so currentUserContext can be private.
+ @Test
+ @EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
+ fun changeUserVerifyCreatingWifiPickerTracker_multiuserEnabled() =
+ testScope.runTest {
+ val primaryUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(
+ UserHandle.of(PRIMARY_USER_ID),
+ primaryUserMockContext,
+ )
+
+ runCurrent()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ runCurrent()
+ val currentUserContext by collectLastValue(underTest.selectedUserContext)
+
+ assertThat(currentUserContext).isEqualTo(primaryUserMockContext)
+
+ val otherUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(
+ UserHandle.of(ANOTHER_USER_ID),
+ otherUserMockContext,
+ )
+
+ runCurrent()
+ userRepository.setSelectedUserInfo(ANOTHER_USER)
+ runCurrent()
+ val otherUserContext by collectLastValue(underTest.selectedUserContext)
+
+ assertThat(otherUserContext).isEqualTo(otherUserMockContext)
+ verify(wifiPickerTrackerFactory, times(2)).create(any(), any(), any(), any())
+ }
+
+ // TODO(b/371586248): This test currently require currentUserContext to be public for testing,
+ // this needs to
+ // be updated to capture the argument instead so currentUserContext can be private.
+ @Test
+ @DisableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
+ fun changeUserVerifyCreatingWifiPickerTracker_multiuserDisabled() =
+ testScope.runTest {
+ val primaryUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(
+ UserHandle.of(PRIMARY_USER_ID),
+ primaryUserMockContext,
+ )
+
+ runCurrent()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ runCurrent()
+ val currentUserContext by collectLastValue(underTest.selectedUserContext)
+
+ assertThat(currentUserContext).isEqualTo(primaryUserMockContext)
+
+ val otherUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(
+ UserHandle.of(ANOTHER_USER_ID),
+ otherUserMockContext,
+ )
+
+ runCurrent()
+ userRepository.setSelectedUserInfo(ANOTHER_USER)
+ runCurrent()
+
+ verify(wifiPickerTrackerFactory, times(1)).create(any(), any(), any(), any())
+ }
+
private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback {
testScope.runCurrent()
return callbackCaptor.value
@@ -1231,5 +1333,20 @@ class WifiRepositoryImplTest : SysuiTestCase() {
private companion object {
const val TITLE = "AB"
+ private const val PRIMARY_USER_ID = 0
+ private val PRIMARY_USER =
+ UserInfo(
+ /* id= */ PRIMARY_USER_ID,
+ /* name= */ "primary user",
+ /* flags= */ UserInfo.FLAG_PROFILE,
+ )
+
+ private const val ANOTHER_USER_ID = 1
+ private val ANOTHER_USER =
+ UserInfo(
+ /* id= */ ANOTHER_USER_ID,
+ /* name= */ "another user",
+ /* flags= */ UserInfo.FLAG_PROFILE,
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 38a61fecdc8a..21adeb01487b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -316,6 +316,31 @@ public class WalletScreenControllerTest extends SysuiTestCase {
}
@Test
+ public void queryCards_hasCards_showCarousel_invalidIconSource_noIcon() {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ Collections.singletonList(createWalletCardWithInvalidIcon(mContext)), 0);
+
+ mController.queryWalletCards();
+ mTestableLooper.processAllMessages();
+
+ verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+ mCallbackCaptor.getValue();
+
+ assertEquals(mController, callback);
+
+ callback.onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(VISIBLE, mWalletView.getCardCarousel().getVisibility());
+ assertEquals(GONE, mWalletView.getEmptyStateView().getVisibility());
+ assertEquals(GONE, mWalletView.getErrorView().getVisibility());
+ assertEquals(null, mWalletView.getIcon().getDrawable());
+ }
+
+ @Test
public void queryCards_noCards_showEmptyState() {
GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
@@ -507,6 +532,16 @@ public class WalletScreenControllerTest extends SysuiTestCase {
.build();
}
+ private WalletCard createWalletCardWithInvalidIcon(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(
+ CARD_ID_1, createIconWithInvalidSource(), "•••• 1234", pendingIntent)
+ .setCardIcon(createIconWithInvalidSource())
+ .setCardLabel("Hold to reader")
+ .build();
+ }
+
private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) {
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
@@ -520,6 +555,10 @@ public class WalletScreenControllerTest extends SysuiTestCase {
return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888));
}
+ private static Icon createIconWithInvalidSource() {
+ return Icon.createWithContentUri("content://media/external/images/media");
+ }
+
private WalletCardViewInfo createCardViewInfo(WalletCard walletCard) {
return new WalletScreenController.QAWalletCardViewInfo(
mContext, walletCard);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 48106de5225b..856333ea724e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -49,6 +49,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -2395,7 +2396,8 @@ public class BubblesTest extends SysuiTestCase {
FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
mBubbleController.registerBubbleStateListener(bubbleStateListener);
- mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT);
+ mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW);
assertThat(bubbleStateListener.mLastUpdate).isNotNull();
assertThat(bubbleStateListener.mLastUpdate.bubbleBarLocation).isEqualTo(
BubbleBarLocation.LEFT);
@@ -2408,7 +2410,8 @@ public class BubblesTest extends SysuiTestCase {
FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
mBubbleController.registerBubbleStateListener(bubbleStateListener);
- mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT);
+ mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW);
assertThat(bubbleStateListener.mStateChangeCalls).isEqualTo(0);
}
@@ -2505,17 +2508,54 @@ public class BubblesTest extends SysuiTestCase {
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@Test
- public void testEventLogging_bubbleBar_dragBubbleToDismiss() {
+ public void testEventLogging_bubbleBar_dragSelectedBubbleToDismiss() {
mBubbleProperties.mIsBubbleBarEnabled = true;
mPositioner.setIsLargeScreen(true);
FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
mBubbleController.registerBubbleStateListener(bubbleStateListener);
mEntryListener.onEntryAdded(mRow);
- mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0);
+
+ clearInvocations(mBubbleLogger);
+
+ // Dismiss selected bubble
+ mBubbleController.startBubbleDrag(mRow2.getKey());
+ mBubbleController.dragBubbleToDismiss(mRow2.getKey(), System.currentTimeMillis());
+ // Log bubble dismissed via drag and new bubble selected
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow2.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED));
+
+ verifyNoMoreInteractions(mBubbleLogger);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_dragOtherBubbleToDismiss() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0);
+
+ clearInvocations(mBubbleLogger);
+
+ // Dismiss other bubble
+ mBubbleController.startBubbleDrag(mRow.getKey());
+ mBubbleController.dragBubbleToDismiss(mRow.getKey(), System.currentTimeMillis());
+
+ // Log bubble dismissed via drag, but no switch event
verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+
+ verifyNoMoreInteractions(mBubbleLogger);
}
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@@ -2535,6 +2575,78 @@ public class BubblesTest extends SysuiTestCase {
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@Test
+ public void testEventLogging_bubbleBar_dragBarLeft() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ mPositioner.setBubbleBarLocation(BubbleBarLocation.RIGHT);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ assertBarMode();
+
+ mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.DRAG_BAR);
+
+ verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_dragBarRight() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ assertBarMode();
+
+ mBubbleController.setBubbleBarLocation(BubbleBarLocation.RIGHT,
+ BubbleBarLocation.UpdateSource.DRAG_BAR);
+
+ verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_dragBubbleLeft() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ mPositioner.setBubbleBarLocation(BubbleBarLocation.RIGHT);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ assertBarMode();
+
+ mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT,
+ BubbleBarLocation.UpdateSource.DRAG_BUBBLE);
+
+ verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_dragBubbleRight() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ assertBarMode();
+
+ mBubbleController.setBubbleBarLocation(BubbleBarLocation.RIGHT,
+ BubbleBarLocation.UpdateSource.DRAG_BUBBLE);
+
+ verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE);
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
public void testEventLogging_bubbleBar_expandAndCollapse() {
mBubbleProperties.mIsBubbleBarEnabled = true;
mPositioner.setIsLargeScreen(true);
@@ -2569,6 +2681,32 @@ public class BubblesTest extends SysuiTestCase {
eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_switchBubble() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow.getKey(), 0);
+
+ // First select is expand
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+ verify(mBubbleLogger, never()).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED));
+
+ // Second select is switch
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0);
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow2.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED));
+ verify(mBubbleLogger, never()).log(eqBubbleWithKey(mRow2.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/android/view/FakeWindowManager.kt b/packages/SystemUI/tests/utils/src/android/view/FakeWindowManager.kt
new file mode 100644
index 000000000000..c28449feeab7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/view/FakeWindowManager.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view
+
+import android.content.Context
+import android.graphics.Region
+import android.view.WindowManager.LayoutParams
+
+class FakeWindowManager(private val context: Context) : WindowManager {
+
+ val addedViews = mutableMapOf<View, LayoutParams>()
+
+ override fun addView(view: View, params: ViewGroup.LayoutParams) {
+ addedViews[view] = params as LayoutParams
+ }
+
+ override fun removeView(view: View) {
+ addedViews.remove(view)
+ }
+
+ override fun updateViewLayout(view: View, params: ViewGroup.LayoutParams) {
+ addedViews[view] = params as LayoutParams
+ }
+
+ override fun getApplicationLaunchKeyboardShortcuts(deviceId: Int): KeyboardShortcutGroup {
+ return KeyboardShortcutGroup("Fake group")
+ }
+
+ override fun getCurrentImeTouchRegion(): Region {
+ return Region.obtain()
+ }
+
+ override fun getDefaultDisplay(): Display {
+ return context.display
+ }
+
+ override fun removeViewImmediate(view: View) {
+ addedViews.remove(view)
+ }
+
+ override fun requestAppKeyboardShortcuts(
+ receiver: WindowManager.KeyboardShortcutsReceiver,
+ deviceId: Int,
+ ) {
+ receiver.onKeyboardShortcutsReceived(emptyList())
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt
index d5451ee8eb10..025f556991f2 100644
--- a/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/view/WindowManagerKosmos.kt
@@ -16,9 +16,12 @@
package android.view
+import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
import org.mockito.Mockito.mock
+val Kosmos.fakeWindowManager by Kosmos.Fixture { FakeWindowManager(applicationContext) }
+
val Kosmos.mockWindowManager: WindowManager by Kosmos.Fixture { mock(WindowManager::class.java) }
var Kosmos.windowManager: WindowManager by Kosmos.Fixture { mockWindowManager }
diff --git a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
index e1c6699348a9..021c7bbb44cd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
@@ -16,11 +16,21 @@
package com.android.app.viewcapture
+import android.view.fakeWindowManager
import com.android.systemui.kosmos.Kosmos
import org.mockito.kotlin.mock
val Kosmos.mockViewCaptureAwareWindowManager by
Kosmos.Fixture { mock<ViewCaptureAwareWindowManager>() }
+val Kosmos.realCaptureAwareWindowManager by
+ Kosmos.Fixture {
+ ViewCaptureAwareWindowManager(
+ fakeWindowManager,
+ lazyViewCapture = lazy { mock<ViewCapture>() },
+ isViewCaptureEnabled = false,
+ )
+ }
+
var Kosmos.viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager by
Kosmos.Fixture { mockViewCaptureAwareWindowManager }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/OnTeardownRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/OnTeardownRule.kt
new file mode 100644
index 000000000000..778614b79fc8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/OnTeardownRule.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+import org.junit.runners.model.MultipleFailureException
+
+/**
+ * Rule that allows teardown steps to be added right next to the places where it becomes clear they
+ * are needed. This can avoid the need for complicated or conditional logic in a single teardown
+ * method. Examples:
+ * ```
+ * @get:Rule teardownRule = OnTeardownRule()
+ *
+ * // setup and teardown right next to each other
+ * @Before
+ * fun setUp() {
+ * val oldTimeout = getGlobalTimeout()
+ * teardownRule.onTeardown { setGlobalTimeout(oldTimeout) }
+ * overrideGlobalTimeout(5000)
+ * }
+ *
+ * // add teardown logic for fixtures that aren't used in every test
+ * fun addCustomer() {
+ * val id = globalDatabase.addCustomer(TEST_NAME, TEST_ADDRESS, ...)
+ * teardownRule.onTeardown { globalDatabase.deleteCustomer(id) }
+ * }
+ * ```
+ */
+class OnTeardownRule : TestWatcher() {
+ private var canAdd = true
+ private val teardowns = mutableListOf<() -> Unit>()
+
+ fun onTeardown(teardownRunnable: () -> Unit) {
+ if (!canAdd) {
+ throw IllegalStateException("Cannot add new teardown routines after test complete.")
+ }
+ teardowns.add(teardownRunnable)
+ }
+
+ fun onTeardown(teardownRunnable: Runnable) {
+ if (!canAdd) {
+ throw IllegalStateException("Cannot add new teardown routines after test complete.")
+ }
+ teardowns.add { teardownRunnable.run() }
+ }
+
+ override fun finished(description: Description?) {
+ canAdd = false
+ val errors = mutableListOf<Throwable>()
+ teardowns.reversed().forEach {
+ try {
+ it()
+ } catch (e: Throwable) {
+ errors.add(e)
+ }
+ }
+ MultipleFailureException.assertEmpty(errors)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 27a2cab1448e..153a8be06adc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -56,6 +56,8 @@ import org.mockito.Mockito;
import java.io.FileInputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
@@ -69,6 +71,17 @@ import java.util.concurrent.Future;
// background on Ravenwood is available at go/ravenwood-docs
@DisabledOnRavenwood
public abstract class SysuiTestCase {
+ /**
+ * Especially when self-testing test utilities, we may have classes that look like test
+ * classes, but we don't expect to ever actually run as a top-level test.
+ * For example, {@link com.android.systemui.TryToDoABadThing}.
+ * Verifying properties on these as a part of structural tests like
+ * AAAPlusPlusVerifySysuiRequiredTestPropertiesTest is a waste of our time, and makes things
+ * look more confusing, so this lets us skip when appropriate.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface SkipSysuiVerification {
+ }
private static final String TAG = "SysuiTestCase";
@@ -172,6 +185,15 @@ public abstract class SysuiTestCase {
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule public final OnTeardownRule mTearDownRule = new OnTeardownRule();
+
+ /**
+ * Schedule a cleanup routine to happen when the test state is torn down.
+ */
+ protected void onTeardown(Runnable tearDownRunnable) {
+ mTearDownRule.onTeardown(tearDownRunnable);
+ }
+
// set the highest order so it's the innermost rule
@Rule(order = Integer.MAX_VALUE)
public TestWithLooperRule mlooperRule = new TestWithLooperRule();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
index 3041240e8c86..b8be6aa50015 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
@@ -29,6 +29,8 @@ import android.util.ArraySet;
import android.util.Log;
import android.view.Display;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.res.R;
@@ -43,6 +45,9 @@ public class SysuiTestableContext extends TestableContext {
private final Map<UserHandle, Context> mContextForUser = new HashMap<>();
private final Map<String, Context> mContextForPackage = new HashMap<>();
+ @Nullable
+ private Display mCustomDisplay;
+
public SysuiTestableContext(Context base) {
super(base);
setTheme(R.style.Theme_SystemUI);
@@ -64,6 +69,18 @@ public class SysuiTestableContext extends TestableContext {
return context;
}
+ public void setDisplay(Display display) {
+ mCustomDisplay = display;
+ }
+
+ @Override
+ public Display getDisplay() {
+ if (mCustomDisplay != null) {
+ return mCustomDisplay;
+ }
+ return super.getDisplay();
+ }
+
public SysuiTestableContext createDefaultDisplayContext() {
Display display = getBaseContext().getSystemService(DisplayManager.class).getDisplays()[0];
return (SysuiTestableContext) createDisplayContext(display);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 219794f3ad18..a7917a0866bb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -37,9 +37,7 @@ import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.currentTime
-class FakeAuthenticationRepository(
- private val currentTime: () -> Long,
-) : AuthenticationRepository {
+class FakeAuthenticationRepository(private val currentTime: () -> Long) : AuthenticationRepository {
override val hintedPinLength: Int = HINTING_PIN_LENGTH
@@ -72,6 +70,9 @@ class FakeAuthenticationRepository(
private val credentialCheckingMutex = Mutex(locked = false)
+ var maximumTimeToLock: Long = 0
+ var powerButtonInstantlyLocks: Boolean = true
+
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return authenticationMethod.value
}
@@ -114,6 +115,7 @@ class FakeAuthenticationRepository(
MAX_FAILED_AUTH_TRIES_BEFORE_WIPE
var profileWithMinFailedUnlockAttemptsForWipe: Int = UserHandle.USER_SYSTEM
+
override suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int =
profileWithMinFailedUnlockAttemptsForWipe
@@ -144,10 +146,7 @@ class FakeAuthenticationRepository(
val failedAttempts = _failedAuthenticationAttempts.value
if (isSuccessful || failedAttempts < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
- AuthenticationResultModel(
- isSuccessful = isSuccessful,
- lockoutDurationMs = 0,
- )
+ AuthenticationResultModel(isSuccessful = isSuccessful, lockoutDurationMs = 0)
} else {
AuthenticationResultModel(
isSuccessful = false,
@@ -178,6 +177,14 @@ class FakeAuthenticationRepository(
credentialCheckingMutex.unlock()
}
+ override suspend fun getMaximumTimeToLock(): Long {
+ return maximumTimeToLock
+ }
+
+ override suspend fun getPowerButtonInstantlyLocks(): Boolean {
+ return powerButtonInstantlyLocks
+ }
+
private fun getExpectedCredential(securityMode: SecurityMode): List<Any> {
return when (val credentialType = getCurrentCredentialType(securityMode)) {
LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN
@@ -219,9 +226,7 @@ class FakeAuthenticationRepository(
}
@LockPatternUtils.CredentialType
- private fun getCurrentCredentialType(
- securityMode: SecurityMode,
- ): Int {
+ private fun getCurrentCredentialType(securityMode: SecurityMode): Int {
return when (securityMode) {
SecurityMode.PIN,
SecurityMode.SimPin,
@@ -260,9 +265,8 @@ class FakeAuthenticationRepository(
object FakeAuthenticationRepositoryModule {
@Provides
@SysUISingleton
- fun provideFake(
- scope: TestScope,
- ) = FakeAuthenticationRepository(currentTime = { scope.currentTime })
+ fun provideFake(scope: TestScope) =
+ FakeAuthenticationRepository(currentTime = { scope.currentTime })
@Module
interface Bindings {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt
index ad5242e2e036..4546b995316a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt
@@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.map
class FakeScreenBrightnessRepository(
initialBrightnessInfo: BrightnessInfo =
- BrightnessInfo(0f, 0f, 1f, HIGH_BRIGHTNESS_MODE_OFF, 1f, BRIGHTNESS_MAX_REASON_NONE)
+ BrightnessInfo(0f, 0f, 1f, HIGH_BRIGHTNESS_MODE_OFF, 1f, BRIGHTNESS_MAX_REASON_NONE),
) : ScreenBrightnessRepository {
private val brightnessInfo = MutableStateFlow(initialBrightnessInfo)
@@ -36,6 +36,8 @@ class FakeScreenBrightnessRepository(
override val linearBrightness = brightnessInfo.map { LinearBrightness(it.brightness) }
override val minLinearBrightness = brightnessInfo.map { LinearBrightness(it.brightnessMinimum) }
override val maxLinearBrightness = brightnessInfo.map { LinearBrightness(it.brightnessMaximum) }
+ override val isBrightnessOverriddenByWindow =
+ MutableStateFlow(initialBrightnessInfo.isBrightnessOverrideByWindow).asStateFlow()
override suspend fun getMinMaxLinearBrightness(): Pair<LinearBrightness, LinearBrightness> {
return minMaxLinearBrightness()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
index 52cdbed6e25d..2198e04eaf8a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.brightness.domain.interactor.screenBrightnessInterac
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
+import com.android.systemui.kosmos.brightnessWarningToast
val Kosmos.brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory by
Kosmos.Fixture {
@@ -32,6 +33,7 @@ val Kosmos.brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory b
hapticsViewModelFactory = sliderHapticsViewModelFactory,
brightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor,
supportsMirroring = allowsMirroring,
+ brightnessWarningToast = brightnessWarningToast,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorKosmos.kt
index 7e0e5f39c708..f876003926a1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorKosmos.kt
@@ -20,4 +20,4 @@ import com.android.systemui.common.ui.data.repository.configurationRepository
import com.android.systemui.kosmos.Kosmos
var Kosmos.configurationInteractor: ConfigurationInteractor by
- Kosmos.Fixture { ConfigurationInteractor(configurationRepository) }
+ Kosmos.Fixture { ConfigurationInteractorImpl(configurationRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index b27dadc9035b..3b175853de7d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -42,15 +42,6 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
val id = nextWidgetId++
val providerInfo = createAppWidgetProviderInfo(provider, user.identifier)
- fakeDatabase[id] =
- CommunalWidgetContentModel.Available(
- appWidgetId = id,
- rank = rank ?: 0,
- providerInfo = providerInfo,
- spanY = 3,
- )
- updateListFromDatabase()
-
val configured = configurator?.configureWidget(id) != false
if (configured) {
onConfigured(id, providerInfo, rank ?: -1)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryKosmos.kt
new file mode 100644
index 000000000000..4bcff5577e4b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.decor
+
+import android.content.testableContext
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.privacyDotDecorProviderFactory by
+ Kosmos.Fixture {
+ PrivacyDotDecorProviderFactory(testableContext.orCreateTestableResources.resources)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index 2dcd275f0103..f6ff4c4a057e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.deviceentry.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus
import dagger.Binds
import dagger.Module
import javax.inject.Inject
@@ -35,6 +36,9 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
private var pendingLockscreenEnabled = _isLockscreenEnabled.value
+ override val deviceUnlockStatus =
+ MutableStateFlow(DeviceUnlockStatus(isUnlocked = false, deviceUnlockSource = null))
+
override suspend fun isLockscreenEnabled(): Boolean {
_isLockscreenEnabled.value = pendingLockscreenEnabled
return isLockscreenEnabled.value
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
index 8922b2f5c5ef..e4c7df64fdc6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
@@ -19,25 +19,27 @@ package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
import com.android.systemui.flags.fakeSystemPropertiesHelper
-import com.android.systemui.flags.systemPropertiesHelper
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.trustInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
val Kosmos.deviceUnlockedInteractor by Fixture {
DeviceUnlockedInteractor(
- applicationScope = applicationCoroutineScope,
- authenticationInteractor = authenticationInteractor,
- deviceEntryRepository = deviceEntryRepository,
- trustInteractor = trustInteractor,
- faceAuthInteractor = deviceEntryFaceAuthInteractor,
- fingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
- powerInteractor = powerInteractor,
- biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
- systemPropertiesHelper = fakeSystemPropertiesHelper,
- keyguardTransitionInteractor = keyguardTransitionInteractor,
- )
+ authenticationInteractor = authenticationInteractor,
+ repository = deviceEntryRepository,
+ trustInteractor = trustInteractor,
+ faceAuthInteractor = deviceEntryFaceAuthInteractor,
+ fingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
+ powerInteractor = powerInteractor,
+ biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ systemPropertiesHelper = fakeSystemPropertiesHelper,
+ userAwareSecureSettingsRepository = userAwareSecureSettingsRepository,
+ keyguardInteractor = keyguardInteractor,
+ )
+ .apply { activateIn(testScope) }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index c41493eaa9c7..f52f039b6758 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -22,7 +22,9 @@ import android.content.res.mainResources
import android.hardware.input.fakeInputManager
import android.view.windowManager
import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper
import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource
@@ -31,17 +33,22 @@ import com.android.systemui.keyboard.shortcut.data.source.InputShortcutsSource
import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsSource
import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
+import com.android.systemui.keyboard.shortcut.ui.ShortcutCustomizationDialogStarter
+import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutCustomizationViewModel
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
import com.android.systemui.settings.displayTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
var Kosmos.shortcutHelperAppCategoriesShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { AppCategoriesShortcutsSource(windowManager, testDispatcher) }
@@ -69,10 +76,18 @@ var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by
var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by
Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) }
-val Kosmos.shortcutHelperCategoriesRepository by
+val Kosmos.shortcutCategoriesUtils by
Kosmos.Fixture {
- ShortcutHelperCategoriesRepository(
+ ShortcutCategoriesUtils(
applicationContext,
+ backgroundCoroutineContext,
+ fakeInputManager.inputManager,
+ )
+ }
+
+val Kosmos.defaultShortcutCategoriesRepository by
+ Kosmos.Fixture {
+ DefaultShortcutCategoriesRepository(
applicationCoroutineScope,
testDispatcher,
shortcutHelperSystemShortcutsSource,
@@ -82,6 +97,18 @@ val Kosmos.shortcutHelperCategoriesRepository by
shortcutHelperCurrentAppShortcutsSource,
fakeInputManager.inputManager,
shortcutHelperStateRepository,
+ shortcutCategoriesUtils,
+ )
+ }
+
+val Kosmos.customShortcutCategoriesRepository by
+ Kosmos.Fixture {
+ CustomShortcutCategoriesRepository(
+ shortcutHelperStateRepository,
+ userTracker,
+ applicationCoroutineScope,
+ testDispatcher,
+ shortcutCategoriesUtils,
)
}
@@ -108,7 +135,7 @@ val Kosmos.shortcutHelperStateInteractor by
}
val Kosmos.shortcutHelperCategoriesInteractor by
- Kosmos.Fixture { ShortcutHelperCategoriesInteractor(shortcutHelperCategoriesRepository) }
+ Kosmos.Fixture { ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) }
val Kosmos.shortcutHelperViewModel by
Kosmos.Fixture {
@@ -121,3 +148,26 @@ val Kosmos.shortcutHelperViewModel by
shortcutHelperCategoriesInteractor,
)
}
+
+val Kosmos.shortcutCustomizationDialogStarterFactory by
+ Kosmos.Fixture {
+ object : ShortcutCustomizationDialogStarter.Factory {
+ override fun create(): ShortcutCustomizationDialogStarter {
+ return ShortcutCustomizationDialogStarter(
+ shortcutCustomizationViewModelFactory,
+ systemUIDialogFactory,
+ )
+ }
+ }
+ }
+
+val Kosmos.shortcutCustomizationInteractor by Kosmos.Fixture { ShortcutCustomizationInteractor() }
+
+val Kosmos.shortcutCustomizationViewModelFactory by
+ Kosmos.Fixture {
+ object : ShortcutCustomizationViewModel.Factory {
+ override fun create(): ShortcutCustomizationViewModel {
+ return ShortcutCustomizationViewModel(shortcutCustomizationInteractor)
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 19e077c57de0..8209ee12ad9a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -87,7 +87,7 @@ class FakeKeyguardTransitionRepository(
) : this(
initInLockscreen = true,
initiallySendTransitionStepsOnStartTransition = true,
- testScope
+ testScope,
)
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
@@ -191,12 +191,12 @@ class FakeKeyguardTransitionRepository(
if (lastStep != null && lastStep.transitionState != TransitionState.FINISHED) {
sendTransitionStep(
step =
- TransitionStep(
- transitionState = TransitionState.CANCELED,
- from = lastStep.from,
- to = lastStep.to,
- value = 0f,
- )
+ TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = lastStep.from,
+ to = lastStep.to,
+ value = 0f,
+ )
)
testScheduler.runCurrent()
}
@@ -390,6 +390,18 @@ class FakeKeyguardTransitionRepository(
@FloatRange(from = 0.0, to = 1.0) value: Float,
state: TransitionState,
) = Unit
+
+ override suspend fun forceFinishCurrentTransition() {
+ _transitions.tryEmit(
+ TransitionStep(
+ _currentTransitionInfo.value.from,
+ _currentTransitionInfo.value.to,
+ 1f,
+ TransitionState.FINISHED,
+ ownerName = _currentTransitionInfo.value.ownerName,
+ )
+ )
+ }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 8c4ec4c2cb75..4a6e27331efc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -77,7 +77,7 @@ object KeyguardInteractorFactory {
repository = repository,
powerInteractor = powerInteractor,
bouncerRepository = bouncerRepository,
- configurationInteractor = ConfigurationInteractor(configurationRepository),
+ configurationInteractor = ConfigurationInteractorImpl(configurationRepository),
shadeRepository = shadeRepository,
keyguardTransitionInteractor = keyguardTransitionInteractor,
sceneInteractorProvider = { sceneInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index aa94c368e8f1..b9a831f11d23 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
@@ -26,6 +27,7 @@ val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
KeyguardTransitionInteractor(
scope = applicationCoroutineScope,
repository = keyguardTransitionRepository,
- sceneInteractor = sceneInteractor
+ sceneInteractor = sceneInteractor,
+ powerInteractor = powerInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index ddae58168201..f43841b31c2e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -1,8 +1,13 @@
package com.android.systemui.kosmos
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
+
+import com.android.systemui.util.mockito.mock
import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -36,6 +41,9 @@ var Kosmos.backgroundCoroutineContext: CoroutineContext by Fixture {
testScope.backgroundScope.coroutineContext
}
var Kosmos.mainCoroutineContext: CoroutineContext by Fixture { testScope.coroutineContext }
+var Kosmos.brightnessWarningToast: BrightnessWarningToast by Kosmos.Fixture {
+ mock<BrightnessWarningToast>()
+}
/**
* Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in
@@ -45,3 +53,5 @@ fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) =
testScope.runTest { this@runTest.testBody() }
fun Kosmos.runCurrent() = testScope.runCurrent()
+
+fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 5eaa198fb2a6..63e6eb6c0ef5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -51,6 +51,8 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
+import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.power.data.repository.fakePowerRepository
@@ -68,6 +70,8 @@ import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.shade.shadeController
import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -106,6 +110,7 @@ class KosmosJavaAdapter() {
val bouncerRepository by lazy { kosmos.bouncerRepository }
val communalRepository by lazy { kosmos.fakeCommunalSceneRepository }
val communalTransitionViewModel by lazy { kosmos.communalTransitionViewModel }
+ val activeNotificationsInteractor by lazy { kosmos.activeNotificationsInteractor }
val headsUpNotificationInteractor by lazy { kosmos.headsUpNotificationInteractor }
val seenNotificationsInteractor by lazy { kosmos.seenNotificationsInteractor }
val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
@@ -119,6 +124,7 @@ class KosmosJavaAdapter() {
val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
val statusBarStateController by lazy { kosmos.statusBarStateController }
+ val statusBarModePerDisplayRepository by lazy { kosmos.fakeStatusBarModePerDisplayRepository }
val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
val sceneInteractor by lazy { kosmos.sceneInteractor }
@@ -164,4 +170,11 @@ class KosmosJavaAdapter() {
val msdlPlayer by lazy { kosmos.fakeMSDLPlayer }
val shadeModeInteractor by lazy { kosmos.shadeModeInteractor }
val bouncerHapticHelper by lazy { kosmos.bouncerHapticPlayer }
+
+ val glanceableHubToLockscreenTransitionViewModel by lazy {
+ kosmos.glanceableHubToLockscreenTransitionViewModel
+ }
+ val lockscreenToGlanceableHubTransitionViewModel by lazy {
+ kosmos.lockscreenToGlanceableHubTransitionViewModel
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt
new file mode 100644
index 000000000000..b3be2c09c6f8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.domain.pipeline
+
+import android.content.applicationContext
+import android.os.Bundle
+import android.os.Handler
+import android.os.looper
+import androidx.media3.session.CommandButton
+import androidx.media3.session.MediaController
+import androidx.media3.session.SessionCommand
+import androidx.media3.session.SessionToken
+import com.android.systemui.Flags
+import com.android.systemui.graphics.imageLoader
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.shared.mediaLogger
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.fakeSessionTokenFactory
+import com.android.systemui.util.concurrency.execution
+import com.google.common.collect.ImmutableList
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Set up fake [Media3ActionFactory]. Note that tests using this fake will need to be
+ * annotated @RunWithLooper
+ */
+var Kosmos.media3ActionFactory: Media3ActionFactory by
+ Kosmos.Fixture {
+ if (Flags.mediaControlsButtonMedia3()) {
+ val customLayout = ImmutableList.of<CommandButton>()
+ val media3Controller =
+ mock<MediaController>().also {
+ whenever(it.customLayout).thenReturn(customLayout)
+ whenever(it.sessionExtras).thenReturn(Bundle())
+ whenever(it.isCommandAvailable(any())).thenReturn(true)
+ whenever(it.isSessionCommandAvailable(any<SessionCommand>())).thenReturn(true)
+ }
+ fakeMediaControllerFactory.setMedia3Controller(media3Controller)
+ fakeSessionTokenFactory.setMedia3SessionToken(mock<SessionToken>())
+ }
+
+ val runnableCaptor = argumentCaptor<Runnable>()
+ val handler =
+ mock<Handler> {
+ on { post(runnableCaptor.capture()) } doAnswer
+ {
+ runnableCaptor.lastValue.run()
+ true
+ }
+ }
+ Media3ActionFactory(
+ context = applicationContext,
+ imageLoader = imageLoader,
+ controllerFactory = fakeMediaControllerFactory,
+ tokenFactory = fakeSessionTokenFactory,
+ logger = mediaLogger,
+ looper = looper,
+ handler = handler,
+ bgScope = testScope,
+ execution = execution,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
index cb7750f55647..af6a0c505535 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
@@ -34,6 +34,7 @@ val Kosmos.mediaDataLoader by
fakeMediaControllerFactory,
mediaFlags,
imageLoader,
- statusBarManager
+ statusBarManager,
+ media3ActionFactory,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
index 7f8348e2ca6f..b833750a2c4a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
@@ -18,21 +18,32 @@ package com.android.systemui.media.controls.util
import android.content.Context
import android.media.session.MediaController
-import android.media.session.MediaSession
import android.media.session.MediaSession.Token
+import android.os.Looper
+import androidx.media3.session.MediaController as Media3Controller
+import androidx.media3.session.SessionToken
class FakeMediaControllerFactory(context: Context) : MediaControllerFactory(context) {
private val mediaControllersForToken = mutableMapOf<Token, MediaController>()
+ private var media3Controller: Media3Controller? = null
- override fun create(token: MediaSession.Token): android.media.session.MediaController {
+ override fun create(token: Token): MediaController {
if (token !in mediaControllersForToken) {
super.create(token)
}
return mediaControllersForToken[token]!!
}
+ override suspend fun create(token: SessionToken, looper: Looper): Media3Controller {
+ return media3Controller ?: super.create(token, looper)
+ }
+
fun setControllerForToken(token: Token, mediaController: MediaController) {
mediaControllersForToken[token] = mediaController
}
+
+ fun setMedia3Controller(mediaController: Media3Controller) {
+ media3Controller = mediaController
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeSessionTokenFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeSessionTokenFactory.kt
new file mode 100644
index 000000000000..94e0bca5675b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeSessionTokenFactory.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import android.content.Context
+import android.media.session.MediaSession.Token
+import androidx.media3.session.SessionToken
+
+class FakeSessionTokenFactory(context: Context) : SessionTokenFactory(context) {
+ private var sessionToken: SessionToken? = null
+
+ override suspend fun createTokenFromLegacy(token: Token): SessionToken {
+ return sessionToken ?: super.createTokenFromLegacy(token)
+ }
+
+ fun setMedia3SessionToken(token: SessionToken) {
+ sessionToken = token
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/SessionTokenFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/SessionTokenFactoryKosmos.kt
new file mode 100644
index 000000000000..8e473042c5d4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/SessionTokenFactoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeSessionTokenFactory by Kosmos.Fixture { FakeSessionTokenFactory(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index bda3192085ed..4ed491233f3c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -29,6 +29,7 @@ import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.inFirstPageViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.mediaInRowInLandscapeViewModelFactory
import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModelFactory
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.shade.transition.largeScreenShadeInterpolator
@@ -57,6 +58,7 @@ val Kosmos.qsFragmentComposeViewModelFactory by
largeScreenHeaderHelper,
tileSquishinessInteractor,
inFirstPageViewModel,
+ mediaInRowInLandscapeViewModelFactory,
qqsMediaHost,
qsMediaHost,
usingMediaInComposeFragment,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt
new file mode 100644
index 000000000000..a977121b3803
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.res.mainResources
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+
+val Kosmos.largeTileSpanRepository by
+ Kosmos.Fixture {
+ LargeTileSpanRepository(applicationCoroutineScope, mainResources, configurationRepository)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
index 513d4e418a41..1395b1818b30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
@@ -16,8 +16,10 @@
package com.android.systemui.qs.panels.data.repository
+import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.settings.userFileManager
import com.android.systemui.user.data.repository.userRepository
@@ -27,6 +29,8 @@ val Kosmos.qsPreferencesRepository by
userFileManager,
userRepository,
defaultLargeTilesRepository,
- testDispatcher
+ testDispatcher,
+ FakeLogBuffer.Factory.create(),
+ broadcastDispatcher,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
index 0c62d0e85ce1..8d4db8b74061 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
@@ -20,6 +20,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
+import com.android.systemui.qs.panels.data.repository.largeTileSpanRepository
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
val Kosmos.iconTilesInteractor by
@@ -28,7 +29,8 @@ val Kosmos.iconTilesInteractor by
defaultLargeTilesRepository,
currentTilesInteractor,
qsPreferencesInteractor,
+ largeTileSpanRepository,
FakeLogBuffer.Factory.create(),
- applicationCoroutineScope
+ applicationCoroutineScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
index 7613ea31c622..57aa20ae5f02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
@@ -25,7 +25,7 @@ val Kosmos.infiniteGridViewModelFactory by
override fun create(): InfiniteGridViewModel {
return InfiniteGridViewModel(
dynamicIconTilesViewModelFactory,
- qsColumnsViewModel,
+ qsColumnsViewModelFactory,
tileSquishinessViewModel,
qsResetDialogDelegateKosmos,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt
new file mode 100644
index 000000000000..f63698a3f2f9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Configuration
+import android.content.res.mainResources
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+
+val Kosmos.mediaInRowInLandscapeViewModelFactory by
+ Kosmos.Fixture {
+ object : MediaInRowInLandscapeViewModel.Factory {
+ override fun create(inLocation: Int): MediaInRowInLandscapeViewModel {
+ return MediaInRowInLandscapeViewModel(
+ mainResources,
+ configurationInteractor,
+ shadeModeInteractor,
+ mediaHostStatesManager,
+ usingMediaInComposeFragment,
+ inLocation,
+ )
+ }
+ }
+ }
+
+fun Kosmos.setConfigurationForMediaInRow(mediaInRow: Boolean) {
+ shadeRepository.setShadeLayoutWide(!mediaInRow) // media in row only in non wide
+ val config =
+ Configuration(mainResources.configuration).apply {
+ orientation =
+ if (mediaInRow) {
+ Configuration.ORIENTATION_LANDSCAPE
+ } else {
+ Configuration.ORIENTATION_PORTRAIT
+ }
+ screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+ }
+ mainResources.configuration.updateFrom(config)
+ fakeConfigurationRepository.onConfigurationChange(config)
+ runCurrent()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 5c8ca83ff2ae..0e5edb75846d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -23,7 +23,7 @@ val Kosmos.paginatedGridViewModel by
Kosmos.Fixture {
PaginatedGridViewModel(
iconTilesViewModel,
- qsColumnsViewModel,
+ qsColumnsViewModelFactory,
paginatedGridInteractor,
inFirstPageViewModel,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
index 16b2f5438797..d63b1b0b4224 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
@@ -19,4 +19,15 @@ package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.domain.interactor.qsColumnsInteractor
-val Kosmos.qsColumnsViewModel by Kosmos.Fixture { QSColumnsSizeViewModelImpl(qsColumnsInteractor) }
+val Kosmos.qsColumnsViewModelFactory by
+ Kosmos.Fixture {
+ object : QSColumnsViewModel.Factory {
+ override fun create(mediaLocation: Int?): QSColumnsViewModel {
+ return QSColumnsViewModel(
+ qsColumnsInteractor,
+ mediaInRowInLandscapeViewModelFactory,
+ mediaLocation,
+ )
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
index 20be5c675851..81beb20706db 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
@@ -27,8 +27,9 @@ val Kosmos.quickQuickSettingsViewModelFactory by
override fun create(): QuickQuickSettingsViewModel {
return QuickQuickSettingsViewModel(
currentTilesInteractor,
- qsColumnsViewModel,
+ qsColumnsViewModelFactory,
quickQuickSettingsRowInteractor,
+ mediaInRowInLandscapeViewModelFactory,
tileSquishinessViewModel,
iconTilesViewModel,
tileHapticsViewModelFactoryProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
index f66125a6087e..6787b8ebb37f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
@@ -52,7 +52,7 @@ val Kosmos.customTileViewModelFactory: QSTileViewModelFactory.Component by
)
object : QSTileViewModel {
override val state: StateFlow<QSTileState?> =
- MutableStateFlow(QSTileState.build({ null }, tileSpec.spec) {})
+ MutableStateFlow(QSTileState.build(null, tileSpec.spec) {})
override val config: QSTileConfig = config
override val isAvailable: StateFlow<Boolean> = MutableStateFlow(true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
index 5b6fd8c3bd62..ab1c1818bf80 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
@@ -44,7 +44,7 @@ private constructor(failureMetadata: FailureMetadata, subject: QSTileState?) :
check("other").that(other).isNotNull()
other ?: return
}
- check("icon").that(actual.icon()).isEqualTo(other.icon())
+ check("icon").that(actual.icon).isEqualTo(other.icon)
check("iconRes").that(actual.iconRes).isEqualTo(other.iconRes)
check("label").that(actual.label).isEqualTo(other.label)
check("activationState").that(actual.activationState).isEqualTo(other.activationState)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt
new file mode 100644
index 000000000000..75c98cb07338
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/notes/NotesTileKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.notes
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.notetask.NoteTaskModule
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsNotesTileConfig by
+ Kosmos.Fixture { NoteTaskModule.provideNotesTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 4228c3c0b110..7e6a7271c561 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -32,7 +32,6 @@ import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
@@ -78,7 +77,6 @@ val Kosmos.sceneContainerStartable by Fixture {
uiEventLogger = uiEventLogger,
sceneBackInteractor = sceneBackInteractor,
shadeSessionStorage = shadeSessionStorage,
- windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
keyguardEnabledInteractor = keyguardEnabledInteractor,
dismissCallbackRegistry = dismissCallbackRegistry,
statusBarStateController = sysuiStatusBarStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
index 88063c9b5db9..aac122c6610c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.classifier.falsingManager
import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.brightnessWarningToast
import com.android.systemui.plugins.activityStarter
import com.android.systemui.settings.brightness.BrightnessSliderController
import com.android.systemui.util.time.systemClock
@@ -35,5 +36,6 @@ var Kosmos.brightnessSliderControllerFactory by
msdlPlayer,
systemClock,
activityStarter,
+ brightnessWarningToast,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
index 718347fc3490..20e4523fda0f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
val Kosmos.notificationsShadeOverlayContentViewModel:
@@ -30,5 +31,6 @@ val Kosmos.notificationsShadeOverlayContentViewModel:
notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
+ activeNotificationsInteractor = activeNotificationsInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
index 8c218be6c982..50a19a9bc68a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar.core
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
import com.android.systemui.statusbar.window.StatusBarWindowController
class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory {
override fun create(
- statusBarWindowController: StatusBarWindowController
+ statusBarWindowController: StatusBarWindowController,
+ statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
): StatusBarInitializer = FakeStatusBarInitializer()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index 303529b7f7b0..6e990277df6b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.core
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() }
@@ -37,6 +38,7 @@ val Kosmos.multiDisplayStatusBarInitializerStore by
displayRepository,
fakeStatusBarInitializerFactory,
fakeStatusBarWindowControllerStore,
+ fakeStatusBarModeRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index 87f7142b8817..28edae7c3689 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -29,6 +29,8 @@ import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.mockNotificationShadeWindowViewController
import com.android.systemui.shade.mockShadeSurface
import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.data.repository.lightBarControllerStore
+import com.android.systemui.statusbar.data.repository.privacyDotWindowControllerStore
import com.android.systemui.statusbar.data.repository.statusBarModeRepository
import com.android.systemui.statusbar.mockNotificationRemoteInputManager
import com.android.systemui.statusbar.phone.mockAutoHideController
@@ -47,6 +49,7 @@ val Kosmos.statusBarOrchestrator by
fakeStatusBarModePerDisplayRepository,
fakeStatusBarInitializer,
fakeStatusBarWindowController,
+ applicationCoroutineScope.coroutineContext,
mockDemoModeController,
mockPluginDependencyProvider,
mockAutoHideController,
@@ -77,5 +80,7 @@ val Kosmos.multiDisplayStatusBarStarter by
statusBarInitializerStore,
statusBarWindowControllerStore,
statusBarInitializerStore,
+ privacyDotWindowControllerStore,
+ lightBarControllerStore,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt
new file mode 100644
index 000000000000..34a828176a08
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStoreKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import org.mockito.kotlin.mock
+
+val Kosmos.multiDisplayDarkIconDispatcherStore by
+ Kosmos.Fixture {
+ MultiDisplayDarkIconDispatcherStore(
+ backgroundApplicationScope = applicationCoroutineScope,
+ displayRepository = displayRepository,
+ factory = { _, _ -> mock() },
+ displayWindowPropertiesRepository = displayWindowPropertiesRepository,
+ )
+ }
+
+val Kosmos.fakeDarkIconDispatcherStore by Kosmos.Fixture { FakeDarkIconDispatcherStore() }
+
+var Kosmos.darkIconDispatcherStore by Kosmos.Fixture { fakeDarkIconDispatcherStore }
+
+val Kosmos.fakeSysUiDarkIconDispatcherStore by Kosmos.Fixture { FakeSysUiDarkIconDispatcherStore() }
+
+var Kosmos.sysUiDarkIconDispatcherStore by Kosmos.Fixture { fakeSysUiDarkIconDispatcherStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt
new file mode 100644
index 000000000000..871b277d7c82
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeDarkIconDispatcherStore.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.plugins.DarkIconDispatcher
+import org.mockito.kotlin.mock
+
+class FakeDarkIconDispatcherStore : DarkIconDispatcherStore {
+
+ private val perDisplayMocks = mutableMapOf<Int, DarkIconDispatcher>()
+
+ override val defaultDisplay: DarkIconDispatcher
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): DarkIconDispatcher {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt
new file mode 100644
index 000000000000..28232465e633
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeLightBarControllerStore.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.phone.LightBarController
+import org.mockito.kotlin.mock
+
+class FakeLightBarControllerStore : LightBarControllerStore {
+
+ val perDisplayMocks = mutableMapOf<Int, LightBarController>()
+
+ override val defaultDisplay: LightBarController
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): LightBarController {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotViewControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotViewControllerStore.kt
new file mode 100644
index 000000000000..27845aae50dc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotViewControllerStore.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.events.PrivacyDotViewController
+import org.mockito.kotlin.mock
+
+class FakePrivacyDotViewControllerStore : PrivacyDotViewControllerStore {
+ private val perDisplayMockControllers = mutableMapOf<Int, PrivacyDotViewController>()
+
+ override val defaultDisplay: PrivacyDotViewController
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): PrivacyDotViewController {
+ return perDisplayMockControllers.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotWindowControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotWindowControllerStore.kt
new file mode 100644
index 000000000000..f0aacc0dc9b7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakePrivacyDotWindowControllerStore.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.events.PrivacyDotWindowController
+import org.mockito.kotlin.mock
+
+class FakePrivacyDotWindowControllerStore : PrivacyDotWindowControllerStore {
+
+ private val perDisplayMockControllers = mutableMapOf<Int, PrivacyDotWindowController>()
+
+ override val defaultDisplay: PrivacyDotWindowController
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): PrivacyDotWindowController {
+ return perDisplayMockControllers.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 285cebb96cae..8712b02ec884 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -20,8 +20,10 @@ import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.data.model.StatusBarAppearance
import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
import dagger.Binds
import dagger.Module
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,6 +55,14 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository
override fun clearTransient() {
isTransientShown.value = false
}
+
+ override fun start() {}
+
+ override fun stop() {}
+
+ override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {}
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {}
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt
new file mode 100644
index 000000000000..4ee323ac40b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSysUiDarkIconDispatcherStore.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
+import org.mockito.kotlin.mock
+
+class FakeSysUiDarkIconDispatcherStore : SysuiDarkIconDispatcherStore {
+
+ private val perDisplayMocks = mutableMapOf<Int, SysuiDarkIconDispatcher>()
+
+ override val defaultDisplay: SysuiDarkIconDispatcher
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): SysuiDarkIconDispatcher {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSystemEventChipAnimationControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSystemEventChipAnimationControllerStore.kt
new file mode 100644
index 000000000000..fa9f1bfa9f92
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeSystemEventChipAnimationControllerStore.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.events.SystemEventChipAnimationController
+import org.mockito.kotlin.mock
+
+class FakeSystemEventChipAnimationControllerStore : SystemEventChipAnimationControllerStore {
+
+ private val perDisplayMocks = mutableMapOf<Int, SystemEventChipAnimationController>()
+
+ override val defaultDisplay: SystemEventChipAnimationController
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): SystemEventChipAnimationController {
+ return perDisplayMocks.computeIfAbsent(displayId) { mock() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt
new file mode 100644
index 000000000000..13fa3feaa453
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayScopeRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import org.mockito.kotlin.mock
+
+val Kosmos.lightBarControllerStoreImpl by
+ Kosmos.Fixture {
+ LightBarControllerStoreImpl(
+ backgroundApplicationScope = applicationCoroutineScope,
+ displayRepository = displayRepository,
+ factory = { _, _, _, _ -> mock() },
+ displayScopeRepository = displayScopeRepository,
+ statusBarModeRepositoryStore = statusBarModeRepository,
+ darkIconDispatcherStore = darkIconDispatcherStore,
+ )
+ }
+
+val Kosmos.fakeLightBarControllerStore by Kosmos.Fixture { FakeLightBarControllerStore() }
+
+var Kosmos.lightBarControllerStore by Kosmos.Fixture { fakeLightBarControllerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStoreKosmos.kt
new file mode 100644
index 000000000000..3d428a12610c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStoreKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakePrivacyDotViewControllerStore by
+ Kosmos.Fixture { FakePrivacyDotViewControllerStore() }
+
+var Kosmos.privacyDotViewControllerStore: PrivacyDotViewControllerStore by
+ Kosmos.Fixture { fakePrivacyDotViewControllerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
new file mode 100644
index 000000000000..aae32cfaafa6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import org.mockito.kotlin.mock
+
+val Kosmos.fakePrivacyDotWindowControllerStore by
+ Kosmos.Fixture { FakePrivacyDotWindowControllerStore() }
+
+val Kosmos.privacyDotWindowControllerStoreImpl by
+ Kosmos.Fixture {
+ PrivacyDotWindowControllerStoreImpl(
+ backgroundApplicationScope = applicationCoroutineScope,
+ displayRepository = displayRepository,
+ windowControllerFactory = { _, _, _, _ -> mock() },
+ displayWindowPropertiesRepository = displayWindowPropertiesRepository,
+ privacyDotViewControllerStore = privacyDotViewControllerStore,
+ viewCaptureAwareWindowManagerFactory =
+ object : ViewCaptureAwareWindowManager.Factory {
+ override fun create(
+ windowManager: WindowManager
+ ): ViewCaptureAwareWindowManager {
+ return mock()
+ }
+ },
+ )
+ }
+
+var Kosmos.privacyDotWindowControllerStore: PrivacyDotWindowControllerStore by
+ Kosmos.Fixture { fakePrivacyDotWindowControllerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
index 12db2f74197d..a5856020d6a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
@@ -16,7 +16,10 @@
package com.android.systemui.statusbar.data.repository
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import org.mockito.kotlin.mock
val Kosmos.fakeStatusBarModePerDisplayRepository by
Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() }
@@ -24,3 +27,21 @@ val Kosmos.fakeStatusBarModePerDisplayRepository by
val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by
Kosmos.Fixture { fakeStatusBarModeRepository }
val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() }
+val Kosmos.fakeStatusBarModePerDisplayRepositoryFactory by
+ Kosmos.Fixture { FakeStatusBarModePerDisplayRepositoryFactory() }
+
+val Kosmos.multiDisplayStatusBarModeRepositoryStore by
+ Kosmos.Fixture {
+ MultiDisplayStatusBarModeRepositoryStore(
+ applicationCoroutineScope,
+ fakeStatusBarModePerDisplayRepositoryFactory,
+ displayRepository,
+ )
+ }
+
+class FakeStatusBarModePerDisplayRepositoryFactory : StatusBarModePerDisplayRepositoryFactory {
+
+ override fun create(displayId: Int): StatusBarModePerDisplayRepositoryImpl {
+ return mock<StatusBarModePerDisplayRepositoryImpl>()
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreKosmos.kt
new file mode 100644
index 000000000000..f0c8f4b87ab6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.window.statusBarWindowControllerStore
+import org.mockito.kotlin.mock
+
+val Kosmos.fakeSystemEventChipAnimationControllerStore by
+ Kosmos.Fixture { FakeSystemEventChipAnimationControllerStore() }
+
+val Kosmos.systemEventChipAnimationControllerStoreImpl by
+ Kosmos.Fixture {
+ SystemEventChipAnimationControllerStoreImpl(
+ backgroundApplicationScope = applicationCoroutineScope,
+ displayRepository = displayRepository,
+ factory = { _, _, _ -> mock() },
+ displayWindowPropertiesRepository = displayWindowPropertiesRepository,
+ statusBarWindowControllerStore = statusBarWindowControllerStore,
+ statusBarContentInsetsProviderStore = statusBarContentInsetsProviderStore,
+ )
+ }
+
+var Kosmos.systemEventChipAnimationControllerStore by
+ Kosmos.Fixture { fakeSystemEventChipAnimationControllerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/FakePrivacyDotViewController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/FakePrivacyDotViewController.kt
new file mode 100644
index 000000000000..53c39a6581d2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/FakePrivacyDotViewController.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import android.view.View
+
+class FakePrivacyDotViewController : PrivacyDotViewController {
+
+ var topLeft: View? = null
+ private set
+
+ var topRight: View? = null
+ private set
+
+ var bottomLeft: View? = null
+ private set
+
+ var bottomRight: View? = null
+ private set
+
+ var isInitialized = false
+ private set
+
+ override fun stop() {}
+
+ override var currentViewState: ViewState = ViewState()
+
+ override var showingListener: PrivacyDotViewController.ShowingListener? = null
+
+ override fun setNewRotation(rot: Int) {}
+
+ override fun hideDotView(dot: View, animate: Boolean) {}
+
+ override fun showDotView(dot: View, animate: Boolean) {}
+
+ override fun updateRotations(rotation: Int, paddingTop: Int) {}
+
+ override fun setCornerSizes(state: ViewState) {}
+
+ override fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
+ this.topLeft = topLeft
+ this.topRight = topRight
+ this.bottomLeft = bottomLeft
+ this.bottomRight = bottomRight
+ isInitialized = true
+ }
+
+ override fun updateDotView(state: ViewState) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerKosmos.kt
new file mode 100644
index 000000000000..9cbc9756cf15
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockPrivacyDotViewController by Kosmos.Fixture { mock<PrivacyDotViewController>() }
+
+val Kosmos.fakePrivacyDotViewController by Kosmos.Fixture { FakePrivacyDotViewController() }
+
+var Kosmos.privacyDotViewController by Kosmos.Fixture { fakePrivacyDotViewController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
new file mode 100644
index 000000000000..c73838708a7a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import android.content.testableContext
+import android.view.layoutInflater
+import com.android.app.viewcapture.realCaptureAwareWindowManager
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.decor.privacyDotDecorProviderFactory
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.privacyDotWindowController by
+ Kosmos.Fixture {
+ PrivacyDotWindowController(
+ testableContext.displayId,
+ privacyDotViewController,
+ realCaptureAwareWindowManager,
+ layoutInflater,
+ fakeExecutor,
+ privacyDotDecorProviderFactory,
+ )
+ }
diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerKosmos.kt
index 0c4052adc3ed..186b04516fd3 100644
--- a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.server.integrity.model;
+package com.android.systemui.statusbar.events
-/** A helper class containing special indexing file constants. */
-public final class IndexingFileConstants {
- // We empirically experimented with different block sizes and identified that 50 is in the
- // optimal range of efficient computation.
- public static final int INDEXING_BLOCK_SIZE = 50;
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.data.repository.systemEventChipAnimationControllerStore
- public static final String START_INDEXING_KEY = "START_KEY";
- public static final String END_INDEXING_KEY = "END_KEY";
-}
+val Kosmos.multiDisplaySystemEventChipAnimationController by
+ Kosmos.Fixture {
+ MultiDisplaySystemEventChipAnimationController(
+ displayRepository,
+ systemEventChipAnimationControllerStore,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt
new file mode 100644
index 000000000000..92eeef97f7c4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
+import kotlinx.coroutines.flow.MutableStateFlow
+
+val Kosmos.systemStatusEventAnimationRepository: FakeSystemStatusEventAnimationRepository by
+ Kosmos.Fixture { FakeSystemStatusEventAnimationRepository() }
+
+class FakeSystemStatusEventAnimationRepository : SystemStatusEventAnimationRepository {
+ override val animationState = MutableStateFlow(SystemEventAnimationState.Idle)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt
new file mode 100644
index 000000000000..7513fead0187
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events.domain.interactor
+
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository
+
+val Kosmos.systemStatusEventAnimationInteractor by
+ Kosmos.Fixture {
+ SystemStatusEventAnimationInteractor(
+ repo = systemStatusEventAnimationRepository,
+ configurationInteractor = configurationInteractor,
+ scope = applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 76bdc0de3d7b..2ec801620212 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.data.model
import android.app.PendingIntent
import android.graphics.drawable.Icon
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
@@ -28,6 +29,7 @@ fun activeNotificationModel(
key: String,
groupKey: String? = null,
whenTime: Long = 0L,
+ isPromoted: Boolean = false,
isAmbient: Boolean = false,
isRowDismissed: Boolean = false,
isSilent: Boolean = false,
@@ -45,11 +47,13 @@ fun activeNotificationModel(
contentIntent: PendingIntent? = null,
bucket: Int = BUCKET_UNKNOWN,
callType: CallType = CallType.None,
+ promotedContent: PromotedNotificationContentModel? = null,
) =
ActiveNotificationModel(
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isPromoted = isPromoted,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -67,4 +71,5 @@ fun activeNotificationModel(
isGroupSummary = isGroupSummary,
bucket = bucket,
callType = callType,
+ promotedContent = promotedContent,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
index f7acae9846df..067193fb7aa9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt
@@ -19,8 +19,13 @@ package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.notification.collection.provider.sectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.promoted.promotedNotificationsProvider
val Kosmos.renderNotificationListInteractor by
Kosmos.Fixture {
- RenderNotificationListInteractor(activeNotificationListRepository, sectionStyleProvider)
+ RenderNotificationListInteractor(
+ activeNotificationListRepository,
+ sectionStyleProvider,
+ promotedNotificationsProvider,
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
index 94b2bdf63608..a7aa0b41a7aa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.util.settings
+package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.kosmos.Kosmos
-val Kosmos.userAwareSecureSettingsRepository by
- Kosmos.Fixture { FakeUserAwareSecureSettingsRepository() }
+val Kosmos.promotedNotificationsProvider by Kosmos.Fixture { PromotedNotificationsProviderImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt
index 282e2e859afe..cb092ced9c72 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt
@@ -24,7 +24,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
@SysUISingleton
class FakeDarkIconRepository @Inject constructor() : DarkIconRepository {
- override val darkState = MutableStateFlow(DarkChange.EMPTY)
+ private val perDisplayStates = mutableMapOf<Int, MutableStateFlow<DarkChange>>()
+
+ override fun darkState(displayId: Int) =
+ perDisplayStates.computeIfAbsent(displayId) { MutableStateFlow(DarkChange.EMPTY) }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index c3c3cce5cf68..dae66d42b2bc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -41,6 +41,7 @@ class FakeMobileConnectionRepository(
override val isGsm = MutableStateFlow(false)
override val cdmaLevel = MutableStateFlow(0)
override val primaryLevel = MutableStateFlow(0)
+ override val satelliteLevel = MutableStateFlow(0)
override val dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected)
override val dataActivityDirection =
MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index 3a7ada2e61b8..03e4c894c2f2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
+import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor
@@ -40,6 +41,7 @@ val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by
sceneContainerOcclusionInteractor,
shadeInteractor,
ongoingActivityChipsViewModel,
+ systemStatusEventAnimationInteractor,
applicationCoroutineScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/viewmodel/StatusBarUserChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/viewmodel/StatusBarUserChipViewModelKosmos.kt
new file mode 100644
index 000000000000..01175a568b3a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/viewmodel/StatusBarUserChipViewModelKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.user.domain.interactor.userSwitcherInteractor
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
+
+val Kosmos.statusBarUserChipViewModel: StatusBarUserChipViewModel by
+ Kosmos.Fixture { StatusBarUserChipViewModel(userSwitcherInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index ed335f9a1834..85d582a27faf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.yield
@@ -67,6 +68,14 @@ class FakeUserRepository @Inject constructor() : UserRepository {
)
override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
+ private val _isSecondaryUserLogoutEnabled = MutableStateFlow<Boolean>(false)
+ override val isSecondaryUserLogoutEnabled: StateFlow<Boolean> =
+ _isSecondaryUserLogoutEnabled.asStateFlow()
+
+ private val _isLogoutToSystemUserEnabled = MutableStateFlow<Boolean>(false)
+ override val isLogoutToSystemUserEnabled: StateFlow<Boolean> =
+ _isLogoutToSystemUserEnabled.asStateFlow()
+
override var mainUserId: Int = MAIN_USER_ID
override var lastSelectedNonGuestUserId: Int = mainUserId
@@ -107,6 +116,28 @@ class FakeUserRepository @Inject constructor() : UserRepository {
return _userSwitcherSettings.value.isUserSwitcherEnabled
}
+ fun setSecondaryUserLogoutEnabled(logoutEnabled: Boolean) {
+ _isSecondaryUserLogoutEnabled.value = logoutEnabled
+ }
+
+ var logOutSecondaryUserCallCount: Int = 0
+ private set
+
+ override suspend fun logOutSecondaryUser() {
+ logOutSecondaryUserCallCount++
+ }
+
+ fun setLogoutToSystemUserEnabled(logoutEnabled: Boolean) {
+ _isLogoutToSystemUserEnabled.value = logoutEnabled
+ }
+
+ var logOutToSystemUserCallCount: Int = 0
+ private set
+
+ override suspend fun logOutToSystemUser() {
+ logOutToSystemUserCallCount++
+ }
+
fun setUserInfos(infos: List<UserInfo>) {
_userInfos.value = infos
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorKosmos.kt
new file mode 100644
index 000000000000..d06e74468d3e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.user.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.userLogoutInteractor by
+ Kosmos.Fixture {
+ UserLogoutInteractor(
+ userRepository = userRepository,
+ applicationScope = applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/ExecutionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/ExecutionKosmos.kt
new file mode 100644
index 000000000000..bf66cb6e8ecc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/ExecutionKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeExecution: FakeExecution by
+ Kosmos.Fixture { FakeExecution().apply { simulateMainThread = false } }
+var Kosmos.execution: Execution by Kosmos.Fixture { fakeExecution }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSecureSettingsRepositoryKosmos.kt
index 5054e29534e9..dc10ca9fd0a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSecureSettingsRepositoryKosmos.kt
@@ -14,22 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.util.settings
+package com.android.systemui.util.settings.data.repository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.map
-class FakeUserAwareSecureSettingsRepository : UserAwareSecureSettingsRepository {
-
- private val settings = MutableStateFlow<Map<String, Boolean>>(mutableMapOf())
-
- override fun boolSettingForActiveUser(name: String, defaultValue: Boolean): Flow<Boolean> {
- return settings.map { it.getOrDefault(name, defaultValue) }
- }
-
- fun setBoolSettingForActiveUser(name: String, value: Boolean) {
- settings.value = settings.value.toMutableMap().apply { this[name] = value }
+val Kosmos.userAwareSecureSettingsRepository by
+ Kosmos.Fixture {
+ UserAwareSecureSettingsRepository(
+ fakeSettings,
+ userRepository,
+ testDispatcher,
+ backgroundCoroutineContext,
+ )
}
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSystemSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSystemSettingsRepositoryKosmos.kt
new file mode 100644
index 000000000000..ff77908a9b94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/data/repository/UserAwareSystemSettingsRepositoryKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.settings.fakeSettings
+import com.android.systemui.util.settings.repository.UserAwareSystemSettingsRepository
+
+val Kosmos.userAwareSystemSettingsRepository by
+ Kosmos.Fixture {
+ UserAwareSystemSettingsRepository(
+ fakeSettings,
+ userRepository,
+ testDispatcher,
+ backgroundCoroutineContext,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
index 1b58582a806f..ed5322ed098e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.mediaOutputDialogManager
+import com.android.systemui.util.concurrency.execution
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -53,6 +54,7 @@ val Kosmos.mediaOutputInteractor by
testScope.testScheduler,
mediaControllerRepository,
mediaControllerInteractor,
+ execution,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/FakeVolumeDialogRingerFeedbackRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/FakeVolumeDialogRingerFeedbackRepository.kt
new file mode 100644
index 000000000000..d42de1e6d0df
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/FakeVolumeDialogRingerFeedbackRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.data.repository
+
+class FakeVolumeDialogRingerFeedbackRepository : VolumeDialogRingerFeedbackRepository {
+
+ private var seenToastCount = 0
+
+ override suspend fun getToastCount(): Int {
+ return seenToastCount
+ }
+
+ override suspend fun updateToastCount(toastCount: Int) {
+ seenToastCount = toastCount
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt
new file mode 100644
index 000000000000..44371b4615df
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeVolumeDialogRingerFeedbackRepository by
+ Kosmos.Fixture { FakeVolumeDialogRingerFeedbackRepository() }
+val Kosmos.volumeDialogRingerFeedbackRepository by
+ Kosmos.Fixture { fakeVolumeDialogRingerFeedbackRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt
index 1addd91d2ec2..a494d04ec741 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt
@@ -21,6 +21,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.plugins.volumeDialogController
import com.android.systemui.volume.data.repository.audioSystemRepository
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
+import com.android.systemui.volume.dialog.ringer.data.repository.fakeVolumeDialogRingerFeedbackRepository
val Kosmos.volumeDialogRingerInteractor by
Kosmos.Fixture {
@@ -29,5 +30,6 @@ val Kosmos.volumeDialogRingerInteractor by
volumeDialogStateInteractor = volumeDialogStateInteractor,
controller = volumeDialogController,
audioSystemRepository = audioSystemRepository,
+ ringerFeedbackRepository = fakeVolumeDialogRingerFeedbackRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
index db1c01a8698c..c8ba551c518a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt
@@ -16,20 +16,24 @@
package com.android.systemui.volume.dialog.ringer.ui.viewmodel
+import android.content.applicationContext
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor
import com.android.systemui.volume.dialog.shared.volumeDialogLogger
val Kosmos.volumeDialogRingerDrawerViewModel by
Kosmos.Fixture {
VolumeDialogRingerDrawerViewModel(
+ applicationContext = applicationContext,
backgroundDispatcher = testDispatcher,
coroutineScope = applicationCoroutineScope,
interactor = volumeDialogRingerInteractor,
vibrator = vibratorHelper,
volumeDialogLogger = volumeDialogLogger,
+ visibilityInteractor = volumeDialogVisibilityInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
index 652b3ea984e7..fdeb8cef02b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto
import android.os.Handler
import android.os.looper
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
var Kosmos.mediaControllerInteractor: MediaControllerInteractor by
- Kosmos.Fixture { MediaControllerInteractorImpl(Handler(looper)) }
+ Kosmos.Fixture { MediaControllerInteractorImpl(Handler(looper), testScope.testScheduler) }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 57b58d8741da..3a0f8c0b02fb 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -306,7 +306,7 @@ constructor(
}
private fun isOnLargeScreen(): Boolean {
- return context.resources.configuration.smallestScreenWidthDp >
+ return context.applicationContext.resources.configuration.smallestScreenWidthDp >
INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
}
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
new file mode 100644
index 000000000000..be64bb1ae404
--- /dev/null
+++ b/packages/Vcn/framework-b/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_team: "trendy_team_enigma",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "framework-connectivity-b-defaults",
+ sdk_version: "module_current",
+ min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+ defaults: ["framework-module-defaults"], // This is a boot jar
+
+ srcs: [
+ "src/**/*.java",
+ ],
+}
+
+java_sdk_library {
+ name: "framework-connectivity-b",
+ defaults: [
+ "framework-connectivity-b-defaults",
+ ],
+
+ permitted_packages: [
+ "android.net.vcn",
+ ],
+
+ // TODO: b/375213246 Expose this library to Tethering module
+}
diff --git a/packages/Vcn/framework-b/api/current.txt b/packages/Vcn/framework-b/api/current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/module-lib-current.txt b/packages/Vcn/framework-b/api/module-lib-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/module-lib-removed.txt b/packages/Vcn/framework-b/api/module-lib-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/removed.txt b/packages/Vcn/framework-b/api/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/system-current.txt b/packages/Vcn/framework-b/api/system-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/api/system-removed.txt b/packages/Vcn/framework-b/api/system-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/packages/Vcn/framework-b/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java b/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java
new file mode 100644
index 000000000000..fb5e15386cc7
--- /dev/null
+++ b/packages/Vcn/framework-b/src/android/net/vcn/Placeholder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+/**
+ * Placeholder class so new framework-vcn isn't empty
+ *
+ * @hide
+ */
+// This class will be removed once source code is migrated
+public class Placeholder {}
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
new file mode 100644
index 000000000000..a462297c07af
--- /dev/null
+++ b/packages/Vcn/service-b/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_team: "trendy_team_enigma",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "service-connectivity-b-pre-jarjar",
+ sdk_version: "system_server_current",
+ min_sdk_version: "35", // TODO: Make it Android 25Q2 when this is included in mainline
+ defaults: ["framework-system-server-module-defaults"], // This is a system server jar
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ // TODO: b/375213246 Expose this library to Tethering module
+ visibility: [
+ "//frameworks/base/services",
+ ],
+}
diff --git a/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java b/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java
new file mode 100644
index 000000000000..e79914531c38
--- /dev/null
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Placeholder.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+/**
+ * Placeholder class so new service-vcn isn't empty
+ *
+ * @hide
+ */
+// This class will be removed once source code is migrated
+public class Placeholder {}
diff --git a/packages/overlays/Android.bp b/packages/overlays/Android.bp
index 5075f6322d1b..44abb9ffb34f 100644
--- a/packages/overlays/Android.bp
+++ b/packages/overlays/Android.bp
@@ -21,6 +21,7 @@ package {
phony {
name: "frameworks-base-overlays",
+ product_specific: true,
required: [
"DisplayCutoutEmulationCornerOverlay",
"DisplayCutoutEmulationDoubleOverlay",
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index b3f78ab30021..0c2ce8dcb698 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -279,6 +279,15 @@ cc_defaults {
shared_libs: [
"liblog",
],
+ visibility: ["//visibility:private"],
+}
+
+cc_library_host_shared {
+ name: "libravenwood_initializer",
+ defaults: ["ravenwood_jni_defaults"],
+ srcs: [
+ "runtime-jni/ravenwood_initializer.cpp",
+ ],
}
// We need this as a separate library because we need to overload the
@@ -301,7 +310,6 @@ cc_library_host_shared {
"libutils",
"libcutils",
],
- visibility: ["//frameworks/base"],
}
// For collecting the *stats.csv files in a known directory under out/host/linux-x86/testcases/.
@@ -368,6 +376,7 @@ filegroup {
":ravenwood-empty-res",
":framework-platform-compat-config",
":services-platform-compat-config",
+ "texts/ravenwood-build.prop",
],
device_first_srcs: [
":apex_icu.dat",
@@ -659,6 +668,7 @@ android_ravenwood_libgroup {
],
jni_libs: [
// Libraries has to be loaded in the following order
+ "libravenwood_initializer",
"libravenwood_sysprop",
"libravenwood_runtime",
"libandroid_runtime",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index d20773844df3..99fc31b258e9 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -214,7 +214,8 @@ java_genrule {
java_genrule {
name: "services.core.ravenwood",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
+ // This is used by unit tests too (so tests will be able to access HSG-processed implementation)
+ // so it's visible to all.
cmd: "cp $(in) $(out)",
srcs: [
":services.core.ravenwood-base{ravenwood.jar}",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index a1243e37003e..cb54e9f56c0c 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -113,11 +113,11 @@
"host": true
},
{
- "name": "FrameworksMockingServicesTestsRavenwood",
+ "name": "FrameworksServicesTestsRavenwood_Compat",
"host": true
},
{
- "name": "FrameworksServicesTestsRavenwood",
+ "name": "FrameworksServicesTestsRavenwood_Uri",
"host": true
},
{
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 66a6890a23b0..869d854f7b23 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -26,12 +26,10 @@ import android.annotation.Nullable;
import android.os.Bundle;
import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
import android.platform.test.annotations.internal.InnerRunner;
-import android.platform.test.ravenwood.RavenwoodTestStats.Result;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
-import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.Runner;
@@ -171,10 +169,11 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
final var notifier = new RavenwoodRunNotifier(realNotifier);
final var description = getDescription();
+ RavenwoodTestStats.getInstance().attachToRunNotifier(notifier);
+
if (mRealRunner instanceof ClassSkippingTestRunner) {
- mRealRunner.run(notifier);
Log.i(TAG, "onClassSkipped: description=" + description);
- RavenwoodTestStats.getInstance().onClassSkipped(description);
+ mRealRunner.run(notifier);
return;
}
@@ -205,7 +204,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
if (!skipRunnerHook) {
try {
- RavenwoodTestStats.getInstance().onClassFinished(description);
mState.exitTestClass();
} catch (Throwable th) {
notifier.reportAfterTestFailure(th);
@@ -295,8 +293,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
// method-level annotations here.
if (scope == Scope.Instance && order == Order.Outer) {
if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(description, true)) {
- RavenwoodTestStats.getInstance().onTestFinished(
- classDescription, description, Result.Skipped);
return false;
}
}
@@ -317,16 +313,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
// End of a test method.
mState.exitTestMethod();
- final Result result;
- if (th == null) {
- result = Result.Passed;
- } else if (th instanceof AssumptionViolatedException) {
- result = Result.Skipped;
- } else {
- result = Result.Failed;
- }
-
- RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result);
}
// If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
index d29b93c0c171..a208d6dce2ce 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
@@ -40,7 +40,7 @@ public final class RavenwoodNativeLoader {
* See frameworks/base/core/jni/platform/host/HostRuntime.cpp
*/
private static final Class<?>[] sLibandroidClasses = {
- android.util.Log.class,
+// android.util.Log.class, // Not using native log: b/377377826
android.os.Parcel.class,
android.os.Binder.class,
android.os.SystemProperties.class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 0f163524d2fe..678a97be60a2 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -22,7 +22,6 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_R
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
@@ -31,16 +30,21 @@ import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.AppCompatCallbacks;
import android.app.Instrumentation;
import android.app.ResourcesManager;
import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.Process_ravenwood;
import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.provider.DeviceConfig_host;
import android.system.ErrnoException;
@@ -50,12 +54,15 @@ import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.hoststubgen.hosthelper.HostTestUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.RuntimeInit;
import com.android.ravenwood.RavenwoodRuntimeNative;
+import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.RavenwoodCommonUtils;
import com.android.ravenwood.common.RavenwoodRuntimeException;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
+import com.android.server.compat.PlatformCompat;
import org.junit.runner.Description;
@@ -84,10 +91,9 @@ public class RavenwoodRuntimeEnvironmentController {
}
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+ private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
- private static final String RAVENWOOD_BUILD_PROP =
- getRavenwoodRuntimePath() + "ravenwood-data/build.prop";
/**
* When enabled, attempt to dump all thread stacks just before we hit the
@@ -137,23 +143,61 @@ public class RavenwoodRuntimeEnvironmentController {
return res;
}
+ private static final Object sInitializationLock = new Object();
+
+ @GuardedBy("sInitializationLock")
+ private static boolean sInitialized = false;
+
+ @GuardedBy("sInitializationLock")
+ private static Throwable sExceptionFromGlobalInit;
+
private static RavenwoodAwareTestRunner sRunner;
private static RavenwoodSystemProperties sProps;
- private static boolean sInitialized = false;
/**
* Initialize the global environment.
*/
public static void globalInitOnce() {
- if (sInitialized) {
- return;
+ synchronized (sInitializationLock) {
+ if (!sInitialized) {
+ // globalInitOnce() is called from class initializer, which cause
+ // this method to be called recursively,
+ sInitialized = true;
+
+ // This is the first call.
+ try {
+ globalInitInner();
+ } catch (Throwable th) {
+ Log.e(TAG, "globalInit() failed", th);
+
+ sExceptionFromGlobalInit = th;
+ throw th;
+ }
+ } else {
+ // Subsequent calls. If the first call threw, just throw the same error, to prevent
+ // the test from running.
+ if (sExceptionFromGlobalInit != null) {
+ Log.e(TAG, "globalInit() failed re-throwing the same exception",
+ sExceptionFromGlobalInit);
+
+ SneakyThrow.sneakyThrow(sExceptionFromGlobalInit);
+ }
+ }
+ }
+ }
+
+ private static void globalInitInner() {
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH"));
}
- sInitialized = true;
+
+ // Some process-wide initialization. (maybe redirect stdout/stderr)
+ RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
// We haven't initialized liblog yet, so directly write to System.out here.
- RavenwoodCommonUtils.log(TAG, "globalInit()");
+ RavenwoodCommonUtils.log(TAG, "globalInitInner()");
- // Load libravenwood_sysprop first
+ // Load libravenwood_sysprop before other libraries that may use SystemProperties.
var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME);
System.load(libProp);
RavenwoodRuntimeNative.reloadNativeLibrary(libProp);
@@ -162,7 +206,7 @@ public class RavenwoodRuntimeEnvironmentController {
System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
// Do the basic set up for the android sysprops.
- RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
+ RavenwoodSystemProperties.initialize();
setSystemProperties(null);
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
@@ -199,7 +243,7 @@ public class RavenwoodRuntimeEnvironmentController {
*/
public static void init(RavenwoodAwareTestRunner runner) {
if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.i(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
+ Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
}
if (sRunner == runner) {
return;
@@ -223,7 +267,9 @@ public class RavenwoodRuntimeEnvironmentController {
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
}
- android.os.Process.init$ravenwood(config.mUid, config.mPid);
+ RavenwoodRuntimeState.sUid = config.mUid;
+ RavenwoodRuntimeState.sPid = config.mPid;
+ RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel;
sOriginalIdentityToken = Binder.clearCallingIdentity();
reinit();
setSystemProperties(config.mSystemProperties);
@@ -290,6 +336,8 @@ public class RavenwoodRuntimeEnvironmentController {
RavenwoodSystemServer.init(config);
+ initializeCompatIds(config);
+
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout = sTimeoutExecutor.schedule(
RavenwoodRuntimeEnvironmentController::dumpStacks,
@@ -305,12 +353,37 @@ public class RavenwoodRuntimeEnvironmentController {
Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
}
+ private static void initializeCompatIds(RavenwoodConfig config) {
+ // Set up compat-IDs for the app side.
+ // TODO: Inside the system server, all the compat-IDs should be enabled,
+ // Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in
+ // SystemServer.
+
+ // Compat framework only uses the package name and the target SDK level.
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = config.mTargetPackageName;
+ appInfo.targetSdkVersion = config.mTargetSdkLevel;
+
+ PlatformCompat platformCompat = null;
+ try {
+ platformCompat = (PlatformCompat) ServiceManager.getServiceOrThrow(
+ Context.PLATFORM_COMPAT_SERVICE);
+ } catch (ServiceNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ var disabledChanges = platformCompat.getDisabledChanges(appInfo);
+ var loggableChanges = platformCompat.getLoggableChanges(appInfo);
+
+ AppCompatCallbacks.install(disabledChanges, loggableChanges);
+ }
+
/**
* De-initialize.
*/
public static void reset() {
if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.i(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
+ Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
}
if (sRunner == null) {
throw new RavenwoodRuntimeException("Internal error: reset() already called");
@@ -350,8 +423,8 @@ public class RavenwoodRuntimeEnvironmentController {
if (sOriginalIdentityToken != -1) {
Binder.restoreCallingIdentity(sOriginalIdentityToken);
}
- android.os.Process.reset$ravenwood();
-
+ RavenwoodRuntimeState.reset();
+ Process_ravenwood.reset();
DeviceConfig_host.reset();
try {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index f198a08a50e3..438a2bfa7a14 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -17,7 +17,9 @@
package android.platform.test.ravenwood;
import android.content.ClipboardManager;
+import android.content.Context;
import android.hardware.SerialManager;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.ravenwood.example.BlueManager;
import android.ravenwood.example.RedManager;
@@ -27,6 +29,8 @@ import android.util.ArraySet;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.PlatformCompatNative;
import com.android.server.utils.TimingsTraceAndSlog;
import java.util.List;
@@ -65,6 +69,14 @@ public class RavenwoodSystemServer {
private static SystemServiceManager sServiceManager;
public static void init(RavenwoodConfig config) {
+ // Always start PlatformCompat, regardless of the requested services.
+ // PlatformCompat is not really a SystemService, so it won't receive boot phases / etc.
+ // This initialization code is copied from SystemServer.java.
+ PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
+ new PlatformCompatNative(platformCompat));
+
// Avoid overhead if no services required
if (config.mServicesRequired.isEmpty()) return;
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index 016de8e45291..787058545fed 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -18,6 +18,9 @@ package android.platform.test.ravenwood;
import android.util.Log;
import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
import java.io.File;
import java.io.IOException;
@@ -27,7 +30,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -39,7 +42,7 @@ import java.util.Map;
*/
public class RavenwoodTestStats {
private static final String TAG = "RavenwoodTestStats";
- private static final String HEADER = "Module,Class,ClassDesc,Passed,Failed,Skipped";
+ private static final String HEADER = "Module,Class,OuterClass,Passed,Failed,Skipped";
private static RavenwoodTestStats sInstance;
@@ -66,7 +69,7 @@ public class RavenwoodTestStats {
private final PrintWriter mOutputWriter;
private final String mTestModuleName;
- public final Map<Description, Map<Description, Result>> mStats = new HashMap<>();
+ public final Map<String, Map<String, Result>> mStats = new LinkedHashMap<>();
/** Ctor */
public RavenwoodTestStats() {
@@ -115,75 +118,129 @@ public class RavenwoodTestStats {
return cwd.getName();
}
- private void addResult(Description classDescription, Description methodDescription,
+ private void addResult(String className, String methodName,
Result result) {
- mStats.compute(classDescription, (classDesc, value) -> {
+ mStats.compute(className, (className_, value) -> {
if (value == null) {
- value = new HashMap<>();
+ value = new LinkedHashMap<>();
+ }
+ // If the result is already set, don't overwrite it.
+ if (!value.containsKey(methodName)) {
+ value.put(methodName, result);
}
- value.put(methodDescription, result);
return value;
});
}
/**
- * Call it when a test class is skipped.
- */
- public void onClassSkipped(Description classDescription) {
- addResult(classDescription, Description.EMPTY, Result.Skipped);
- onClassFinished(classDescription);
- }
-
- /**
* Call it when a test method is finished.
*/
- public void onTestFinished(Description classDescription, Description testDescription,
- Result result) {
- addResult(classDescription, testDescription, result);
+ private void onTestFinished(String className, String testName, Result result) {
+ addResult(className, testName, result);
}
/**
- * Call it when a test class is finished.
+ * Dump all the results and clear it.
*/
- public void onClassFinished(Description classDescription) {
- int passed = 0;
- int skipped = 0;
- int failed = 0;
- var stats = mStats.get(classDescription);
- if (stats == null) {
- return;
- }
- for (var e : stats.values()) {
- switch (e) {
- case Passed: passed++; break;
- case Skipped: skipped++; break;
- case Failed: failed++; break;
+ private void dumpAllAndClear() {
+ for (var entry : mStats.entrySet()) {
+ int passed = 0;
+ int skipped = 0;
+ int failed = 0;
+ var className = entry.getKey();
+
+ for (var e : entry.getValue().values()) {
+ switch (e) {
+ case Passed:
+ passed++;
+ break;
+ case Skipped:
+ skipped++;
+ break;
+ case Failed:
+ failed++;
+ break;
+ }
}
+
+ mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n",
+ mTestModuleName, className, getOuterClassName(className),
+ passed, failed, skipped);
}
+ mOutputWriter.flush();
+ mStats.clear();
+ }
- var testClass = extractTestClass(classDescription);
+ private static String getOuterClassName(String className) {
+ // Just delete the '$', because I'm not sure if the className we get here is actaully a
+ // valid class name that does exist. (it might have a parameter name, etc?)
+ int p = className.indexOf('$');
+ if (p < 0) {
+ return className;
+ }
+ return className.substring(0, p);
+ }
- mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n",
- mTestModuleName, (testClass == null ? "?" : testClass.getCanonicalName()),
- classDescription, passed, failed, skipped);
- mOutputWriter.flush();
+ public void attachToRunNotifier(RunNotifier notifier) {
+ notifier.addListener(mRunListener);
}
- /**
- * Try to extract the class from a description, which is needed because
- * ParameterizedAndroidJunit4's description doesn't contain a class.
- */
- private Class<?> extractTestClass(Description desc) {
- if (desc.getTestClass() != null) {
- return desc.getTestClass();
+ private final RunListener mRunListener = new RunListener() {
+ @Override
+ public void testSuiteStarted(Description description) {
+ Log.d(TAG, "testSuiteStarted: " + description);
}
- // Look into the children.
- for (var child : desc.getChildren()) {
- var fromChild = extractTestClass(child);
- if (fromChild != null) {
- return fromChild;
- }
+
+ @Override
+ public void testSuiteFinished(Description description) {
+ Log.d(TAG, "testSuiteFinished: " + description);
}
- return null;
- }
+
+ @Override
+ public void testRunStarted(Description description) {
+ Log.d(TAG, "testRunStarted: " + description);
+ }
+
+ @Override
+ public void testRunFinished(org.junit.runner.Result result) {
+ Log.d(TAG, "testRunFinished: " + result);
+
+ dumpAllAndClear();
+ }
+
+ @Override
+ public void testStarted(Description description) {
+ Log.d(TAG, " testStarted: " + description);
+ }
+
+ @Override
+ public void testFinished(Description description) {
+ Log.d(TAG, " testFinished: " + description);
+
+ // Send "Passed", but if there's already another result sent for this, this won't
+ // override it.
+ onTestFinished(description.getClassName(), description.getMethodName(), Result.Passed);
+ }
+
+ @Override
+ public void testFailure(Failure failure) {
+ Log.d(TAG, " testFailure: " + failure);
+
+ var description = failure.getDescription();
+ onTestFinished(description.getClassName(), description.getMethodName(), Result.Failed);
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ Log.d(TAG, " testAssumptionFailure: " + failure);
+ var description = failure.getDescription();
+ onTestFinished(description.getClassName(), description.getMethodName(), Result.Skipped);
+ }
+
+ @Override
+ public void testIgnored(Description description) {
+ Log.d(TAG, " testIgnored: " + description);
+ onTestFinished(description.getClassName(), description.getMethodName(), Result.Skipped);
+ }
+ };
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 37b0abcceede..3ed8b0a748e1 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -16,13 +16,13 @@
package android.platform.test.ravenwood;
import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.SYSTEM;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
+import android.os.Build;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -60,16 +60,13 @@ public final class RavenwoodConfig {
* Unless the test author requests differently, run as "nobody", and give each collection of
* tests its own unique PID.
*/
- int mUid = NOBODY_UID;
+ int mUid = FIRST_APPLICATION_UID;
int mPid = sNextPid.getAndIncrement();
String mTestPackageName;
String mTargetPackageName;
- int mMinSdkLevel;
- int mTargetSdkLevel;
-
- boolean mProvideMainThread = false;
+ int mTargetSdkLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
@@ -107,20 +104,18 @@ public final class RavenwoodConfig {
}
/**
- * Configure the identity of this process to be the system UID for the duration of the
- * test. Has no effect on non-Ravenwood environments.
+ * @deprecated no longer used. We always use an app UID.
*/
+ @Deprecated
public Builder setProcessSystem() {
- mConfig.mUid = SYSTEM_UID;
return this;
}
/**
- * Configure the identity of this process to be an app UID for the duration of the
- * test. Has no effect on non-Ravenwood environments.
+ * @deprecated no longer used. We always use an app UID.
*/
+ @Deprecated
public Builder setProcessApp() {
- mConfig.mUid = FIRST_APPLICATION_UID;
return this;
}
@@ -143,14 +138,6 @@ public final class RavenwoodConfig {
}
/**
- * Configure the min SDK level of the test.
- */
- public Builder setMinSdkLevel(int sdkLevel) {
- mConfig.mMinSdkLevel = sdkLevel;
- return this;
- }
-
- /**
* Configure the target SDK level of the test.
*/
public Builder setTargetSdkLevel(int sdkLevel) {
@@ -159,14 +146,10 @@ public final class RavenwoodConfig {
}
/**
- * Configure a "main" thread to be available for the duration of the test, as defined
- * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
- *
- * @deprecated
+ * @deprecated no longer used. Main thread is always available.
*/
@Deprecated
public Builder setProvideMainThread(boolean provideMainThread) {
- mConfig.mProvideMainThread = provideMainThread;
return this;
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 3d6ac0f37050..bfa3802ce583 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -112,20 +112,18 @@ public final class RavenwoodRule implements TestRule {
}
/**
- * Configure the identity of this process to be the system UID for the duration of the
- * test. Has no effect on non-Ravenwood environments.
+ * @deprecated no longer used. We always use an app UID.
*/
+ @Deprecated
public Builder setProcessSystem() {
- mBuilder.setProcessSystem();
return this;
}
/**
- * Configure the identity of this process to be an app UID for the duration of the
- * test. Has no effect on non-Ravenwood environments.
+ * @deprecated no longer used. We always use an app UID.
*/
+ @Deprecated
public Builder setProcessApp() {
- mBuilder.setProcessApp();
return this;
}
@@ -139,14 +137,10 @@ public final class RavenwoodRule implements TestRule {
}
/**
- * Configure a "main" thread to be available for the duration of the test, as defined
- * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
- *
- * @deprecated
+ * @deprecated no longer used. Main thread is always available.
*/
@Deprecated
public Builder setProvideMainThread(boolean provideMainThread) {
- mBuilder.setProvideMainThread(provideMainThread);
return this;
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 9bc45bee1775..3e4619f55c6d 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -16,21 +16,30 @@
package android.platform.test.ravenwood;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
+import android.util.Log;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class RavenwoodSystemProperties {
private static final String TAG = "RavenwoodSystemProperties";
+ /** We pull in propeties from this file. */
+ private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";
+
+ /** This is the actual build.prop we use to build the device (contents depends on lunch). */
+ private static final String DEVICE_BUILD_PROP = "ravenwood-data/build.prop";
+
+ /** The default values. */
private static final Map<String, String> sDefaultValues = new HashMap<>();
private static final String[] PARTITIONS = {
@@ -43,52 +52,54 @@ public class RavenwoodSystemProperties {
"vendor_dlkm",
};
- /**
- * More info about property file loading: system/core/init/property_service.cpp
- * In the following logic, the only partition we would need to consider is "system",
- * since we only read from system-build.prop
- */
- static void initialize(String propFile) {
- // Load all properties from build.prop
+ private static Map<String, String> readProperties(String propFile) {
+ // Use an ordered map just for cleaner dump log.
+ final Map<String, String> ret = new LinkedHashMap<>();
try {
Files.readAllLines(Path.of(propFile)).stream()
.map(String::trim)
.filter(s -> !s.startsWith("#"))
.map(s -> s.split("\\s*=\\s*", 2))
.filter(a -> a.length == 2)
- .forEach(a -> sDefaultValues.put(a[0], a[1]));
+ .forEach(a -> ret.put(a[0], a[1]));
} catch (IOException e) {
throw new RuntimeException(e);
}
+ return ret;
+ }
- // If ro.product.${name} is not set, derive from ro.product.${partition}.${name}
- // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist*
- for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
- final String key;
- if (entry.getKey().startsWith("ro.product.system.")) {
- var name = entry.getKey().substring(18);
- key = "ro.product." + name;
-
- } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) {
- var name = entry.getKey().substring(22);
- key = "ro.product.cpu." + name;
+ /**
+ * Load default sysprops from {@link #RAVENWOOD_BUILD_PROP}. We also pull in
+ * certain properties from the acutual device's build.prop {@link #DEVICE_BUILD_PROP} too.
+ *
+ * More info about property file loading: system/core/init/property_service.cpp
+ * In the following logic, the only partition we would need to consider is "system",
+ * since we only read from system-build.prop
+ */
+ static void initialize() {
+ var path = getRavenwoodRuntimePath();
+ var ravenwoodProps = readProperties(path + RAVENWOOD_BUILD_PROP);
+ var deviceProps = readProperties(path + DEVICE_BUILD_PROP);
+
+ Log.i(TAG, "Default system properties:");
+ ravenwoodProps.forEach((key, origValue) -> {
+ final String value;
+
+ // If a value starts with "$$$", then this is a reference to the device-side value.
+ if (origValue.startsWith("$$$")) {
+ var deviceKey = origValue.substring(3);
+ var deviceValue = deviceProps.get(deviceKey);
+ if (deviceValue == null) {
+ throw new RuntimeException("Failed to initialize system properties. Key '"
+ + deviceKey + "' doesn't exist in the device side build.prop");
+ }
+ value = deviceValue;
} else {
- continue;
- }
- if (!sDefaultValues.containsKey(key)) {
- sDefaultValues.put(key, entry.getValue());
+ value = origValue;
}
- }
-
- // Some other custom values
- sDefaultValues.put("ro.board.first_api_level", "1");
- sDefaultValues.put("ro.product.first_api_level", "1");
- sDefaultValues.put("ro.soc.manufacturer", "Android");
- sDefaultValues.put("ro.soc.model", "Ravenwood");
- sDefaultValues.put(RAVENWOOD_SYSPROP, "1");
-
- // Log all values
- sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value));
+ Log.i(TAG, key + "=" + value);
+ sDefaultValues.put(key, value);
+ });
// Copy ro.product.* and ro.build.* to all partitions, just in case
// We don't want to log these because these are just a lot of duplicate values
@@ -104,6 +115,13 @@ public class RavenwoodSystemProperties {
}
}
}
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ // Dump all properties for local debugging.
+ Log.v(TAG, "All system properties:");
+ for (var key : sDefaultValues.keySet().stream().sorted().toList()) {
+ Log.v(TAG, "" + key + "=" + sDefaultValues.get(key));
+ }
+ }
}
private volatile boolean mIsImmutable;
diff --git a/ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java b/ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java
new file mode 100644
index 000000000000..3c6a4d78d1d9
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.util.Pair;
+
+public class Process_ravenwood {
+
+ private static volatile ThreadLocal<Pair<Integer, Boolean>> sThreadPriority;
+
+ static {
+ reset();
+ }
+
+ public static void reset() {
+ // Reset the thread local variable
+ sThreadPriority = ThreadLocal.withInitial(
+ () -> Pair.create(Process.THREAD_PRIORITY_DEFAULT, true));
+ }
+
+ /**
+ * Called by {@link Process#setThreadPriority(int, int)}
+ */
+ public static void setThreadPriority(int tid, int priority) {
+ if (Process.myTid() == tid) {
+ boolean backgroundOk = sThreadPriority.get().second;
+ if (priority >= Process.THREAD_PRIORITY_BACKGROUND && !backgroundOk) {
+ throw new IllegalArgumentException(
+ "Priority " + priority + " blocked by setCanSelfBackground()");
+ }
+ sThreadPriority.set(Pair.create(priority, backgroundOk));
+ } else {
+ throw new UnsupportedOperationException(
+ "Cross-thread priority management not yet available in Ravenwood");
+ }
+ }
+
+ /**
+ * Called by {@link Process#setCanSelfBackground(boolean)}
+ */
+ public static void setCanSelfBackground(boolean backgroundOk) {
+ int priority = sThreadPriority.get().first;
+ sThreadPriority.set(Pair.create(priority, backgroundOk));
+ }
+
+ /**
+ * Called by {@link Process#getThreadPriority(int)}
+ */
+ public static int getThreadPriority(int tid) {
+ if (Process.myTid() == tid) {
+ return sThreadPriority.get().first;
+ } else {
+ throw new UnsupportedOperationException(
+ "Cross-thread priority management not yet available in Ravenwood");
+ }
+ }
+}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_host.java b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
index d232ef2076be..c85bd23db893 100644
--- a/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
+++ b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
@@ -18,6 +18,7 @@ package android.util;
import android.util.Log.Level;
import com.android.internal.os.RuntimeInit;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.PrintStream;
@@ -35,6 +36,9 @@ public class Log_host {
}
public static int println_native(int bufID, int priority, String tag, String msg) {
+ if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) {
+ return msg.length(); // No verbose logging.
+ }
final String buffer;
switch (bufID) {
case Log.LOG_ID_MAIN: buffer = "main"; break;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java
deleted file mode 100644
index c18c307ad1e3..000000000000
--- a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayContainer_host.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import java.util.Arrays;
-import java.util.HashMap;
-
-public class LongArrayContainer_host {
- private static final HashMap<Long, long[]> sInstances = new HashMap<>();
- private static long sNextId = 1;
-
- public static long native_init(int arrayLength) {
- long[] array = new long[arrayLength];
- long instanceId = sNextId++;
- sInstances.put(instanceId, array);
- return instanceId;
- }
-
- static long[] getInstance(long instanceId) {
- return sInstances.get(instanceId);
- }
-
- public static void native_setValues(long instanceId, long[] values) {
- System.arraycopy(values, 0, getInstance(instanceId), 0, values.length);
- }
-
- public static void native_getValues(long instanceId, long[] values) {
- System.arraycopy(getInstance(instanceId), 0, values, 0, values.length);
- }
-
- public static boolean native_combineValues(long instanceId, long[] array, int[] indexMap) {
- long[] values = getInstance(instanceId);
-
- boolean nonZero = false;
- Arrays.fill(array, 0);
-
- for (int i = 0; i < values.length; i++) {
- int index = indexMap[i];
- if (index < 0 || index >= array.length) {
- throw new IndexOutOfBoundsException("Index " + index + " is out of bounds: [0, "
- + (array.length - 1) + "]");
- }
- if (values[i] != 0) {
- array[index] += values[i];
- nonZero = true;
- }
- }
- return nonZero;
- }
-}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java
index e12ff240c4d9..b65668b67e03 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java
@@ -23,14 +23,6 @@ public class RavenwoodEnvironment_host {
}
/**
- * Called from {@link RavenwoodEnvironment#ensureRavenwoodInitialized()}.
- */
- public static void ensureRavenwoodInitialized() {
- // Initialization is now done by RavenwoodAwareTestRunner.
- // Should we remove it?
- }
-
- /**
* Called from {@link RavenwoodEnvironment#getRavenwoodRuntimePath()}.
*/
public static String getRavenwoodRuntimePath(RavenwoodEnvironment env) {
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
index c94ef31a5e5e..02981713674d 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -16,6 +16,7 @@
package android.system;
import com.android.ravenwood.RavenwoodRuntimeNative;
+import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.JvmWorkaround;
import java.io.FileDescriptor;
@@ -97,4 +98,16 @@ public final class Os {
public static void setenv(String name, String value, boolean overwrite) throws ErrnoException {
RavenwoodRuntimeNative.setenv(name, value, overwrite);
}
+
+ public static int getpid() {
+ return RavenwoodRuntimeState.sPid;
+ }
+
+ public static int getuid() {
+ return RavenwoodRuntimeState.sUid;
+ }
+
+ public static int gettid() {
+ return RavenwoodRuntimeNative.gettid();
+ }
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index f13189f6f8be..7b940b423b69 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -58,6 +58,8 @@ public class RavenwoodRuntimeNative {
public static native void clearSystemProperties();
+ public static native int gettid();
+
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeState.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeState.java
new file mode 100644
index 000000000000..175e020d61d6
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeState.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwood;
+
+public class RavenwoodRuntimeState {
+ // This must match VMRuntime.SDK_VERSION_CUR_DEVELOPMENT.
+ public static final int CUR_DEVELOPMENT = 10000;
+
+ public static volatile int sUid;
+ public static volatile int sPid;
+ public static volatile int sTargetSdkLevel;
+
+ static {
+ reset();
+ }
+
+ public static void reset() {
+ sUid = -1;
+ sPid = -1;
+ sTargetSdkLevel = CUR_DEVELOPMENT;
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
index ba89f71dde8a..eaadac6a8b92 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
@@ -19,6 +19,7 @@ package dalvik.system;
// The original is here:
// $ANDROID_BUILD_TOP/libcore/libart/src/main/java/dalvik/system/VMRuntime.java
+import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.JvmWorkaround;
import java.lang.reflect.Array;
@@ -52,4 +53,8 @@ public class VMRuntime {
public long addressOf(Object obj) {
return JvmWorkaround.getInstance().addressOf(obj);
}
+
+ public int getTargetSdkVersion() {
+ return RavenwoodRuntimeState.sTargetSdkLevel;
+ }
}
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
new file mode 100644
index 000000000000..89fb7c3c3510
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * This file is compiled into a single SO file, which we load at the very first.
+ * We can do process-wide initialization here.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "jni_helper.h"
+
+static void maybeRedirectLog() {
+ auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
+ if (ravenwoodLogOut == NULL) {
+ return;
+ }
+ ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
+
+ // Redirect stdin / stdout to /dev/tty.
+ int ttyFd = open(ravenwoodLogOut, O_WRONLY | O_APPEND);
+ if (ttyFd == -1) {
+ ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
+ strerror(errno));
+ return;
+ }
+ dup2(ttyFd, 1);
+ dup2(ttyFd, 2);
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+ ALOGI("%s: JNI_OnLoad", __FILE__);
+
+ maybeRedirectLog();
+ return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 2a3c26ed3ea3..c1993f691686 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -17,6 +17,7 @@
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <unistd.h>
#include <utils/misc.h>
@@ -173,22 +174,10 @@ static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaVal
throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0));
}
-static void maybeRedirectLog() {
- auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
- if (ravenwoodLogOut == NULL) {
- return;
- }
- ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
-
- // Redirect stdin / stdout to /dev/tty.
- int ttyFd = open(ravenwoodLogOut, O_WRONLY);
- if (ttyFd == -1) {
- ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
- strerror(errno));
- return;
- }
- dup2(ttyFd, 1);
- dup2(ttyFd, 2);
+
+static jint Linux_gettid(JNIEnv* env, jobject) {
+ // gettid(2() was added in glibc 2.30 but Android uses an older version in prebuilt.
+ return syscall(__NR_gettid);
}
// ---- Registration ----
@@ -207,11 +196,10 @@ static const JNINativeMethod sMethods[] =
{ "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat },
{ "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open },
{ "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
+ { "gettid", "()I", (void*)Linux_gettid },
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- maybeRedirectLog();
-
ALOGI("%s: JNI_OnLoad", __FILE__);
JNIEnv* env = GetJNIEnvOrDie(vm);
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index 5d623e0b6c36..fe2269a8dc38 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -18,12 +18,41 @@
# Options:
#
# -s: "Smoke" test -- skip slow tests (SysUI, ICU)
+#
+# -x PCRE: Specify exclusion filter in PCRE
+# Example: -x '^(Cts|hoststub)' # Exclude CTS and hoststubgen tests.
+#
+# -f PCRE: Specify inclusion filter in PCRE
+
+
+# Regex to identify slow tests, in PCRE
+SLOW_TEST_RE='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood|CarSystemUIRavenTests)$'
smoke=0
-while getopts "s" opt; do
+include_re=""
+exclude_re=""
+smoke_exclude_re=""
+dry_run=""
+while getopts "sx:f:dt" opt; do
case "$opt" in
s)
- smoke=1
+ # Remove slow tests.
+ smoke_exclude_re="$SLOW_TEST_RE"
+ ;;
+ x)
+ # Take a PCRE from the arg, and use it as an exclusion filter.
+ exclude_re="$OPTARG"
+ ;;
+ f)
+ # Take a PCRE from the arg, and use it as an inclusion filter.
+ include_re="$OPTARG"
+ ;;
+ d)
+ # Dry run
+ dry_run="echo"
+ ;;
+ t)
+ export RAVENWOOD_LOG_OUT=$(tty)
;;
'?')
exit 1
@@ -35,21 +64,46 @@ shift $(($OPTIND - 1))
all_tests=(hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker)
all_tests+=( $(${0%/*}/list-ravenwood-tests.sh) )
-# Regex to identify slow tests, in PCRE
-slow_tests_re='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood)$'
-
-if (( $smoke )) ; then
- # Remove the slow tests.
- all_tests=( $(
- for t in "${all_tests[@]}"; do
- echo $t | grep -vP "$slow_tests_re"
- done
- ) )
-fi
+filter() {
+ local re="$1"
+ local grep_arg="$2"
+ if [[ "$re" == "" ]] ; then
+ cat # No filtering
+ else
+ grep $grep_arg -iP "$re"
+ fi
+}
-run() {
- echo "Running: $*"
- "${@}"
+filter_in() {
+ filter "$1"
}
-run ${ATEST:-atest} "${all_tests[@]}"
+filter_out() {
+ filter "$1" -v
+}
+
+
+# Remove the slow tests.
+targets=( $(
+ for t in "${all_tests[@]}"; do
+ echo $t | filter_in "$include_re" | filter_out "$smoke_exclude_re" | filter_out "$exclude_re"
+ done
+) )
+
+# Show the target tests
+
+echo "Target tests:"
+for t in "${targets[@]}"; do
+ echo " $t"
+done
+
+# Calculate the removed tests.
+
+diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') )"
+
+if [[ "$diff" != "" ]]; then
+ echo "Excluded tests:"
+ echo "$diff"
+fi
+
+$dry_run ${ATEST:-atest} "${targets[@]}"
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index 4895a1a6d1a2..40e6672a3c63 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -58,6 +58,9 @@ java_defaults {
java_defaults {
name: "ravenwood-bivalent-device-defaults",
defaults: ["ravenwood-bivalent-defaults"],
+
+ target_sdk_version: "34", // For compat-framework tests
+
// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
exclude_srcs: [
"test/**/ravenizer/*.java",
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
new file mode 100644
index 000000000000..a95760db1a61
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.bivalenttest.compat
+
+import android.app.compat.CompatChanges
+import android.os.Build
+import android.platform.test.ravenwood.RavenwoodConfig
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodCompatFrameworkTest {
+ companion object {
+ @JvmField // Expose as a raw field, not as a property.
+ @RavenwoodConfig.Config
+ val config = RavenwoodConfig.Builder()
+ .setTargetSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ .build()
+ }
+
+ @Test
+ fun testEnabled() {
+ Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
+ }
+
+ @Test
+ fun testDisabled() {
+ Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
+ }
+
+ @Test
+ fun testEnabledAfterSForUApps() {
+ Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
+ }
+
+ @Test
+ fun testEnabledAfterUForUApps() {
+ Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+ }
+} \ No newline at end of file
diff --git a/ravenwood/tests/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp
index 410292001670..0c0df1f993aa 100644
--- a/ravenwood/tests/runtime-test/Android.bp
+++ b/ravenwood/tests/runtime-test/Android.bp
@@ -10,6 +10,9 @@ package {
android_ravenwood_test {
name: "RavenwoodRuntimeTest",
+ libs: [
+ "ravenwood-helper-runtime",
+ ],
static_libs: [
"androidx.annotation_annotation",
"androidx.test.ext.junit",
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
new file mode 100644
index 000000000000..8e04b698c9d9
--- /dev/null
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.runtimetest;
+
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodConfig;
+import android.system.Os;
+
+import com.android.ravenwood.RavenwoodRuntimeState;
+
+import dalvik.system.VMRuntime;
+
+import org.junit.Test;
+
+public class IdentityTest {
+
+ @RavenwoodConfig.Config
+ public static final RavenwoodConfig sConfig =
+ new RavenwoodConfig.Builder()
+ .setTargetSdkLevel(UPSIDE_DOWN_CAKE)
+ .setProcessApp()
+ .build();
+
+ @Test
+ public void testUid() {
+ assertEquals(FIRST_APPLICATION_UID, RavenwoodRuntimeState.sUid);
+ assertEquals(FIRST_APPLICATION_UID, Os.getuid());
+ assertEquals(FIRST_APPLICATION_UID, Process.myUid());
+ assertEquals(FIRST_APPLICATION_UID, Binder.getCallingUid());
+ }
+
+ @Test
+ public void testPid() {
+ int pid = RavenwoodRuntimeState.sPid;
+ assertEquals(pid, Os.getpid());
+ assertEquals(pid, Process.myPid());
+ assertEquals(pid, Binder.getCallingPid());
+ }
+
+ @Test
+ public void testTargetSdkLevel() {
+ assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, RavenwoodRuntimeState.CUR_DEVELOPMENT);
+ assertEquals(UPSIDE_DOWN_CAKE, RavenwoodRuntimeState.sTargetSdkLevel);
+ assertEquals(UPSIDE_DOWN_CAKE, VMRuntime.getRuntime().getTargetSdkVersion());
+ }
+}
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
index c2230c739ccf..c55506a0a10a 100644
--- a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
@@ -24,6 +24,8 @@ import static android.system.OsConstants.S_ISREG;
import static android.system.OsConstants.S_ISSOCK;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
@@ -51,10 +53,12 @@ import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class OsTest {
+
public interface ConsumerWithThrow<T> {
void accept(T var1) throws Exception;
}
@@ -165,6 +169,35 @@ public class OsTest {
});
}
+ private static class TestThread extends Thread {
+
+ final CountDownLatch mLatch = new CountDownLatch(1);
+ int mTid;
+
+ TestThread() {
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ mTid = Os.gettid();
+ mLatch.countDown();
+ }
+ }
+
+ @Test
+ public void testGetTid() throws InterruptedException {
+ var t1 = new TestThread();
+ var t2 = new TestThread();
+ t1.start();
+ t2.start();
+ // Wait for thread execution
+ assertTrue(t1.mLatch.await(1, TimeUnit.SECONDS));
+ assertTrue(t2.mLatch.await(1, TimeUnit.SECONDS));
+ // Make sure the tid is unique per-thread
+ assertNotEquals(t1.mTid, t2.mTid);
+ }
+
// Verify StructStat values from libcore against native JVM PosixFileAttributes
private static void assertAttributesEqual(PosixFileAttributes attr, StructStat stat) {
assertEquals(attr.lastModifiedTime(), convertTimespecToFileTime(stat.st_mtim));
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java
new file mode 100644
index 000000000000..d25b5c19f351
--- /dev/null
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.runtimetest;
+
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.os.Process;
+import android.system.Os;
+
+import org.junit.Test;
+
+public class ProcessTest {
+
+ @Test
+ public void testGetUidPidTid() {
+ assertEquals(Os.getuid(), Process.myUid());
+ assertEquals(Os.getpid(), Process.myPid());
+ assertEquals(Os.gettid(), Process.myTid());
+ }
+
+ @Test
+ public void testThreadPriority() {
+ assertThrows(UnsupportedOperationException.class,
+ () -> Process.getThreadPriority(Process.myTid() + 1));
+ assertThrows(UnsupportedOperationException.class,
+ () -> Process.setThreadPriority(Process.myTid() + 1, THREAD_PRIORITY_DEFAULT));
+ assertEquals(THREAD_PRIORITY_DEFAULT, Process.getThreadPriority(Process.myTid()));
+ Process.setThreadPriority(THREAD_PRIORITY_FOREGROUND);
+ assertEquals(THREAD_PRIORITY_FOREGROUND, Process.getThreadPriority(Process.myTid()));
+ Process.setCanSelfBackground(false);
+ Process.setThreadPriority(THREAD_PRIORITY_DEFAULT);
+ assertEquals(THREAD_PRIORITY_DEFAULT, Process.getThreadPriority(Process.myTid()));
+ assertThrows(IllegalArgumentException.class,
+ () -> Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND));
+ }
+}
diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index b3d3963270ee..4aae1e11b72e 100644
--- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
@@ -19,17 +19,22 @@ package com.android.ravenwoodtest.servicestest;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import android.content.Context;
import android.hardware.SerialManager;
import android.hardware.SerialManagerInternal;
-import android.platform.test.ravenwood.RavenwoodRule;
+import android.platform.test.ravenwood.RavenwoodConfig;
+import android.platform.test.ravenwood.RavenwoodConfig.Config;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.LocalServices;
-import org.junit.Rule;
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,18 +42,25 @@ import org.junit.runner.RunWith;
public class RavenwoodServicesTest {
private static final String TEST_VIRTUAL_PORT = "virtual:example";
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ @Config
+ public static final RavenwoodConfig sRavenwood = new RavenwoodConfig.Builder()
.setProcessSystem()
.setServicesRequired(SerialManager.class)
.build();
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
@Test
public void testDefined() {
final SerialManager fromName = (SerialManager)
- mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+ mContext.getSystemService(Context.SERIAL_SERVICE);
final SerialManager fromClass =
- mRavenwood.getContext().getSystemService(SerialManager.class);
+ mContext.getSystemService(SerialManager.class);
assertNotNull(fromName);
assertNotNull(fromClass);
assertEquals(fromName, fromClass);
@@ -61,9 +73,9 @@ public class RavenwoodServicesTest {
// Verify that we can obtain a manager, and talk to the backend service, and that no
// serial ports are configured by default
final SerialManager service = (SerialManager)
- mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+ mContext.getSystemService(Context.SERIAL_SERVICE);
final String[] ports = service.getSerialPorts();
- final String[] refPorts = mRavenwood.getContext().getResources().getStringArray(
+ final String[] refPorts = mContext.getResources().getStringArray(
com.android.internal.R.array.config_serialPorts);
assertArrayEquals(refPorts, ports);
}
@@ -71,7 +83,7 @@ public class RavenwoodServicesTest {
@Test
public void testDriven() {
final SerialManager service = (SerialManager)
- mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+ mContext.getSystemService(Context.SERIAL_SERVICE);
final SerialManagerInternal internal = LocalServices.getService(
SerialManagerInternal.class);
@@ -79,8 +91,17 @@ public class RavenwoodServicesTest {
throw new UnsupportedOperationException(
"Needs socketpair() to offer accurate emulation");
});
- final String[] ports = service.getSerialPorts();
- assertEquals(1, ports.length);
- assertEquals(TEST_VIRTUAL_PORT, ports[0]);
+ try {
+ final String[] ports = service.getSerialPorts();
+ for (var port : ports) {
+ if (TEST_VIRTUAL_PORT.equals(port)) {
+ return; // Pass
+ }
+ }
+ fail("Virtual port " + TEST_VIRTUAL_PORT + " not found. Actual="
+ + Lists.newArrayList(ports));
+ } finally {
+ internal.removeVirtualSerialPortForTest(TEST_VIRTUAL_PORT);
+ }
}
}
diff --git a/ravenwood/texts/build.prop-sample-cuttlefish b/ravenwood/texts/build.prop-sample-cuttlefish
new file mode 100644
index 000000000000..f78b727f5779
--- /dev/null
+++ b/ravenwood/texts/build.prop-sample-cuttlefish
@@ -0,0 +1,132 @@
+# This is file is generated with `aosp_cf_x86_64_phone-trunk_staging-eng` on 2024-11-06.
+# We have this file here only as a reference. We don't actually use this file anywhere.
+
+####################################
+# from generate_common_build_props
+# These properties identify this partition image.
+####################################
+ro.product.system.brand=Android
+ro.product.system.device=generic
+ro.product.system.manufacturer=Android
+ro.product.system.model=mainline
+ro.product.system.name=mainline
+ro.system.product.cpu.abilist=x86_64,x86,arm64-v8a,armeabi-v7a,armeabi
+ro.system.product.cpu.abilist32=x86,armeabi-v7a,armeabi
+ro.system.product.cpu.abilist64=x86_64,arm64-v8a
+ro.system.build.date=Tue Nov 5 13:25:43 PST 2024
+ro.system.build.date.utc=1730841943
+ro.system.build.fingerprint=generic/aosp_cf_x86_64_phone/vsoc_x86_64:Baklava/MAIN/eng.omakot:eng/test-keys
+ro.system.build.id=MAIN
+ro.system.build.tags=test-keys
+ro.system.build.type=eng
+ro.system.build.version.incremental=eng.omakot
+ro.system.build.version.release=15
+ro.system.build.version.release_or_codename=Baklava
+ro.system.build.version.sdk=35
+####################################
+# from gen_build_prop.py:generate_build_info
+####################################
+# begin build properties
+ro.build.legacy.id=MAIN
+ro.build.display.id=aosp_cf_x86_64_phone-eng Baklava MAIN eng.omakot test-keys
+ro.build.version.incremental=eng.omakot
+ro.build.version.sdk=35
+ro.build.version.preview_sdk=1
+ro.build.version.preview_sdk_fingerprint=2ef06129940d459014cf4dede3950d71
+ro.build.version.codename=Baklava
+ro.build.version.all_codenames=Baklava
+ro.build.version.known_codenames=Base,Base11,Cupcake,Donut,Eclair,Eclair01,EclairMr1,Froyo,Gingerbread,GingerbreadMr1,Honeycomb,HoneycombMr1,HoneycombMr2,IceCreamSandwich,IceCreamSandwichMr1,JellyBean,JellyBeanMr1,JellyBeanMr2,Kitkat,KitkatWatch,Lollipop,LollipopMr1,M,N,NMr1,O,OMr1,P,Q,R,S,Sv2,Tiramisu,UpsideDownCake,VanillaIceCream,Baklava
+ro.build.version.release=15
+ro.build.version.release_or_codename=Baklava
+ro.build.version.release_or_preview_display=Baklava
+ro.build.version.security_patch=2024-08-05
+ro.build.version.base_os=
+ro.build.version.min_supported_target_sdk=28
+ro.build.date=Tue Nov 5 13:25:43 PST 2024
+ro.build.date.utc=1730841943
+ro.build.type=eng
+ro.build.user=omakoto
+ro.build.host=omakoto-ct1.c.googlers.com
+ro.build.tags=test-keys
+ro.build.flavor=aosp_cf_x86_64_phone-eng
+# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,
+# use ro.product.cpu.abilist instead.
+ro.product.cpu.abi=x86_64
+ro.product.locale=en-US
+ro.wifi.channels=
+# ro.build.product is obsolete; use ro.product.device
+ro.build.product=vsoc_x86_64
+# Do not try to parse description or thumbprint
+ro.build.description=aosp_cf_x86_64_phone-eng Baklava MAIN eng.omakot test-keys
+# end build properties
+####################################
+# from variable ADDITIONAL_SYSTEM_PROPERTIES
+####################################
+ro.treble.enabled=true
+ro.llndk.api_level=202504
+ro.actionable_compatible_property.enabled=true
+persist.debug.dalvik.vm.core_platform_api_policy=just-warn
+ro.postinstall.fstab.prefix=/system
+ro.kernel.android.checkjni=1
+ro.secure=0
+ro.allow.mock.location=1
+dalvik.vm.lockprof.threshold=500
+ro.debuggable=1
+dalvik.vm.image-dex2oat-filter=extract
+init.svc_debug.no_fatal.zygote=true
+net.bt.name=Android
+ro.force.debuggable=0
+####################################
+# from variable PRODUCT_SYSTEM_PROPERTIES
+####################################
+debug.atrace.tags.enableflags=0
+persist.traced.enable=1
+dalvik.vm.image-dex2oat-Xms=64m
+dalvik.vm.image-dex2oat-Xmx=64m
+dalvik.vm.dex2oat-Xms=64m
+dalvik.vm.dex2oat-Xmx=512m
+dalvik.vm.usejit=true
+dalvik.vm.dexopt.secondary=true
+dalvik.vm.dexopt.thermal-cutoff=2
+dalvik.vm.appimageformat=lz4
+ro.dalvik.vm.native.bridge=0
+pm.dexopt.post-boot=verify
+pm.dexopt.first-boot=verify
+pm.dexopt.boot-after-ota=verify
+pm.dexopt.boot-after-mainline-update=verify
+pm.dexopt.install=speed-profile
+pm.dexopt.install-fast=skip
+pm.dexopt.install-bulk=speed-profile
+pm.dexopt.install-bulk-secondary=verify
+pm.dexopt.install-bulk-downgraded=verify
+pm.dexopt.install-bulk-secondary-downgraded=verify
+pm.dexopt.bg-dexopt=speed-profile
+pm.dexopt.ab-ota=speed-profile
+pm.dexopt.inactive=verify
+pm.dexopt.cmdline=verify
+pm.dexopt.shared=speed
+dalvik.vm.disable-art-service-dexopt=true
+dalvik.vm.disable-odrefresh=true
+dalvik.vm.dex2oat-resolve-startup-strings=true
+dalvik.vm.dex2oat-max-image-block-size=524288
+dalvik.vm.minidebuginfo=true
+dalvik.vm.dex2oat-minidebuginfo=true
+dalvik.vm.madvise.vdexfile.size=104857600
+dalvik.vm.madvise.odexfile.size=104857600
+dalvik.vm.madvise.artfile.size=4294967295
+dalvik.vm.usap_pool_enabled=false
+dalvik.vm.usap_refill_threshold=1
+dalvik.vm.usap_pool_size_max=3
+dalvik.vm.usap_pool_size_min=1
+dalvik.vm.usap_pool_refill_delay_ms=3000
+dalvik.vm.useartservice=true
+dalvik.vm.enable_pr_dexopt=true
+ro.cp_system_other_odex=1
+ro.apex.updatable=true
+ro.launcher.depth.widget=0
+####################################
+# from variable PRODUCT_SYSTEM_DEFAULT_PROPERTIES
+####################################
+# Auto-added by post_process_props.py
+persist.sys.usb.config=adb
+# end of file
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 70c1d781587f..c196a09e18d1 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -15,7 +15,9 @@ com.android.internal.os.BatteryStatsHistory
com.android.internal.os.BatteryStatsHistoryIterator
com.android.internal.os.Clock
com.android.internal.os.LongArrayMultiStateCounter
+com.android.internal.os.LongArrayMultiStateCounter_ravenwood
com.android.internal.os.LongMultiStateCounter
+com.android.internal.os.LongMultiStateCounter_ravenwood
com.android.internal.os.MonotonicClock
com.android.internal.os.PowerProfile
com.android.internal.os.PowerStats
@@ -143,6 +145,7 @@ android.os.LocaleList
android.os.Looper
android.os.Message
android.os.MessageQueue
+android.os.MessageQueue_ravenwood
android.os.PackageTagsList
android.os.Parcel
android.os.ParcelFileDescriptor
@@ -235,6 +238,7 @@ android.database.Cursor
android.database.CursorIndexOutOfBoundsException
android.database.CursorJoiner
android.database.CursorWindow
+android.database.CursorWindow_ravenwood
android.database.CursorWrapper
android.database.DataSetObservable
android.database.DataSetObserver
@@ -365,3 +369,8 @@ com.android.server.utils.TimingsTraceAndSlog
android.os.IpcDataCache
android.app.PropertyInvalidatedCache
+
+android.app.compat.*
+com.android.server.compat.*
+com.android.internal.compat.*
+android.app.AppCompatCallbacks
diff --git a/ravenwood/texts/ravenwood-build.prop b/ravenwood/texts/ravenwood-build.prop
new file mode 100644
index 000000000000..93a18cffec50
--- /dev/null
+++ b/ravenwood/texts/ravenwood-build.prop
@@ -0,0 +1,44 @@
+# This file contains system properties used on ravenwood.
+
+ro.is_on_ravenwood=1
+
+ro.board.first_api_level=1
+ro.product.first_api_level=1
+ro.soc.manufacturer=Android
+ro.soc.model=Ravenwood
+ro.debuggable=1
+
+# The ones starting with "ro.product" or "ro.bild" will be copied to all "partitions" too.
+# See RavenwoodSystemProperties.
+ro.product.brand=Android
+ro.product.device=Ravenwood
+ro.product.manufacturer=Android
+ro.product.model=Ravenwood
+ro.product.name=Ravenwood
+ro.product.cpu.abilist=x86_64
+ro.product.cpu.abilist32=
+ro.product.cpu.abilist64=x86_64
+
+ro.build.date=Thu Jan 01 00:00:00 GMT 2024
+ro.build.date.utc=1704092400
+ro.build.id=MAIN
+ro.build.tags=dev-keys
+ro.build.type=userdebug
+ro.build.version.incremental=userdebug.ravenwood.20240101
+
+# These are what we used to use on Ravenwood, copied here as a reference.
+#ro.build.version.codename=REL
+#ro.build.version.all_codenames=REL
+#ro.build.version.known_codenames=REL
+#ro.build.version.release=14
+#ro.build.version.release_or_codename=VanillaIceCream
+#ro.build.version.sdk=34
+
+# We pull in the following values from the real build.prop file.
+ro.build.version.codename=$$$ro.build.version.codename
+ro.build.version.all_codenames=$$$ro.build.version.codename
+ro.build.version.known_codenames=$$$ro.build.version.codename
+ro.build.version.release=$$$ro.build.version.release
+ro.build.version.release_or_codename=$$$ro.build.version.release_or_codename
+ro.build.version.release_or_preview_display=$$$ro.build.version.release_or_preview_display
+ro.build.version.sdk=$$$ro.build.version.sdk
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index cc2fa602b3c3..530e5c8f5986 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1 +1,12 @@
# Ravenwood "policy" file for services.core.
+
+# Auto-generated from XSD
+class com.android.server.compat.config.Change keepclass
+class com.android.server.compat.config.Config keepclass
+class com.android.server.compat.config.XmlParser keepclass
+class com.android.server.compat.overrides.ChangeOverrides keepclass
+class com.android.server.compat.overrides.OverrideValue keepclass
+class com.android.server.compat.overrides.Overrides keepclass
+class com.android.server.compat.overrides.RawOverrideValue keepclass
+class com.android.server.compat.overrides.XmlParser keepclass
+class com.android.server.compat.overrides.XmlWriter keepclass \ No newline at end of file
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 3726ca972564..b389a67a8e4c 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -30,7 +30,7 @@ help() {
EOF
}
-source "${0%/*}"/../../common.sh
+source "${0%/*}"/../common.sh
SCRIPT_NAME="${0##*/}"
@@ -61,7 +61,6 @@ esac
done
shift $(($OPTIND - 1))
-
# Build the dump files, which are the input of this test.
run m dump-jar tiny-framework-dump-test
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index cee29dcd1d59..7a7de3553829 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -47,6 +47,7 @@ class TestWithGoldenOutput(unittest.TestCase):
# Test to check the generated jar files to the golden output.
def test_compare_to_golden(self):
+ self.skipTest("test cannot handle multiple images (see b/378470825)")
files = os.listdir(GOLDEN_DIR)
files.sort()
diff --git a/services/Android.bp b/services/Android.bp
index 899e224c6fd7..fc0bb33e6e4e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -282,6 +282,7 @@ system_java_library {
"services.wifi",
"service-blobstore",
"service-jobscheduler",
+ "service-connectivity-b-pre-jarjar", // Move it to mainline module
"android.hidl.base-V1.0-java",
],
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 617cca9d3075..d6fc6e461edc 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -27,6 +27,8 @@ import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.hardware.input.InputManager;
+import android.hardware.input.KeyGestureEvent;
+import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -44,11 +46,14 @@ import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.Nullable;
+
import com.android.server.LocalServices;
import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.MouseEventHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
@@ -187,6 +192,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private final AccessibilityManagerService mAms;
+ private final InputManager mInputManager;
+
private final SparseArray<EventStreamTransformation> mEventHandler;
private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);
@@ -228,6 +235,47 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*/
private MotionEvent mLastActiveDeviceMotionEvent = null;
+ private boolean mKeyGestureEventHandlerInstalled = false;
+ private InputManager.KeyGestureEventHandler mKeyGestureEventHandler =
+ new InputManager.KeyGestureEventHandler() {
+ @Override
+ public boolean handleKeyGestureEvent(
+ @NonNull KeyGestureEvent event,
+ @Nullable IBinder focusedToken) {
+ final boolean complete =
+ event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ final int gestureType = event.getKeyGestureType();
+ final int displayId = isDisplayIdValid(event.getDisplayId())
+ ? event.getDisplayId() : Display.DEFAULT_DISPLAY;
+
+ switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN:
+ if (complete) {
+ mAms.getMagnificationController().scaleMagnificationByStep(
+ displayId, MagnificationController.ZOOM_DIRECTION_IN);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT:
+ if (complete) {
+ mAms.getMagnificationController().scaleMagnificationByStep(
+ displayId, MagnificationController.ZOOM_DIRECTION_OUT);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isKeyGestureSupported(int gestureType) {
+ return switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT -> true;
+ default -> false;
+ };
+ }
+ };
+
private static MotionEvent cancelMotion(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT
@@ -287,6 +335,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mContext = context;
mAms = service;
mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mInputManager = context.getSystemService(InputManager.class);
mEventHandler = eventHandler;
}
@@ -723,6 +772,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
createMagnificationGestureHandler(displayId, displayContext);
addFirstEventHandler(displayId, magnificationGestureHandler);
mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+
+ if (com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures()
+ && !mKeyGestureEventHandlerInstalled) {
+ mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler);
+ mKeyGestureEventHandlerInstalled = true;
+ }
}
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
@@ -842,6 +897,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mMouseKeysInterceptor.onDestroy();
mMouseKeysInterceptor = null;
}
+
+ if (mKeyGestureEventHandlerInstalled) {
+ mInputManager.unregisterKeyGestureEventHandler(mKeyGestureEventHandler);
+ mKeyGestureEventHandlerInstalled = false;
+ }
}
private MagnificationGestureHandler createMagnificationGestureHandler(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 974cba2450f4..d4af7b765254 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -42,9 +42,11 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATIN
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static android.view.accessibility.AccessibilityManager.FlashNotificationReason;
+import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
@@ -55,6 +57,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
@@ -111,6 +114,8 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.input.InputManager;
+import android.hardware.input.KeyGestureEvent;
import android.media.AudioManagerInternal;
import android.net.Uri;
import android.os.Binder;
@@ -338,6 +343,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private AlertDialog mEnableTouchExplorationDialog;
+ private final InputManager mInputManager;
+
private AccessibilityInputFilter mInputFilter;
private boolean mHasInputFilter;
@@ -503,6 +510,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ private InputManager.KeyGestureEventHandler mKeyGestureEventHandler =
+ new InputManager.KeyGestureEventHandler() {
+ @Override
+ public boolean handleKeyGestureEvent(
+ @NonNull KeyGestureEvent event,
+ @Nullable IBinder focusedToken) {
+ return AccessibilityManagerService.this.handleKeyGestureEvent(event);
+ }
+
+ @Override
+ public boolean isKeyGestureSupported(int gestureType) {
+ return switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK -> true;
+ default -> false;
+ };
+ }
+ };
+
@VisibleForTesting
AccessibilityManagerService(
Context context,
@@ -542,6 +568,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mUmi = LocalServices.getService(UserManagerInternal.class);
// TODO(b/255426725): not used on tests
mVisibleBgUserIds = null;
+ mInputManager = context.getSystemService(InputManager.class);
init();
}
@@ -583,6 +610,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mUiAutomationManager, this);
mFlashNotificationsController = new FlashNotificationsController(mContext);
mUmi = LocalServices.getService(UserManagerInternal.class);
+ mInputManager = context.getSystemService(InputManager.class);
if (UserManager.isVisibleBackgroundUsersEnabled()) {
mVisibleBgUserIds = new SparseBooleanArray();
@@ -599,6 +627,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
mContext.getContentResolver());
+ if (enableTalkbackAndMagnifierKeyGestures()) {
+ mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler);
+ }
disableAccessibilityMenuToMigrateIfNeeded();
}
@@ -640,6 +671,79 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return mIsAccessibilityButtonShown;
}
+ @VisibleForTesting
+ boolean handleKeyGestureEvent(KeyGestureEvent event) {
+ final boolean complete =
+ event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ final int gestureType = event.getKeyGestureType();
+ if (!complete) {
+ return false;
+ }
+
+ String targetName;
+ switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION:
+ targetName = MAGNIFICATION_CONTROLLER_NAME;
+ break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK:
+ targetName = mContext.getString(R.string.config_defaultSelectToSpeakService);
+ if (targetName.isEmpty()) {
+ return false;
+ }
+
+ final ComponentName targetServiceComponent = TextUtils.isEmpty(targetName)
+ ? null : ComponentName.unflattenFromString(targetName);
+ AccessibilityServiceInfo accessibilityServiceInfo;
+ synchronized (mLock) {
+ AccessibilityUserState userState = getCurrentUserStateLocked();
+ accessibilityServiceInfo =
+ userState.getInstalledServiceInfoLocked(targetServiceComponent);
+ }
+ if (accessibilityServiceInfo == null) {
+ return false;
+ }
+
+ // Skip enabling if a warning dialog is required for the feature.
+ // TODO(b/377752960): Explore better options to instead show the warning dialog
+ // in this scenario.
+ if (isAccessibilityServiceWarningRequired(accessibilityServiceInfo)) {
+ Slog.w(LOG_TAG,
+ "Accessibility warning is required before this service can be "
+ + "activated automatically via KEY_GESTURE shortcut.");
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ List<String> shortcutTargets = getAccessibilityShortcutTargets(
+ KEY_GESTURE);
+ if (!shortcutTargets.contains(targetName)) {
+ int userId;
+ synchronized (mLock) {
+ userId = mCurrentUserId;
+ }
+ // TODO(b/377752960): Add dialog to confirm enabling the service and to
+ // activate the first time.
+ enableShortcutForTargets(true, UserShortcutType.KEY_GESTURE,
+ List.of(targetName), userId);
+
+ // Do not perform action on first press since it was just registered. Eventually,
+ // this will be a separate dialog that appears that requires the user to confirm
+ // which will resolve this race condition. For now, just require two presses the
+ // first time it is activated.
+ return true;
+ }
+
+ final int displayId = event.getDisplayId() != INVALID_DISPLAY
+ ? event.getDisplayId() : getLastNonProxyTopFocusedDisplayId();
+ performAccessibilityShortcutInternal(displayId, KEY_GESTURE, targetName);
+
+ return true;
+ }
+
@Override
public Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
int windowId) {
@@ -1224,14 +1328,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
int displayId = event.getDisplayId();
final int windowId = event.getWindowId();
if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
- && displayId == Display.INVALID_DISPLAY) {
+ && displayId == INVALID_DISPLAY) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowId(
resolvedUserId, windowId);
event.setDisplayId(displayId);
}
synchronized (mLock) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- && displayId != Display.INVALID_DISPLAY
+ && displayId != INVALID_DISPLAY
&& mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
shouldComputeWindows = true;
}
@@ -3257,6 +3361,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
updateAccessibilityShortcutTargetsLocked(userState, GESTURE);
updateAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
+ updateAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE);
// Update the capabilities before the mode because we will check the current mode is
// invalid or not..
updateMagnificationCapabilitiesSettingsChangeLocked(userState);
@@ -3387,6 +3492,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, GESTURE);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE);
somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
@@ -3968,6 +4074,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
shortcutTypes.add(GESTURE);
}
+ shortcutTypes.add(KEY_GESTURE);
final ComponentName serviceName = service.getComponentName();
for (Integer shortcutType: shortcutTypes) {
@@ -4078,13 +4185,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
private void performAccessibilityShortcutInternal(int displayId,
@UserShortcutType int shortcutType, @Nullable String targetName) {
- final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType);
+ final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(
+ shortcutType);
if (shortcutTargets.isEmpty()) {
Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType);
return;
}
// In case the caller specified a target name
- if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, targetName)) {
+ if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets,
+ targetName)) {
Slog.v(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
targetName = null;
}
@@ -4306,6 +4415,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
+ if (shortcutType == UserShortcutType.KEY_GESTURE
+ && !enableTalkbackAndMagnifierKeyGestures()) {
+ Slog.w(LOG_TAG,
+ "KEY_GESTURE type shortcuts are disabled by feature flag");
+ return;
+ }
+
final String shortcutTypeSettingKey = ShortcutUtils.convertToKey(shortcutType);
if (shortcutType == UserShortcutType.TRIPLETAP
|| shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP) {
@@ -5071,6 +5187,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@EnforcePermission(MANAGE_ACCESSIBILITY)
public boolean isAccessibilityServiceWarningRequired(AccessibilityServiceInfo info) {
isAccessibilityServiceWarningRequired_enforcePermission();
+ if (info == null) {
+ Log.e(LOG_TAG, "Called isAccessibilityServiceWarningRequired with null service info");
+ return true;
+ }
+
final ComponentName componentName = info.getComponentName();
// Warning is not required if the service is already enabled.
@@ -5678,6 +5799,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Uri mAccessibilityGestureTargetsUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS);
+ private final Uri mAccessibilityKeyGestureTargetsUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS);
+
private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS);
@@ -5742,6 +5866,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
contentResolver.registerContentObserver(
mAccessibilityGestureTargetsUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
+ mAccessibilityKeyGestureTargetsUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mUserInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
@@ -5823,6 +5949,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (readAccessibilityShortcutTargetsLocked(userState, GESTURE)) {
onUserStateChangedLocked(userState);
}
+ } else if (mAccessibilityKeyGestureTargetsUri.equals(uri)) {
+ if (readAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE)) {
+ onUserStateChangedLocked(userState);
+ }
} else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
|| mUserInteractiveUiTimeoutUri.equals(uri)) {
readUserRecommendedUiTimeoutSettingsLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 67b40632dde8..8b3e63d0dc5e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -29,6 +29,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
@@ -209,6 +210,7 @@ class AccessibilityUserState {
mShortcutTargets.put(SOFTWARE, new ArraySet<>());
mShortcutTargets.put(GESTURE, new ArraySet<>());
mShortcutTargets.put(QUICK_SETTINGS, new ArraySet<>());
+ mShortcutTargets.put(KEY_GESTURE, new ArraySet<>());
}
boolean isHandlingAccessibilityEventsLocked() {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index d40e7476f7ec..51c4305061f8 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -27,6 +27,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_
import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
import android.accessibilityservice.MagnificationConfig;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -101,6 +102,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
/** Whether the platform supports window magnification feature. */
private final boolean mSupportWindowMagnification;
+ private final MagnificationScaleStepProvider mScaleStepProvider;
private final Executor mBackgroundExecutor;
@@ -131,6 +133,14 @@ public class MagnificationController implements MagnificationConnectionManager.C
.UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray =
new SparseArray<>();
+ // Direction magnifier scale can be altered.
+ public static final int ZOOM_DIRECTION_IN = 0;
+ public static final int ZOOM_DIRECTION_OUT = 1;
+
+ @IntDef({ZOOM_DIRECTION_IN, ZOOM_DIRECTION_OUT})
+ public @interface ZoomDirection {
+ }
+
/**
* A callback to inform the magnification transition result on the given display.
*/
@@ -144,6 +154,41 @@ public class MagnificationController implements MagnificationConnectionManager.C
void onResult(int displayId, boolean success);
}
+
+ /**
+ * An interface to configure how much the magnification scale should be affected when moving in
+ * steps.
+ */
+ public interface MagnificationScaleStepProvider {
+ /**
+ * Calculate the next value given which direction (in/out) to adjust the magnification
+ * scale.
+ *
+ * @param currentScale The current magnification scale value.
+ * @param direction Whether to zoom in or out.
+ * @return The next scale value.
+ */
+ float nextScaleStep(float currentScale, @ZoomDirection int direction);
+ }
+
+ public static class DefaultMagnificationScaleStepProvider implements
+ MagnificationScaleStepProvider {
+ // Factor of magnification scale. For example, when this value is 1.189, scale
+ // value will be changed x1.000, x1.189, x1.414, x1.681, x2.000, ...
+ // Note: this value is 2.0 ^ (1 / 4).
+ public static final float ZOOM_STEP_SCALE_FACTOR = 1.18920712f;
+
+ @Override
+ public float nextScaleStep(float currentScale, @ZoomDirection int direction) {
+ final int stepDelta = direction == ZOOM_DIRECTION_IN ? 1 : -1;
+ final long scaleIndex = Math.round(
+ Math.log(currentScale) / Math.log(ZOOM_STEP_SCALE_FACTOR));
+ final float nextScale = (float) Math.pow(ZOOM_STEP_SCALE_FACTOR,
+ scaleIndex + stepDelta);
+ return MagnificationScaleProvider.constrainScale(nextScale);
+ }
+ }
+
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, MagnificationScaleProvider scaleProvider,
Executor backgroundExecutor) {
@@ -156,6 +201,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
.getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
FEATURE_WINDOW_MAGNIFICATION);
+ mScaleStepProvider = new DefaultMagnificationScaleStepProvider();
mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context);
mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
@@ -891,6 +937,37 @@ public class MagnificationController implements MagnificationConnectionManager.C
return isActivated;
}
+ /**
+ * Scales the magnifier on the given display one step in/out based on the zoomIn param.
+ *
+ * @param displayId The logical display id.
+ * @param direction Whether the scale should be zoomed in or out.
+ * @return {@code true} if the magnification scale was affected.
+ */
+ public boolean scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
+ if (getFullScreenMagnificationController().isActivated(displayId)) {
+ final float magnificationScale = getFullScreenMagnificationController().getScale(
+ displayId);
+ final float nextMagnificationScale = mScaleStepProvider.nextScaleStep(
+ magnificationScale, direction);
+ getFullScreenMagnificationController().setScaleAndCenter(displayId,
+ nextMagnificationScale,
+ Float.NaN, Float.NaN, true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ return nextMagnificationScale != magnificationScale;
+ }
+
+ if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
+ final float magnificationScale = getMagnificationConnectionManager().getScale(
+ displayId);
+ final float nextMagnificationScale = mScaleStepProvider.nextScaleStep(
+ magnificationScale, direction);
+ getMagnificationConnectionManager().setScale(displayId, nextMagnificationScale);
+ return nextMagnificationScale != magnificationScale;
+ }
+
+ return false;
+ }
+
private final class DisableMagnificationCallback implements
MagnificationAnimationCallback {
private final TransitionCallBack mTransitionCallBack;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
index 44ae1d1fbbbf..81e83b563945 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -16,7 +16,6 @@
package com.android.server.appfunctions;
-import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -24,15 +23,21 @@ import java.util.concurrent.TimeUnit;
/** Executors for App function operations. */
public final class AppFunctionExecutors {
+ static final int sConcurrency = Runtime.getRuntime().availableProcessors();
+
/** Executor for operations that do not need to block. */
- public static final Executor THREAD_POOL_EXECUTOR =
+ public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(
- /* corePoolSize= */ Runtime.getRuntime().availableProcessors(),
- /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(),
- /* keepAliveTime= */ 0L,
+ /* corePoolSize= */ sConcurrency,
+ /* maxConcurrency= */ sConcurrency,
+ /* keepAliveTime= */ 1L,
/* unit= */ TimeUnit.SECONDS,
/* workQueue= */ new LinkedBlockingQueue<>(),
new NamedThreadFactory("AppFunctionExecutors"));
+ static {
+ THREAD_POOL_EXECUTOR.allowCoreThreadTimeOut(true);
+ }
+
private AppFunctionExecutors() {}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 268e56487c4b..f13e22950e2d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -29,7 +29,7 @@ import android.app.appfunctions.AppFunctionManagerHelper;
import android.app.appfunctions.AppFunctionRuntimeMetadata;
import android.app.appfunctions.AppFunctionStaticMetadataHelper;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
-import android.app.appfunctions.ExecuteAppFunctionResponse;
+import android.app.appfunctions.AppFunctionException;
import android.app.appfunctions.IAppFunctionEnabledCallback;
import android.app.appfunctions.IAppFunctionManager;
import android.app.appfunctions.IAppFunctionService;
@@ -156,11 +156,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
mCallerValidator.verifyTargetUserHandle(
requestInternal.getUserHandle(), validatedCallingPackage);
} catch (SecurityException exception) {
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_DENIED,
- exception.getMessage(),
- /* extras= */ null));
+ safeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(
+ AppFunctionException.ERROR_DENIED,
+ exception.getMessage()));
return null;
}
@@ -180,7 +179,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
safeExecuteAppFunctionCallback,
executeAppFunctionCallback.asBinder());
} catch (Exception e) {
- safeExecuteAppFunctionCallback.onResult(
+ safeExecuteAppFunctionCallback.onError(
mapExceptionToExecuteAppFunctionResponse(e));
}
});
@@ -198,22 +197,19 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
UserHandle targetUser = requestInternal.getUserHandle();
// TODO(b/354956319): Add and honor the new enterprise policies.
if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
+ safeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR,
"Cannot run on a device with a device owner or from the managed"
- + " profile.",
- /* extras= */ null));
+ + " profile."));
return;
}
String targetPackageName = requestInternal.getClientRequest().getTargetPackageName();
if (TextUtils.isEmpty(targetPackageName)) {
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT,
- "Target package name cannot be empty.",
- /* extras= */ null));
+ safeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(
+ AppFunctionException.ERROR_INVALID_ARGUMENT,
+ "Target package name cannot be empty."));
return;
}
@@ -253,11 +249,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
mInternalServiceHelper.resolveAppFunctionService(
targetPackageName, targetUser);
if (serviceIntent == null) {
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
- "Cannot find the target service.",
- /* extras= */ null));
+ safeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(
+ AppFunctionException.ERROR_SYSTEM_ERROR,
+ "Cannot find the target service."));
return;
}
bindAppFunctionServiceUnchecked(
@@ -272,7 +267,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
})
.exceptionally(
ex -> {
- safeExecuteAppFunctionCallback.onResult(
+ safeExecuteAppFunctionCallback.onError(
mapExceptionToExecuteAppFunctionResponse(ex));
return null;
});
@@ -446,11 +441,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
if (!bindServiceResult) {
Slog.e(TAG, "Failed to bind to the AppFunctionService");
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
- "Failed to bind the AppFunctionService.",
- /* extras= */ null));
+ safeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR,
+ "Failed to bind the AppFunctionService."));
}
}
@@ -459,22 +452,21 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
.getSystemService(AppSearchManager.class);
}
- private ExecuteAppFunctionResponse mapExceptionToExecuteAppFunctionResponse(Throwable e) {
+ private AppFunctionException mapExceptionToExecuteAppFunctionResponse(Throwable e) {
if (e instanceof CompletionException) {
e = e.getCause();
}
- int resultCode = ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR;
+ int resultCode = AppFunctionException.ERROR_SYSTEM_ERROR;
if (e instanceof AppSearchException appSearchException) {
resultCode =
mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(
appSearchException.getResultCode());
} else if (e instanceof SecurityException) {
- resultCode = ExecuteAppFunctionResponse.RESULT_DENIED;
+ resultCode = AppFunctionException.ERROR_DENIED;
} else if (e instanceof DisabledAppFunctionException) {
- resultCode = ExecuteAppFunctionResponse.RESULT_DISABLED;
+ resultCode = AppFunctionException.ERROR_DISABLED;
}
- return ExecuteAppFunctionResponse.newFailure(
- resultCode, e.getMessage(), /* extras= */ null);
+ return new AppFunctionException(resultCode, e.getMessage());
}
private int mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(int resultCode) {
@@ -485,13 +477,13 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
switch (resultCode) {
case AppSearchResult.RESULT_NOT_FOUND:
- return ExecuteAppFunctionResponse.RESULT_FUNCTION_NOT_FOUND;
+ return AppFunctionException.ERROR_FUNCTION_NOT_FOUND;
case AppSearchResult.RESULT_INVALID_ARGUMENT:
case AppSearchResult.RESULT_INTERNAL_ERROR:
case AppSearchResult.RESULT_SECURITY_ERROR:
// fall-through
}
- return ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR;
+ return AppFunctionException.ERROR_SYSTEM_ERROR;
}
private void registerAppSearchObserver(@NonNull TargetUser user) {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
index 129be65f3153..c689bb92f8f7 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
@@ -17,6 +17,7 @@ package com.android.server.appfunctions;
import android.annotation.NonNull;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
+import android.app.appfunctions.AppFunctionException;
import android.app.appfunctions.ExecuteAppFunctionResponse;
import android.app.appfunctions.IAppFunctionService;
import android.app.appfunctions.ICancellationCallback;
@@ -57,17 +58,22 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp
mCancellationCallback,
new IExecuteAppFunctionCallback.Stub() {
@Override
- public void onResult(ExecuteAppFunctionResponse response) {
+ public void onSuccess(ExecuteAppFunctionResponse response) {
mSafeExecuteAppFunctionCallback.onResult(response);
serviceUsageCompleteListener.onCompleted();
}
+
+ @Override
+ public void onError(AppFunctionException error) {
+ mSafeExecuteAppFunctionCallback.onError(error);
+ serviceUsageCompleteListener.onCompleted();
+ }
});
} catch (Exception e) {
- mSafeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
- e.getMessage(),
- /* extras= */ null));
+ mSafeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(
+ AppFunctionException.ERROR_APP_UNKNOWN_ERROR,
+ e.getMessage()));
serviceUsageCompleteListener.onCompleted();
}
}
@@ -75,11 +81,9 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp
@Override
public void onFailedToConnect() {
Slog.e(TAG, "Failed to connect to service");
- mSafeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
- "Failed to connect to AppFunctionService",
- /* extras= */ null));
+ mSafeExecuteAppFunctionCallback.onError(
+ new AppFunctionException(AppFunctionException.ERROR_APP_UNKNOWN_ERROR,
+ "Failed to connect to AppFunctionService"));
}
@Override
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 68ff9725ce5c..b221d74e2d86 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -17,6 +17,7 @@
package com.android.server.appwidget;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
+import static android.appwidget.flags.Flags.remoteViewsProto;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
import static android.appwidget.flags.Flags.securityPolicyInteractAcrossUsers;
import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
@@ -31,6 +32,7 @@ import static com.android.server.appwidget.AppWidgetXmlUtil.serializeWidgetSizes
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionName;
@@ -104,6 +106,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.service.appwidget.AppWidgetServiceDumpProto;
+import android.service.appwidget.GeneratedPreviewsProto;
import android.service.appwidget.WidgetProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -122,7 +125,9 @@ import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TypedValue;
import android.util.Xml;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import android.view.Display;
import android.view.View;
import android.widget.RemoteViews;
@@ -134,6 +139,7 @@ import com.android.internal.app.UnlaunchableAppActivity;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
@@ -181,11 +187,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Simple flag to enable/disable debug logging.
private static final boolean DEBUG = Build.IS_DEBUGGABLE;
- // String constants for XML schema migration related to changes in keyguard package.
- private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
- private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
- private static final int KEYGUARD_HOST_ID = 0x4b455947;
-
// Filename for app widgets state persisted on disk.
private static final String STATE_FILENAME = "appwidgets.xml";
@@ -205,6 +206,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private static final int UNKNOWN_USER_ID = -10;
// Version of XML schema for app widgets. Bump if the stored widgets need to be upgraded.
+ // Version 1 introduced in 2014 - Android 5.0
private static final int CURRENT_VERSION = 1;
// Every widget update request is associated which an increasing sequence number. This is
@@ -221,6 +223,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// XML attribute for widget ids that are pending deletion.
// See {@link Provider#pendingDeletedWidgetIds}.
private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
+ // Name of service directory in /data/system_ce/<user>/
+ private static final String APPWIDGET_CE_DATA_DIRNAME = "appwidget";
+ // Name of previews directory in /data/system_ce/<user>/appwidget/
+ private static final String WIDGET_PREVIEWS_DIRNAME = "previews";
// Hard limit of number of hosts an app can create, note that the app that hosts the widgets
// can have multiple instances of {@link AppWidgetHost}, typically in respect to different
@@ -320,6 +326,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Handler to the background thread that saves states to disk.
private Handler mSaveStateHandler;
+ // Handler to the background thread that saves generated previews to disk. All operations that
+ // modify saved previews must be run on this Handler.
+ private Handler mSavePreviewsHandler;
// Handler to the foreground thread that handles broadcasts related to user
// and package events, as well as various internal events within
// AppWidgetService.
@@ -363,6 +372,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
} else {
mSaveStateHandler = BackgroundThread.getHandler();
}
+ mSavePreviewsHandler = new Handler(BackgroundThread.get().getLooper());
final ServiceThread serviceThread = new ServiceThread(TAG,
android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
serviceThread.start();
@@ -382,7 +392,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS,
DEFAULT_GENERATED_PREVIEW_MAX_PROVIDERS);
mGeneratedPreviewsApiCounter = new ApiCounter(generatedPreviewResetInterval,
- generatedPreviewMaxCallsPerInterval, generatedPreviewsMaxProviders);
+ generatedPreviewMaxCallsPerInterval,
+ // Set a limit on the number of providers if storing them in memory.
+ remoteViewsProto() ? Integer.MAX_VALUE : generatedPreviewsMaxProviders);
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
new HandlerExecutor(mCallbackHandler), this::handleSystemUiDeviceConfigChange);
@@ -648,7 +660,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
for (int i = 0; i < providerCount; i++) {
Provider provider = mProviders.get(i);
if (provider.id.uid == clearedUid) {
- changed |= provider.clearGeneratedPreviewsLocked();
+ if (remoteViewsProto()) {
+ changed |= clearGeneratedPreviewsAsync(provider);
+ } else {
+ changed |= provider.clearGeneratedPreviewsLocked();
+ }
+ if (DEBUG) {
+ Slog.e(TAG, "clearPreviewsForUidLocked " + provider + " changed " + changed);
+ }
}
}
return changed;
@@ -898,18 +917,30 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
for (int j = 0; j < widgetCount; j++) {
Widget widget = provider.widgets.get(j);
if (targetWidget != null && targetWidget != widget) continue;
+ // Identify the user in the host process since the intent will be invoked by
+ // the host app.
+ final Host host = widget.host;
+ final UserHandle hostUser;
+ if (host != null && host.id != null) {
+ hostUser = UserHandle.getUserHandleForUid(host.id.uid);
+ } else {
+ // Fallback to the parent profile if the host is null.
+ Slog.w(TAG, "Host is null when masking widget: " + widget.appWidgetId);
+ hostUser = mUserManager.getProfileParent(appUserId).getUserHandle();
+ }
if (provider.maskedByStoppedPackage) {
Intent intent = createUpdateIntentLocked(provider,
new int[] { widget.appWidgetId });
views.setOnClickPendingIntent(android.R.id.background,
- PendingIntent.getBroadcast(mContext, widget.appWidgetId,
+ PendingIntent.getBroadcastAsUser(mContext, widget.appWidgetId,
intent, PendingIntent.FLAG_UPDATE_CURRENT
- | PendingIntent.FLAG_IMMUTABLE));
+ | PendingIntent.FLAG_IMMUTABLE, hostUser));
} else if (onClickIntent != null) {
views.setOnClickPendingIntent(android.R.id.background,
- PendingIntent.getActivity(mContext, widget.appWidgetId, onClickIntent,
- PendingIntent.FLAG_UPDATE_CURRENT
- | PendingIntent.FLAG_IMMUTABLE));
+ PendingIntent.getActivityAsUser(mContext, widget.appWidgetId,
+ onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE, null /* options */,
+ hostUser));
}
if (widget.replaceWithMaskedViewsLocked(views)) {
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
@@ -3246,6 +3277,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
deleteWidgetsLocked(provider, UserHandle.USER_ALL);
mProviders.remove(provider);
mGeneratedPreviewsApiCounter.remove(provider.id);
+ if (remoteViewsProto()) {
+ clearGeneratedPreviewsAsync(provider);
+ }
// no need to send the DISABLE broadcast, since the receiver is gone anyway
cancelBroadcastsLocked(provider);
@@ -3824,6 +3858,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
} catch (IOException e) {
Slog.w(TAG, "Failed to read state: " + e);
}
+
+ if (remoteViewsProto()) {
+ try {
+ loadGeneratedPreviewCategoriesLocked(profileId);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read preview categories: " + e);
+ }
+ }
}
if (version >= 0) {
@@ -4382,19 +4424,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
int version = fromVersion;
- // Update 1: keyguard moved from package "android" to "com.android.keyguard"
+ // Update 1: From version 0 to 1, was used from Android 4 to Android 5. It updated the
+ // location of the keyguard widget database. No modern device will have db version 0.
if (version == 0) {
- HostId oldHostId = new HostId(Process.myUid(),
- KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE);
-
- Host host = lookupHostLocked(oldHostId);
- if (host != null) {
- final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
- UserHandle.USER_SYSTEM);
- if (uid >= 0) {
- host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
- }
- }
+ Slog.e(TAG, "Found widget database with version 0, this should not be possible,"
+ + " forcing upgrade to version 1");
version = 1;
}
@@ -4404,24 +4438,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
- private static File getStateFile(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME);
- }
-
private static AtomicFile getSavedStateFile(int userId) {
- File dir = Environment.getUserSystemDirectory(userId);
- File settingsFile = getStateFile(userId);
- if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
- if (!dir.exists()) {
- dir.mkdirs();
- }
- // Migrate old data
- File oldFile = new File("/data/system/" + STATE_FILENAME);
- // Method doesn't throw an exception on failure. Ignore any errors
- // in moving the file (like non-existence)
- oldFile.renameTo(settingsFile);
- }
- return new AtomicFile(settingsFile);
+ return new AtomicFile(new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME));
}
void onUserStopped(int userId) {
@@ -4593,6 +4611,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
keep.add(providerId);
// Use the new AppWidgetProviderInfo.
provider.setPartialInfoLocked(info);
+ // Clear old previews
+ if (remoteViewsProto()) {
+ clearGeneratedPreviewsAsync(provider);
+ } else {
+ provider.clearGeneratedPreviewsLocked();
+ }
// If it's enabled
final int M = provider.widgets.size();
if (M > 0) {
@@ -4884,6 +4908,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mSecurityPolicy.enforceCallFromPackage(callingPackage);
ensureWidgetCategoryCombinationIsValid(widgetCategory);
+ AndroidFuture<RemoteViews> result = null;
synchronized (mLock) {
ensureGroupStateLoadedLocked(profileId);
final int providerCount = mProviders.size();
@@ -4917,10 +4942,23 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
callingPackage);
if (providerIsInCallerProfile && !shouldFilterAppAccess
&& (providerIsInCallerPackage || hasBindAppWidgetPermission)) {
- return provider.getGeneratedPreviewLocked(widgetCategory);
+ if (remoteViewsProto()) {
+ result = getGeneratedPreviewsAsync(provider, widgetCategory);
+ } else {
+ return provider.getGeneratedPreviewLocked(widgetCategory);
+ }
}
}
}
+
+ if (result != null) {
+ try {
+ return result.get();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to get generated previews Future result", e);
+ return null;
+ }
+ }
// Either the provider does not exist or the caller does not have permission to access its
// previews.
return null;
@@ -4950,8 +4988,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
providerComponent + " is not a valid AppWidget provider");
}
if (mGeneratedPreviewsApiCounter.tryApiCall(providerId)) {
- provider.setGeneratedPreviewLocked(widgetCategories, preview);
- scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ if (remoteViewsProto()) {
+ setGeneratedPreviewsAsync(provider, widgetCategories, preview);
+ } else {
+ provider.setGeneratedPreviewLocked(widgetCategories, preview);
+ scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ }
return true;
}
return false;
@@ -4979,11 +5021,361 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
throw new IllegalArgumentException(
providerComponent + " is not a valid AppWidget provider");
}
- final boolean changed = provider.removeGeneratedPreviewLocked(widgetCategories);
- if (changed) scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+
+ if (remoteViewsProto()) {
+ removeGeneratedPreviewsAsync(provider, widgetCategories);
+ } else {
+ final boolean changed = provider.removeGeneratedPreviewLocked(widgetCategories);
+ if (changed) scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ }
+ }
+ }
+
+ /**
+ * Return previews for the specified provider from a background thread. The result of the future
+ * is nullable.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ @NonNull
+ private AndroidFuture<RemoteViews> getGeneratedPreviewsAsync(
+ @NonNull Provider provider, @AppWidgetProviderInfo.CategoryFlags int widgetCategory) {
+ AndroidFuture<RemoteViews> result = new AndroidFuture<>();
+ mSavePreviewsHandler.post(() -> {
+ SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
+ for (int i = 0; i < previews.size(); i++) {
+ if ((widgetCategory & previews.keyAt(i)) != 0) {
+ result.complete(previews.valueAt(i));
+ return;
+ }
+ }
+ result.complete(null);
+ });
+ return result;
+ }
+
+ /**
+ * Set previews for the specified provider on a background thread.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ private void setGeneratedPreviewsAsync(@NonNull Provider provider, int widgetCategories,
+ @NonNull RemoteViews preview) {
+ mSavePreviewsHandler.post(() -> {
+ SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
+ for (int flag : Provider.WIDGET_CATEGORY_FLAGS) {
+ if ((widgetCategories & flag) != 0) {
+ previews.put(flag, preview);
+ }
+ }
+ saveGeneratedPreviews(provider, previews, /* notify= */ true);
+ });
+ }
+
+ /**
+ * Remove previews for the specified provider on a background thread.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ private void removeGeneratedPreviewsAsync(@NonNull Provider provider, int widgetCategories) {
+ mSavePreviewsHandler.post(() -> {
+ SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
+ boolean changed = false;
+ for (int flag : Provider.WIDGET_CATEGORY_FLAGS) {
+ if ((widgetCategories & flag) != 0) {
+ changed |= previews.removeReturnOld(flag) != null;
+ }
+ }
+ if (changed) {
+ saveGeneratedPreviews(provider, previews, /* notify= */ true);
+ }
+ });
+ }
+
+ /**
+ * Clear previews for the specified provider on a background thread. Returns true if changed
+ * (i.e. there are previews to clear). If returns true, the caller should schedule a providers
+ * changed notification.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ private boolean clearGeneratedPreviewsAsync(@NonNull Provider provider) {
+ mSavePreviewsHandler.post(() -> {
+ saveGeneratedPreviews(provider, /* previews= */ null, /* notify= */ false);
+ });
+ return provider.info.generatedPreviewCategories != 0;
+ }
+
+ private void checkSavePreviewsThread() {
+ if (DEBUG && !mSavePreviewsHandler.getLooper().isCurrentThread()) {
+ throw new IllegalStateException("Only modify previews on the background thread");
+ }
+ }
+
+ /**
+ * Load previews from file for the given provider. If there are no previews, returns an empty
+ * SparseArray. Else, returns a SparseArray of the previews mapped by widget category.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ @NonNull
+ private SparseArray<RemoteViews> loadGeneratedPreviews(@NonNull Provider provider) {
+ checkSavePreviewsThread();
+ try {
+ AtomicFile previewsFile = getWidgetPreviewsFile(provider);
+ if (!previewsFile.exists()) {
+ return new SparseArray<>();
+ }
+ ProtoInputStream input = new ProtoInputStream(previewsFile.readFully());
+ SparseArray<RemoteViews> entries = readGeneratedPreviewsFromProto(input);
+ SparseArray<RemoteViews> singleCategoryKeyedEntries = new SparseArray<>();
+ for (int i = 0; i < entries.size(); i++) {
+ int widgetCategories = entries.keyAt(i);
+ RemoteViews preview = entries.valueAt(i);
+ for (int flag : Provider.WIDGET_CATEGORY_FLAGS) {
+ if ((widgetCategories & flag) != 0) {
+ singleCategoryKeyedEntries.put(flag, preview);
+ }
+ }
+ }
+ return singleCategoryKeyedEntries;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to load generated previews for " + provider, e);
+ return new SparseArray<>();
+ }
+ }
+
+ /**
+ * This is called when loading profile/group state to populate
+ * AppWidgetProviderInfo.generatedPreviewCategories based on what previews are saved.
+ *
+ * This is the only time previews are read while not on mSavePreviewsHandler. It happens once
+ * per profile during initialization, before any calls to get/set/removeWidgetPreviewAsync
+ * happen for that profile.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ @GuardedBy("mLock")
+ private void loadGeneratedPreviewCategoriesLocked(int profileId) throws IOException {
+ for (Provider provider : mProviders) {
+ if (provider.id.getProfile().getIdentifier() != profileId) {
+ continue;
+ }
+ AtomicFile previewsFile = getWidgetPreviewsFile(provider);
+ if (!previewsFile.exists()) {
+ continue;
+ }
+ ProtoInputStream input = new ProtoInputStream(previewsFile.readFully());
+ provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
+ input);
+ if (DEBUG) {
+ Slog.i(TAG, TextUtils.formatSimple(
+ "loadGeneratedPreviewCategoriesLocked %d %s categories %d", profileId,
+ provider, provider.info.generatedPreviewCategories));
+ }
+ }
+ }
+
+ /**
+ * Save the given previews into storage.
+ *
+ * @param provider Provider for which to save previews
+ * @param previews Previews to save. If null or empty, clears any saved previews for this
+ * provider.
+ * @param notify If true, then this function will notify hosts of updated provider info.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ private void saveGeneratedPreviews(@NonNull Provider provider,
+ @Nullable SparseArray<RemoteViews> previews, boolean notify) {
+ checkSavePreviewsThread();
+ AtomicFile file = null;
+ FileOutputStream stream = null;
+ try {
+ file = getWidgetPreviewsFile(provider);
+ if (previews == null || previews.size() == 0) {
+ if (file.exists()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Deleting widget preview file " + file);
+ }
+ file.delete();
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Writing widget preview file " + file);
+ }
+ ProtoOutputStream out = new ProtoOutputStream();
+ writePreviewsToProto(out, previews);
+ stream = file.startWrite();
+ stream.write(out.getBytes());
+ file.finishWrite(stream);
+ }
+
+ synchronized (mLock) {
+ provider.updateGeneratedPreviewCategoriesLocked(previews);
+ if (notify) {
+ scheduleNotifyGroupHostsForProvidersChangedLocked(provider.getUserId());
+ }
+ }
+ } catch (IOException e) {
+ if (file != null && stream != null) {
+ file.failWrite(stream);
+ }
+ Slog.w(TAG, "Failed to save widget previews for provider " + provider.id.componentName);
}
}
+
+ /**
+ * Write the given previews as a GeneratedPreviewsProto to the output stream.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ private void writePreviewsToProto(@NonNull ProtoOutputStream out,
+ @NonNull SparseArray<RemoteViews> generatedPreviews) {
+ // Collect RemoteViews mapped by hashCode in order to avoid writing duplicates.
+ SparseArray<Pair<Integer, RemoteViews>> previewsToWrite = new SparseArray<>();
+ for (int i = 0; i < generatedPreviews.size(); i++) {
+ int widgetCategory = generatedPreviews.keyAt(i);
+ RemoteViews views = generatedPreviews.valueAt(i);
+ if (!previewsToWrite.contains(views.hashCode())) {
+ previewsToWrite.put(views.hashCode(), new Pair<>(widgetCategory, views));
+ } else {
+ Pair<Integer, RemoteViews> entry = previewsToWrite.get(views.hashCode());
+ previewsToWrite.put(views.hashCode(),
+ Pair.create(entry.first | widgetCategory, views));
+ }
+ }
+
+ for (int i = 0; i < previewsToWrite.size(); i++) {
+ final long token = out.start(GeneratedPreviewsProto.PREVIEWS);
+ Pair<Integer, RemoteViews> entry = previewsToWrite.valueAt(i);
+ out.write(GeneratedPreviewsProto.Preview.WIDGET_CATEGORIES, entry.first);
+ final long viewsToken = out.start(GeneratedPreviewsProto.Preview.VIEWS);
+ entry.second.writePreviewToProto(mContext, out);
+ out.end(viewsToken);
+ out.end(token);
+ }
+ }
+
+ /**
+ * Read a GeneratedPreviewsProto message from the input stream.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ @NonNull
+ private SparseArray<RemoteViews> readGeneratedPreviewsFromProto(@NonNull ProtoInputStream input)
+ throws IOException {
+ SparseArray<RemoteViews> entries = new SparseArray<>();
+ while (input.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (input.getFieldNumber()) {
+ case (int) GeneratedPreviewsProto.PREVIEWS:
+ final long token = input.start(GeneratedPreviewsProto.PREVIEWS);
+ Pair<Integer, RemoteViews> entry = readSinglePreviewFromProto(input,
+ /* skipViews= */ false);
+ entries.put(entry.first, entry.second);
+ input.end(token);
+ break;
+ default:
+ Slog.w(TAG, "Unknown field while reading GeneratedPreviewsProto! "
+ + ProtoUtils.currentFieldToString(input));
+ }
+ }
+ return entries;
+ }
+
+ /**
+ * Read the widget categories from GeneratedPreviewsProto and return an int representing the
+ * combined widget categories of all the previews.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ @AppWidgetProviderInfo.CategoryFlags
+ private int readGeneratedPreviewCategoriesFromProto(@NonNull ProtoInputStream input)
+ throws IOException {
+ int widgetCategories = 0;
+ while (input.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (input.getFieldNumber()) {
+ case (int) GeneratedPreviewsProto.PREVIEWS:
+ final long token = input.start(GeneratedPreviewsProto.PREVIEWS);
+ Pair<Integer, RemoteViews> entry = readSinglePreviewFromProto(input,
+ /* skipViews= */ true);
+ widgetCategories |= entry.first;
+ input.end(token);
+ break;
+ default:
+ Slog.w(TAG, "Unknown field while reading GeneratedPreviewsProto! "
+ + ProtoUtils.currentFieldToString(input));
+ }
+ }
+ return widgetCategories;
+ }
+
+ /**
+ * Read a single GeneratedPreviewsProto.Preview message from the input stream, and returns a
+ * pair of widget category and corresponding RemoteViews. If skipViews is true, this function
+ * will only read widget categories and the returned RemoteViews will be null.
+ */
+ @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
+ @NonNull
+ private Pair<Integer, RemoteViews> readSinglePreviewFromProto(@NonNull ProtoInputStream input,
+ boolean skipViews) throws IOException {
+ int widgetCategories = 0;
+ RemoteViews views = null;
+ while (input.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (input.getFieldNumber()) {
+ case (int) GeneratedPreviewsProto.Preview.VIEWS:
+ if (skipViews) {
+ // ProtoInputStream will skip over the nested message when nextField() is
+ // called.
+ continue;
+ }
+ final long token = input.start(GeneratedPreviewsProto.Preview.VIEWS);
+ try {
+ views = RemoteViews.createPreviewFromProto(mContext, input);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to deserialize RemoteViews", e);
+ }
+ input.end(token);
+ break;
+ case (int) GeneratedPreviewsProto.Preview.WIDGET_CATEGORIES:
+ widgetCategories = input.readInt(
+ GeneratedPreviewsProto.Preview.WIDGET_CATEGORIES);
+ break;
+ default:
+ Slog.w(TAG, "Unknown field while reading GeneratedPreviewsProto! "
+ + ProtoUtils.currentFieldToString(input));
+ }
+ }
+ return Pair.create(widgetCategories, views);
+ }
+
+ /**
+ * Returns the file in which all generated previews for this provider are stored. This will be
+ * a path of the form:
+ * {@literal /data/system_ce/<userId>/appwidget/previews/<package>-<class>-<uid>.binpb}
+ *
+ * This function will not create the file if it does not already exist.
+ */
+ @NonNull
+ private static AtomicFile getWidgetPreviewsFile(@NonNull Provider provider) throws IOException {
+ int userId = provider.getUserId();
+ File previewsDirectory = getWidgetPreviewsDirectory(userId);
+ File providerPreviews = Environment.buildPath(previewsDirectory,
+ TextUtils.formatSimple("%s-%s-%d.binpb", provider.id.componentName.getPackageName(),
+ provider.id.componentName.getClassName(), provider.id.uid));
+ return new AtomicFile(providerPreviews);
+ }
+
+ /**
+ * Returns the widget previews directory for the given user, creating it if it does not exist.
+ * This will be a path of the form:
+ * {@literal /data/system_ce/<userId>/appwidget/previews}
+ */
+ @NonNull
+ private static File getWidgetPreviewsDirectory(int userId) throws IOException {
+ File dataSystemCeDirectory = Environment.getDataSystemCeDirectory(userId);
+ File previewsDirectory = Environment.buildPath(dataSystemCeDirectory,
+ APPWIDGET_CE_DATA_DIRNAME, WIDGET_PREVIEWS_DIRNAME);
+ if (!previewsDirectory.exists()) {
+ if (!previewsDirectory.mkdirs()) {
+ throw new IOException("Unable to create widget preview directory "
+ + previewsDirectory.getPath());
+ }
+ }
+ return previewsDirectory;
+ }
+
private static void ensureWidgetCategoryCombinationIsValid(int widgetCategories) {
int validCategories = AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
| AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
@@ -5414,11 +5806,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
}
if (newInfo != null) {
+ newInfo.generatedPreviewCategories = info.generatedPreviewCategories;
info = newInfo;
if (DEBUG) {
Objects.requireNonNull(info);
}
- updateGeneratedPreviewCategoriesLocked();
}
}
mInfoParsed = true;
@@ -5475,7 +5867,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
generatedPreviews.put(flag, preview);
}
}
- updateGeneratedPreviewCategoriesLocked();
+ updateGeneratedPreviewCategoriesLocked(generatedPreviews);
}
@GuardedBy("this.mLock")
@@ -5487,7 +5879,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
if (changed) {
- updateGeneratedPreviewCategoriesLocked();
+ updateGeneratedPreviewCategoriesLocked(generatedPreviews);
}
return changed;
}
@@ -5496,17 +5888,19 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
public boolean clearGeneratedPreviewsLocked() {
if (generatedPreviews.size() > 0) {
generatedPreviews.clear();
- updateGeneratedPreviewCategoriesLocked();
+ updateGeneratedPreviewCategoriesLocked(generatedPreviews);
return true;
}
return false;
}
-
@GuardedBy("this.mLock")
- private void updateGeneratedPreviewCategoriesLocked() {
+ private void updateGeneratedPreviewCategoriesLocked(
+ @Nullable SparseArray<RemoteViews> previews) {
info.generatedPreviewCategories = 0;
- for (int i = 0; i < generatedPreviews.size(); i++) {
- info.generatedPreviewCategories |= generatedPreviews.keyAt(i);
+ if (previews != null) {
+ for (int i = 0; i < previews.size(); i++) {
+ info.generatedPreviewCategories |= previews.keyAt(i);
+ }
}
}
diff --git a/services/art-profile b/services/art-profile
index 6fa4c88cb1f6..ce1e2c6f1397 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -5657,7 +5657,7 @@ Lcom/android/server/utils/WatchedSparseSetArray;
Lcom/android/server/utils/Watcher;
Lcom/android/server/vibrator/VibratorController$NativeWrapper;
Lcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;
-Lcom/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener;
+Lcom/android/server/vibrator/VibratorManagerService$VibratorManagerNativeCallbacks;
Lcom/android/server/vibrator/VibratorManagerService;
Lcom/android/server/vr/EnabledComponentsObserver$EnabledComponentChangeListener;
Lcom/android/server/vr/VrManagerService;
diff --git a/services/art-wear-profile b/services/art-wear-profile
index 47bdb1385137..1e3090f9bf00 100644
--- a/services/art-wear-profile
+++ b/services/art-wear-profile
@@ -1330,7 +1330,7 @@ Lcom/android/server/utils/WatchedSparseSetArray;
Lcom/android/server/utils/Watcher;
Lcom/android/server/vibrator/VibratorController$NativeWrapper;
Lcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;
-Lcom/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener;
+Lcom/android/server/vibrator/VibratorManagerService$VibratorManagerNativeCallbacks;
Lcom/android/server/vibrator/VibratorManagerService;
Lcom/android/server/vr/EnabledComponentsObserver$EnabledComponentChangeListener;
Lcom/android/server/vr/VrManagerService;
@@ -24948,7 +24948,7 @@ PLcom/android/server/vibrator/VibratorManagerService$NativeWrapper;-><init>()V
PLcom/android/server/vibrator/VibratorManagerService$NativeWrapper;->cancelSynced()V
PLcom/android/server/vibrator/VibratorManagerService$NativeWrapper;->getCapabilities()J
PLcom/android/server/vibrator/VibratorManagerService$NativeWrapper;->getVibratorIds()[I
-PLcom/android/server/vibrator/VibratorManagerService$NativeWrapper;->init(Lcom/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener;)V
+PLcom/android/server/vibrator/VibratorManagerService$NativeWrapper;->init(Lcom/android/server/vibrator/VibratorManagerService$VibratorManagerNativeCallbacks;)V
PLcom/android/server/vibrator/VibratorManagerService$VibrationCompleteListener;-><init>(Lcom/android/server/vibrator/VibratorManagerService;)V
PLcom/android/server/vibrator/VibratorManagerService$VibrationCompleteListener;->onComplete(IJ)V
PLcom/android/server/vibrator/VibratorManagerService$VibrationRecords;-><init>(II)V
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 1dc3b73d2bd3..bd46debf12ca 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -22,3 +22,11 @@ flag {
description: "Guards against Autofill-Credman Phase1 developer integration via new APIs"
bug: "320730001"
}
+
+flag {
+ name: "fill_dialog_improvements"
+ is_exported: true
+ namespace: "autofill"
+ description: "Improvements for Fill Dialog, including deprecation of pre-trigger API's"
+ bug: "336223371"
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index ddccb3731cc1..466d477992b3 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -281,6 +281,9 @@ public class UserBackupManagerService {
private static final int SCHEDULE_FILE_VERSION = 1;
public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
+
+ public static final String TELEPHONY_PROVIDER_PACKAGE = "com.android.providers.telephony";
+
public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
// Pseudoname that we use for the Package Manager metadata "package".
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index f24a3c1afc86..508b62cd83f0 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -21,6 +21,7 @@ import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+import static com.android.server.backup.UserBackupManagerService.TELEPHONY_PROVIDER_PACKAGE;
import static com.android.server.backup.UserBackupManagerService.WALLPAPER_PACKAGE;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -75,6 +76,12 @@ public class BackupEligibilityRules {
systemPackagesAllowedForProfileUser,
Sets.newArraySet(WALLPAPER_PACKAGE, SETTINGS_PACKAGE));
+ static {
+ if (UserManager.isHeadlessSystemUserMode()) {
+ systemPackagesAllowedForNonSystemUsers.add(TELEPHONY_PROVIDER_PACKAGE);
+ }
+ }
+
private final PackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
private final int mUserId;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 51034d24df14..7cba9e0ccca8 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -31,16 +31,13 @@ import static android.os.UserHandle.getCallingUserId;
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.MINUTES;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -69,31 +66,22 @@ import android.companion.datatransfer.PermissionSyncRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.MacAddress;
-import android.net.NetworkPolicyManager;
import android.os.Binder;
-import android.os.Environment;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerExemptionManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.flags.Flags;
-import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Slog;
-import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
@@ -114,35 +102,25 @@ import com.android.server.companion.devicepresence.DevicePresenceProcessor;
import com.android.server.companion.devicepresence.ObservableUuid;
import com.android.server.companion.devicepresence.ObservableUuidStore;
import com.android.server.companion.transport.CompanionTransportManager;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
@SuppressLint("LongLogTag")
public class CompanionDeviceManagerService extends SystemService {
private static final String TAG = "CDM_CompanionDeviceManagerService";
private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
-
- private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
- private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
private static final int MAX_CN_LENGTH = 500;
- private final ActivityTaskManagerInternal mAtmInternal;
- private final ActivityManagerInternal mAmInternal;
- private final IAppOpsService mAppOpsManager;
- private final PowerExemptionManager mPowerExemptionManager;
- private final PackageManagerInternal mPackageManagerInternal;
-
private final AssociationStore mAssociationStore;
private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
private final ObservableUuidStore mObservableUuidStore;
+
+ private final CompanionExemptionProcessor mCompanionExemptionProcessor;
private final AssociationRequestsProcessor mAssociationRequestsProcessor;
private final SystemDataTransferProcessor mSystemDataTransferProcessor;
private final BackupRestoreProcessor mBackupRestoreProcessor;
@@ -156,12 +134,15 @@ public class CompanionDeviceManagerService extends SystemService {
super(context);
final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
- mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
- mAppOpsManager = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
- mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
- mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ final PowerExemptionManager powerExemptionManager = context.getSystemService(
+ PowerExemptionManager.class);
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ final ActivityTaskManagerInternal atmInternal = LocalServices.getService(
+ ActivityTaskManagerInternal.class);
+ final ActivityManagerInternal amInternal = LocalServices.getService(
+ ActivityManagerInternal.class);
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
final UserManager userManager = context.getSystemService(UserManager.class);
final PowerManagerInternal powerManagerInternal = LocalServices.getService(
PowerManagerInternal.class);
@@ -173,25 +154,29 @@ public class CompanionDeviceManagerService extends SystemService {
// Init processors
mAssociationRequestsProcessor = new AssociationRequestsProcessor(context,
- mPackageManagerInternal, mAssociationStore);
- mBackupRestoreProcessor = new BackupRestoreProcessor(context, mPackageManagerInternal,
+ packageManagerInternal, mAssociationStore);
+ mBackupRestoreProcessor = new BackupRestoreProcessor(context, packageManagerInternal,
mAssociationStore, associationDiskStore, mSystemDataTransferRequestStore,
mAssociationRequestsProcessor);
mCompanionAppBinder = new CompanionAppBinder(context);
+ mCompanionExemptionProcessor = new CompanionExemptionProcessor(context,
+ powerExemptionManager, appOpsManager, packageManagerInternal, atmInternal,
+ amInternal, mAssociationStore);
+
mDevicePresenceProcessor = new DevicePresenceProcessor(context,
mCompanionAppBinder, userManager, mAssociationStore, mObservableUuidStore,
- powerManagerInternal);
+ powerManagerInternal, mCompanionExemptionProcessor);
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
mDisassociationProcessor = new DisassociationProcessor(context, activityManager,
- mAssociationStore, mPackageManagerInternal, mDevicePresenceProcessor,
+ mAssociationStore, packageManagerInternal, mDevicePresenceProcessor,
mCompanionAppBinder, mSystemDataTransferRequestStore, mTransportManager);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
- mPackageManagerInternal, mAssociationStore,
+ packageManagerInternal, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
// TODO(b/279663946): move context sync to a dedicated system service
@@ -202,7 +187,6 @@ public class CompanionDeviceManagerService extends SystemService {
public void onStart() {
// Init association stores
mAssociationStore.refreshCache();
- mAssociationStore.registerLocalListener(mAssociationStoreChangeListener);
// Init UUID store
mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId());
@@ -240,11 +224,8 @@ public class CompanionDeviceManagerService extends SystemService {
if (associations.isEmpty()) return;
- updateAtm(userId, associations);
-
- BackgroundThread.getHandler().sendMessageDelayed(
- obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
- MINUTES.toMillis(10));
+ mCompanionExemptionProcessor.updateAtm(userId, associations);
+ mCompanionExemptionProcessor.updateAutoRevokeExemptions();
}
@Override
@@ -262,9 +243,12 @@ public class CompanionDeviceManagerService extends SystemService {
if (!associationsForPackage.isEmpty()) {
Slog.i(TAG, "Package removed or data cleared for user=[" + userId + "], package=["
+ packageName + "]. Cleaning up CDM data...");
- }
- for (AssociationInfo association : associationsForPackage) {
- mDisassociationProcessor.disassociate(association.getId());
+
+ for (AssociationInfo association : associationsForPackage) {
+ mDisassociationProcessor.disassociate(association.getId());
+ }
+
+ mCompanionAppBinder.onPackageChanged(userId);
}
// Clear observable UUIDs for the package.
@@ -273,19 +257,16 @@ public class CompanionDeviceManagerService extends SystemService {
for (ObservableUuid uuid : uuidsTobeObserved) {
mObservableUuidStore.removeObservableUuid(userId, uuid.getUuid(), packageName);
}
-
- mCompanionAppBinder.onPackagesChanged(userId);
}
private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) {
- final List<AssociationInfo> associationsForPackage =
+ final List<AssociationInfo> associations =
mAssociationStore.getAssociationsByPackage(userId, packageName);
- for (AssociationInfo association : associationsForPackage) {
- updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
- association.getPackageName());
- }
+ if (!associations.isEmpty()) {
+ mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
- mCompanionAppBinder.onPackagesChanged(userId);
+ mCompanionAppBinder.onPackageChanged(userId);
+ }
}
private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
@@ -765,130 +746,6 @@ public class CompanionDeviceManagerService extends SystemService {
}
}
- /**
- * Update special access for the association's package
- */
- public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) {
- final PackageInfo packageInfo =
- getPackageInfo(getContext(), userId, packageName);
-
- Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo));
- }
-
- private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
- if (packageInfo == null) {
- return;
- }
-
- if (containsEither(packageInfo.requestedPermissions,
- android.Manifest.permission.RUN_IN_BACKGROUND,
- android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
- mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
- } else {
- try {
- mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
- } catch (UnsupportedOperationException e) {
- Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
- + " whitelist. It might due to the package is whitelisted by the system.");
- }
- }
-
- NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
- try {
- if (containsEither(packageInfo.requestedPermissions,
- android.Manifest.permission.USE_DATA_IN_BACKGROUND,
- android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
- networkPolicyManager.addUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- } else {
- networkPolicyManager.removeUidPolicy(
- packageInfo.applicationInfo.uid,
- NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
- }
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, e.getMessage());
- }
-
- exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
- }
-
- private void exemptFromAutoRevoke(String packageName, int uid) {
- try {
- mAppOpsManager.setMode(
- AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
- uid,
- packageName,
- AppOpsManager.MODE_IGNORED);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error while granting auto revoke exemption for " + packageName, e);
- }
- }
-
- private void updateAtm(int userId, List<AssociationInfo> associations) {
- final Set<Integer> companionAppUids = new ArraySet<>();
- for (AssociationInfo association : associations) {
- final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
- 0, userId);
- if (uid >= 0) {
- companionAppUids.add(uid);
- }
- }
- if (mAtmInternal != null) {
- mAtmInternal.setCompanionAppUids(userId, companionAppUids);
- }
- if (mAmInternal != null) {
- // Make a copy of the set and send it to ActivityManager.
- mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
- }
- }
-
- private void maybeGrantAutoRevokeExemptions() {
- Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
-
- PackageManager pm = getContext().getPackageManager();
- for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
- SharedPreferences pref = getContext().getSharedPreferences(
- new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
- Context.MODE_PRIVATE);
- if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
- continue;
- }
-
- try {
- final List<AssociationInfo> associations =
- mAssociationStore.getActiveAssociationsByUser(userId);
- for (AssociationInfo a : associations) {
- try {
- int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
- exemptFromAutoRevoke(a.getPackageName(), uid);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
- }
- }
- } finally {
- pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
- }
- }
- }
-
- private final AssociationStore.OnChangeListener mAssociationStoreChangeListener =
- new AssociationStore.OnChangeListener() {
- @Override
- public void onAssociationChanged(int changeType, AssociationInfo association) {
- Slog.d(TAG, "onAssociationChanged changeType=[" + changeType
- + "], association=[" + association);
-
- final int userId = association.getUserId();
- final List<AssociationInfo> updatedAssociations =
- mAssociationStore.getActiveAssociationsByUser(userId);
-
- updateAtm(userId, updatedAssociations);
- updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
- association.getPackageName());
- }
- };
-
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
@@ -911,10 +768,6 @@ public class CompanionDeviceManagerService extends SystemService {
}
};
- private static <T> boolean containsEither(T[] array, T a, T b) {
- return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
- }
-
private class LocalService implements CompanionDeviceManagerServiceInternal {
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
new file mode 100644
index 000000000000..ea2bc17dafcd
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionExemptionProcessor.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.companion.AssociationInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.NetworkPolicyManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.PowerExemptionManager;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
+import com.android.server.companion.association.AssociationStore;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressLint("LongLogTag")
+public class CompanionExemptionProcessor {
+
+ private static final String TAG = "CDM_CompanionExemptionProcessor";
+
+ private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
+ private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
+
+ private final Context mContext;
+ private final PowerExemptionManager mPowerExemptionManager;
+ private final AppOpsManager mAppOpsManager;
+ private final PackageManagerInternal mPackageManager;
+ private final ActivityTaskManagerInternal mAtmInternal;
+ private final ActivityManagerInternal mAmInternal;
+ private final AssociationStore mAssociationStore;
+
+ public CompanionExemptionProcessor(Context context, PowerExemptionManager powerExemptionManager,
+ AppOpsManager appOpsManager, PackageManagerInternal packageManager,
+ ActivityTaskManagerInternal atmInternal, ActivityManagerInternal amInternal,
+ AssociationStore associationStore) {
+ mContext = context;
+ mPowerExemptionManager = powerExemptionManager;
+ mAppOpsManager = appOpsManager;
+ mPackageManager = packageManager;
+ mAtmInternal = atmInternal;
+ mAmInternal = amInternal;
+ mAssociationStore = associationStore;
+
+ mAssociationStore.registerLocalListener(new AssociationStore.OnChangeListener() {
+ @Override
+ public void onAssociationChanged(int changeType, AssociationInfo association) {
+ final int userId = association.getUserId();
+ final List<AssociationInfo> updatedAssociations =
+ mAssociationStore.getActiveAssociationsByUser(userId);
+
+ updateAtm(userId, updatedAssociations);
+ }
+ });
+ }
+
+ /**
+ * Update ActivityManager and ActivityTaskManager exemptions
+ */
+ public void updateAtm(int userId, List<AssociationInfo> associations) {
+ final Set<Integer> companionAppUids = new ArraySet<>();
+ for (AssociationInfo association : associations) {
+ int uid = mPackageManager.getPackageUid(association.getPackageName(), 0, userId);
+ if (uid >= 0) {
+ companionAppUids.add(uid);
+ }
+ }
+ if (mAtmInternal != null) {
+ mAtmInternal.setCompanionAppUids(userId, companionAppUids);
+ }
+ if (mAmInternal != null) {
+ // Make a copy of the set and send it to ActivityManager.
+ mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
+ }
+ }
+
+ /**
+ * Update special access for the association's package
+ */
+ public void exemptPackage(int userId, String packageName, boolean hasPresentDevices) {
+ Slog.i(TAG, "Exempting package [" + packageName + "]...");
+
+ final PackageInfo packageInfo = getPackageInfo(mContext, userId, packageName);
+
+ Binder.withCleanCallingIdentity(
+ () -> exemptPackageAsSystem(userId, packageInfo, hasPresentDevices));
+ }
+
+ @SuppressLint("MissingPermission")
+ private void exemptPackageAsSystem(int userId, PackageInfo packageInfo,
+ boolean hasPresentDevices) {
+ if (packageInfo == null) {
+ return;
+ }
+
+ // If the app has run-in-bg permission and present devices, add it to power saver allowlist.
+ if (containsEither(packageInfo.requestedPermissions,
+ android.Manifest.permission.RUN_IN_BACKGROUND,
+ android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)
+ && hasPresentDevices) {
+ mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
+ } else {
+ try {
+ mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
+ + " allowlist. It might be due to the package being allowlisted by the"
+ + " system.");
+ }
+ }
+
+ // If the app has run-in-bg permission and present device, allow metered network use.
+ NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(mContext);
+ try {
+ if (containsEither(packageInfo.requestedPermissions,
+ android.Manifest.permission.USE_DATA_IN_BACKGROUND,
+ android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)
+ && hasPresentDevices) {
+ networkPolicyManager.addUidPolicy(
+ packageInfo.applicationInfo.uid,
+ NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+ } else {
+ networkPolicyManager.removeUidPolicy(
+ packageInfo.applicationInfo.uid,
+ NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, e.getMessage());
+ }
+
+ updateAutoRevokeExemption(packageInfo.packageName, packageInfo.applicationInfo.uid,
+ !mAssociationStore.getActiveAssociationsByPackage(userId,
+ packageInfo.packageName).isEmpty());
+ }
+
+ /**
+ * Update auto revoke exemptions.
+ * If the app has any association, exempt it from permission auto revoke.
+ */
+ public void updateAutoRevokeExemptions() {
+ Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");
+
+ PackageManager pm = mContext.getPackageManager();
+ for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
+ SharedPreferences pref = mContext.getSharedPreferences(
+ new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
+ Context.MODE_PRIVATE);
+ if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
+ continue;
+ }
+
+ try {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getActiveAssociationsByUser(userId);
+ Set<Pair<String, Integer>> exemptedPackages = new HashSet<>();
+ for (AssociationInfo a : associations) {
+ try {
+ int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
+ exemptedPackages.add(new Pair<>(a.getPackageName(), uid));
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
+ }
+ }
+ for (Pair<String, Integer> exemptedPackage : exemptedPackages) {
+ updateAutoRevokeExemption(exemptedPackage.first, exemptedPackage.second, true);
+ }
+ } finally {
+ pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ private void updateAutoRevokeExemption(String packageName, int uid, boolean hasAssociations) {
+ try {
+ mAppOpsManager.setMode(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ uid,
+ packageName,
+ hasAssociations ? MODE_IGNORED : MODE_ALLOWED);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error while granting auto revoke exemption for " + packageName, e);
+ }
+ }
+
+ private <T> boolean containsEither(T[] array, T a, T b) {
+ return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
+ }
+
+}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
index 60f46887fa5c..8307da5f7218 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java
@@ -95,7 +95,9 @@ public class CompanionAppBinder {
/**
* On package changed.
*/
- public void onPackagesChanged(@UserIdInt int userId) {
+ public void onPackageChanged(@UserIdInt int userId) {
+ // Note: To invalidate the user space for simplicity. We could alternatively manage each
+ // package, but that would easily cause errors if one case is mis-handled.
mCompanionServicesRegister.invalidate(userId);
}
@@ -299,12 +301,14 @@ public class CompanionAppBinder {
private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> {
@Override
- public synchronized @NonNull Map<String, List<ComponentName>> forUser(
+ @NonNull
+ public synchronized Map<String, List<ComponentName>> forUser(
@UserIdInt int userId) {
return super.forUser(userId);
}
- synchronized @NonNull List<ComponentName> forPackage(
+ @NonNull
+ synchronized List<ComponentName> forPackage(
@UserIdInt int userId, @NonNull String packageName) {
return forUser(userId).getOrDefault(packageName, Collections.emptyList());
}
@@ -314,7 +318,8 @@ public class CompanionAppBinder {
}
@Override
- protected final @NonNull Map<String, List<ComponentName>> create(@UserIdInt int userId) {
+ @NonNull
+ protected final Map<String, List<ComponentName>> create(@UserIdInt int userId) {
return PackageUtils.getCompanionServicesForUser(mContext, userId);
}
}
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index a374d279af0b..7b4dd7df8be3 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -57,6 +57,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.CompanionExemptionProcessor;
import com.android.server.companion.association.AssociationStore;
import java.io.PrintWriter;
@@ -101,6 +102,8 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
private final PowerManagerInternal mPowerManagerInternal;
@NonNull
private final UserManager mUserManager;
+ @NonNull
+ private final CompanionExemptionProcessor mCompanionExemptionProcessor;
// NOTE: Same association may appear in more than one of the following sets at the same time.
// (E.g. self-managed devices that have MAC addresses, could be reported as present by their
@@ -111,7 +114,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
@NonNull
private final Set<Integer> mNearbyBleDevices = new HashSet<>();
@NonNull
- private final Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
+ private final Set<Integer> mConnectedSelfManagedDevices = new HashSet<>();
@NonNull
private final Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
@NonNull
@@ -146,7 +149,8 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
@NonNull UserManager userManager,
@NonNull AssociationStore associationStore,
@NonNull ObservableUuidStore observableUuidStore,
- @NonNull PowerManagerInternal powerManagerInternal) {
+ @NonNull PowerManagerInternal powerManagerInternal,
+ @NonNull CompanionExemptionProcessor companionExemptionProcessor) {
mContext = context;
mCompanionAppBinder = companionAppBinder;
mAssociationStore = associationStore;
@@ -156,6 +160,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
mObservableUuidStore, this);
mBleDeviceProcessor = new BleDeviceProcessor(associationStore, this);
mPowerManagerInternal = powerManagerInternal;
+ mCompanionExemptionProcessor = companionExemptionProcessor;
}
/** Initialize {@link DevicePresenceProcessor} */
@@ -404,7 +409,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* nearby (for "self-managed" associations).
*/
public boolean isDevicePresent(int associationId) {
- return mReportedSelfManagedDevices.contains(associationId)
+ return mConnectedSelfManagedDevices.contains(associationId)
|| mConnectedBtDevices.contains(associationId)
|| mNearbyBleDevices.contains(associationId)
|| mSimulated.contains(associationId);
@@ -451,7 +456,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* notifyDeviceAppeared()}
*/
public void onSelfManagedDeviceConnected(int associationId) {
- onDevicePresenceEvent(mReportedSelfManagedDevices,
+ onDevicePresenceEvent(mConnectedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_APPEARED);
}
@@ -467,7 +472,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* notifyDeviceDisappeared()}
*/
public void onSelfManagedDeviceDisconnected(int associationId) {
- onDevicePresenceEvent(mReportedSelfManagedDevices,
+ onDevicePresenceEvent(mConnectedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_DISAPPEARED);
}
@@ -475,7 +480,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
* Marks a "self-managed" device as disconnected when binderDied.
*/
public void onSelfManagedDeviceReporterBinderDied(int associationId) {
- onDevicePresenceEvent(mReportedSelfManagedDevices,
+ onDevicePresenceEvent(mConnectedSelfManagedDevices,
associationId, EVENT_SELF_MANAGED_DISAPPEARED);
}
@@ -683,6 +688,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
if (association.shouldBindWhenPresent()) {
bindApplicationIfNeeded(userId, packageName, association.isSelfManaged());
+ mCompanionExemptionProcessor.exemptPackage(userId, packageName, true);
} else {
return;
}
@@ -715,6 +721,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
// Check if there are other devices associated to the app that are present.
if (!shouldBindPackage(userId, packageName)) {
mCompanionAppBinder.unbindCompanionApp(userId, packageName);
+ mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
}
break;
default:
@@ -940,7 +947,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
mConnectedBtDevices.remove(id);
mNearbyBleDevices.remove(id);
- mReportedSelfManagedDevices.remove(id);
+ mConnectedSelfManagedDevices.remove(id);
mSimulated.remove(id);
synchronized (mBtDisconnectedDevices) {
mBtDisconnectedDevices.remove(id);
@@ -1100,7 +1107,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
out.append("Companion Device Present: ");
if (mConnectedBtDevices.isEmpty()
&& mNearbyBleDevices.isEmpty()
- && mReportedSelfManagedDevices.isEmpty()) {
+ && mConnectedSelfManagedDevices.isEmpty()) {
out.append("<empty>\n");
return;
} else {
@@ -1130,11 +1137,11 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
}
out.append(" Self-Reported Devices: ");
- if (mReportedSelfManagedDevices.isEmpty()) {
+ if (mConnectedSelfManagedDevices.isEmpty()) {
out.append("<empty>\n");
} else {
out.append("\n");
- for (int associationId : mReportedSelfManagedDevices) {
+ for (int associationId : mConnectedSelfManagedDevices) {
AssociationInfo a = mAssociationStore.getAssociationById(associationId);
out.append(" ").append(a.toShortString()).append('\n');
}
diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
index ef39846f4750..8a4b1faf2df9 100644
--- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
+++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
@@ -59,14 +59,14 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
private int mObserverCount = 0;
@GuardedBy("mLock")
- private ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>();
+ private final ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>();
/**
* Mapping from camera ID to open camera app associations. Key is the camera id, value is the
* information of the app's uid and package name.
*/
@GuardedBy("mLock")
- private ArrayMap<String, OpenCameraInfo> mAppsToBlockOnVirtualDevice = new ArrayMap<>();
+ private final ArrayMap<String, OpenCameraInfo> mAppsToBlockOnVirtualDevice = new ArrayMap<>();
static class InjectionSessionData {
public int appUid;
@@ -179,6 +179,15 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
Slog.w(TAG, "Unexpected close with observers remaining: " + mObserverCount);
}
}
+ // Clean up camera injection sessions (if any).
+ synchronized (mLock) {
+ for (InjectionSessionData sessionData : mPackageToSessionData.values()) {
+ for (CameraInjectionSession session : sessionData.cameraIdToSession.values()) {
+ session.close();
+ }
+ }
+ mPackageToSessionData.clear();
+ }
mCameraManager.unregisterAvailabilityCallback(this);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 281a2ce0556b..8b5b93e96494 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1465,28 +1465,28 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
@NonNull IVirtualDisplayCallback callback) {
checkCallerIsDeviceOwner();
+
+ int displayId;
+ boolean showPointer;
+ boolean isTrustedDisplay;
GenericWindowPolicyController gwpc;
synchronized (mVirtualDeviceLock) {
gwpc = createWindowPolicyControllerLocked(virtualDisplayConfig.getDisplayCategories());
- }
- int displayId;
- displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, callback,
- this, gwpc, mOwnerPackageName);
- boolean isMirrorDisplay =
- mDisplayManagerInternal.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY;
- gwpc.setDisplayId(displayId, isMirrorDisplay);
- boolean isTrustedDisplay =
- (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
- == Display.FLAG_TRUSTED;
- if (!isTrustedDisplay) {
- if (getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) {
- throw new SecurityException("All displays must be trusted for devices with custom"
- + "clipboard policy.");
+ displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig,
+ callback, this, gwpc, mOwnerPackageName);
+ boolean isMirrorDisplay =
+ mDisplayManagerInternal.getDisplayIdToMirror(displayId)
+ != Display.INVALID_DISPLAY;
+ gwpc.setDisplayId(displayId, isMirrorDisplay);
+ isTrustedDisplay =
+ (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
+ == Display.FLAG_TRUSTED;
+ if (!isTrustedDisplay
+ && getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) {
+ throw new SecurityException("All displays must be trusted for devices with "
+ + "custom clipboard policy.");
}
- }
- boolean showPointer;
- synchronized (mVirtualDeviceLock) {
if (mVirtualDisplays.contains(displayId)) {
gwpc.unregisterRunningAppsChangedListener(this);
throw new IllegalStateException(
@@ -1523,6 +1523,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
private PowerManager.WakeLock createAndAcquireWakeLockForDisplay(int displayId) {
+ if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()) {
+ return null;
+ }
final long token = Binder.clearCallingIdentity();
try {
PowerManager powerManager = mContext.getSystemService(PowerManager.class);
@@ -1681,6 +1684,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
return mOwnerUid;
}
+ long getDimDurationMillis() {
+ return mParams.getDimDuration().toMillis();
+ }
+
+ long getScreenOffTimeoutMillis() {
+ return mParams.getScreenOffTimeout().toMillis();
+ }
+
@Override // Binder call
public int[] getDisplayIds() {
synchronized (mVirtualDeviceLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index f87e3c338df7..6729231d68ab 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.companion.virtual;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS;
import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
@@ -27,6 +28,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
+import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
@@ -41,11 +43,14 @@ import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtualnative.IVirtualDeviceManagerNative;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManagerInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.LocaleList;
@@ -88,7 +93,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-
@SuppressLint("LongLogTag")
public class VirtualDeviceManagerService extends SystemService {
@@ -101,6 +105,11 @@ public class VirtualDeviceManagerService extends SystemService {
AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+ /** Enable default device camera access for apps running on virtual devices. */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long ENABLE_DEFAULT_DEVICE_CAMERA_ACCESS = 371173368L;
+
/**
* A virtual device association id corresponding to no CDM association.
*/
@@ -110,7 +119,7 @@ public class VirtualDeviceManagerService extends SystemService {
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerNativeImpl mNativeImpl;
private final VirtualDeviceManagerInternal mLocalService;
- private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
+ private final VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
@@ -236,7 +245,7 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
- void onCameraAccessBlocked(int appUid) {
+ private void onCameraAccessBlocked(int appUid) {
ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
@@ -248,8 +257,13 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
- CameraAccessController getCameraAccessController(UserHandle userHandle) {
- if (Flags.streamCamera()) {
+ private CameraAccessController getCameraAccessController(UserHandle userHandle,
+ VirtualDeviceParams params, String callingPackage) {
+ if (CompatChanges.isChangeEnabled(ENABLE_DEFAULT_DEVICE_CAMERA_ACCESS, callingPackage,
+ userHandle)
+ && android.companion.virtualdevice.flags.Flags.defaultDeviceCameraAccessPolicy()
+ && (params.getDevicePolicy(POLICY_TYPE_DEFAULT_DEVICE_CAMERA_ACCESS)
+ == DEVICE_POLICY_DEFAULT)) {
return null;
}
int userId = userHandle.getIdentifier();
@@ -496,7 +510,8 @@ public class VirtualDeviceManagerService extends SystemService {
final UserHandle userHandle = getCallingUserHandle();
final CameraAccessController cameraAccessController =
- getCameraAccessController(userHandle);
+ getCameraAccessController(userHandle, params,
+ attributionSource.getPackageName());
final int deviceId = sNextUniqueIndex.getAndIncrement();
final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
@@ -576,7 +591,6 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
-
@Override // Binder call
public int getDeviceIdForDisplayId(int displayId) {
if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) {
@@ -911,6 +925,22 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public long getDimDurationMillisForDeviceId(int deviceId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
+ return virtualDevice == null ? -1 : virtualDevice.getDimDurationMillis();
+ }
+ }
+
+ @Override
+ public long getScreenOffTimeoutMillisForDeviceId(int deviceId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
+ return virtualDevice == null ? -1 : virtualDevice.getScreenOffTimeoutMillis();
+ }
+ }
+
+ @Override
public boolean isValidVirtualDeviceId(int deviceId) {
return mImpl.isValidVirtualDeviceId(deviceId);
}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index ea6351baf597..fd18fa856916 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -24,7 +24,8 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -261,24 +262,28 @@ public class ContextualSearchManagerService extends SystemService {
}
}
- private Intent getResolvedLaunchIntent() {
+ private Intent getResolvedLaunchIntent(int userId) {
synchronized (this) {
+ if(DEBUG_USER) Log.d(TAG, "Attempting to getResolvedLaunchIntent");
// If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
String csPkgName = getContextualSearchPackageName();
if (csPkgName.isEmpty()) {
// Return null if csPackageName is not specified.
+ if (DEBUG_USER) Log.w(TAG, "getContextualSearchPackageName is empty");
return null;
}
Intent launchIntent = new Intent(
ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH);
launchIntent.setPackage(csPkgName);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
- launchIntent, MATCH_FACTORY_ONLY);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(
+ launchIntent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
if (resolveInfo == null) {
+ if (DEBUG_USER) Log.w(TAG, "resolveInfo is null");
return null;
}
ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
if (componentName == null) {
+ if (DEBUG_USER) Log.w(TAG, "componentName is null");
return null;
}
launchIntent.setComponent(componentName);
@@ -286,9 +291,10 @@ public class ContextualSearchManagerService extends SystemService {
}
}
- private Intent getContextualSearchIntent(int entrypoint, CallbackToken mToken) {
- final Intent launchIntent = getResolvedLaunchIntent();
+ private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
+ final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
+ if (DEBUG_USER) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
return null;
}
@@ -341,6 +347,7 @@ public class ContextualSearchManagerService extends SystemService {
TYPE_NAVIGATION_BAR_PANEL,
TYPE_POINTER));
} else {
+ if (DEBUG_USER) Log.w(TAG, "Can't capture contextual screenshot: mWmInternal is null");
shb = null;
}
final Bitmap bm = shb != null ? shb.asBitmap() : null;
@@ -444,7 +451,7 @@ public class ContextualSearchManagerService extends SystemService {
@Override
public void startContextualSearch(int entrypoint) {
synchronized (this) {
- if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
+ if (DEBUG_USER) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
enforcePermission("startContextualSearch");
final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
@@ -455,7 +462,8 @@ public class ContextualSearchManagerService extends SystemService {
// server has READ_FRAME_BUFFER permission to get the screenshot and because only
// the system server can invoke non-exported activities.
Binder.withCleanCallingIdentity(() -> {
- Intent launchIntent = getContextualSearchIntent(entrypoint, mToken);
+ Intent launchIntent =
+ getContextualSearchIntent(entrypoint, callingUserId, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6cfd44bb2d1a..aea16b08df49 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,6 +158,7 @@ java_library_static {
"android.hardware.gnss-V2-java",
"android.hardware.vibrator-V3-java",
"app-compat-annotations",
+ "art_exported_aconfig_flags_lib",
"framework-tethering.stubs.module_lib",
"keepanno-annotations",
"service-art.stubs.system_server",
@@ -237,9 +238,11 @@ java_library_static {
"connectivity_flags_lib",
"device_config_service_flags_java",
"dreams_flags_lib",
+ "aconfig_flags_java",
"aconfig_new_storage_flags_lib",
"powerstats_flags_lib",
"locksettings_flags_lib",
+ "profiling_flags_lib",
],
javac_shard_size: 50,
javacflags: [
@@ -255,6 +258,13 @@ java_library_static {
"FlaggedApi",
],
},
+ jarjar_rules: ":services-jarjar-rules",
+ apex_available: ["//apex_available:platform"],
+}
+
+filegroup {
+ name: "services-jarjar-rules",
+ srcs: ["services-jarjar-rules.txt"],
}
java_genrule {
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 60b826b50045..289935acd5e2 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -132,4 +132,7 @@ public abstract class BatteryStatsInternal {
* @param uids the uids of all apps that have any alarm in this batch.
*/
public abstract void noteWakingAlarmBatch(long elapsedMillis, int... uids);
+
+ /** See PowerStatsUidResolver.mapUid(). */
+ public abstract int getOwnerUid(int uid);
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 78bc658d49c7..3dcca1433dec 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -22,6 +22,9 @@ import static android.os.Flags.stateOfHealthPublic;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import static com.android.server.health.Utils.copyV1Battery;
+import static java.lang.Math.abs;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -48,6 +51,7 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
+import android.os.Looper;
import android.os.OsProtoEnums;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -67,6 +71,7 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
@@ -84,6 +89,7 @@ import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@@ -149,19 +155,112 @@ public final class BatteryService extends SystemService {
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
private boolean mBatteryLevelCritical;
- private int mLastBatteryStatus;
- private int mLastBatteryHealth;
- private boolean mLastBatteryPresent;
- private int mLastBatteryLevel;
- private int mLastBatteryVoltage;
- private int mLastBatteryTemperature;
- private boolean mLastBatteryLevelCritical;
- private int mLastMaxChargingCurrent;
- private int mLastMaxChargingVoltage;
- private int mLastChargeCounter;
- private int mLastBatteryCycleCount;
- private int mLastChargingState;
- private int mLastBatteryCapacityLevel;
+
+ /**
+ * {@link HealthInfo#batteryStatus} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryStatus;
+ /**
+ * {@link HealthInfo#batteryHealth} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryHealth;
+ /**
+ * {@link HealthInfo#batteryPresent} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private boolean mLastBroadcastBatteryPresent;
+ /**
+ * {@link HealthInfo#batteryLevel} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryLevel;
+ /**
+ * {@link HealthInfo#batteryVoltageMillivolts} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryVoltage;
+ /**
+ * {@link HealthInfo#batteryTemperatureTenthsCelsius} value when
+ * {@link Intent#ACTION_BATTERY_CHANGED} broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryTemperature;
+ /**
+ * {@link #mBatteryLevelCritical} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: These values may be used for internal operations and/or to determine whether to trigger
+ * the broadcast or not.
+ */
+ private boolean mLastBroadcastBatteryLevelCritical;
+ /**
+ * {@link HealthInfo#maxChargingCurrentMicroamps} value when
+ * {@link Intent#ACTION_BATTERY_CHANGED} broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastMaxChargingCurrent;
+ /**
+ * {@link HealthInfo#maxChargingVoltageMicrovolts} value when
+ * {@link Intent#ACTION_BATTERY_CHANGED} broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastMaxChargingVoltage;
+ /**
+ * {@link HealthInfo#batteryChargeCounterUah} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastChargeCounter;
+ /**
+ * {@link HealthInfo#batteryCycleCount} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryCycleCount;
+ /**
+ * {@link HealthInfo#chargingState} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastChargingState;
+ /**
+ * {@link HealthInfo#batteryCapacityLevel} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: This value may be used for internal operations and/or to determine whether to trigger
+ * the {@link Intent#ACTION_BATTERY_CHANGED} broadcast or not.
+ */
+ private int mLastBroadcastBatteryCapacityLevel;
+ /**
+ * {@link #mPlugType} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: These values may be used for internal operations and/or to determine whether to trigger
+ * the broadcast or not.
+ */
+ private int mLastBroadcastPlugType = -1; // Extra state so we can detect first run
+ /**
+ * {@link #mInvalidCharger} value when {@link Intent#ACTION_BATTERY_CHANGED}
+ * broadcast was sent last.
+ * Note: These values may be used for internal operations and/or to determine whether to trigger
+ * the broadcast or not.
+ */
+ private int mLastBroadcastInvalidCharger;
/**
* The last seen charging policy. This requires the
* {@link android.Manifest.permission#BATTERY_STATS} permission and should therefore not be
@@ -172,7 +271,6 @@ public final class BatteryService extends SystemService {
private int mSequence = 1;
private int mInvalidCharger;
- private int mLastInvalidCharger;
private int mLowBatteryWarningLevel;
private int mLastLowBatteryWarningLevel;
@@ -184,7 +282,6 @@ public final class BatteryService extends SystemService {
private static String sSystemUiPackage;
private int mPlugType;
- private int mLastPlugType = -1; // Extra state so we can detect first run
private boolean mBatteryLevelLow;
@@ -197,6 +294,16 @@ public final class BatteryService extends SystemService {
private boolean mUpdatesStopped;
private boolean mBatteryInputSuspended;
+ /**
+ * Time when the voltage was updated last by HAL and we sent the
+ * {@link Intent#ACTION_BATTERY_CHANGED} broadcast.
+ * Note: This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast
+ * so it is possible that voltage was updated but we did not send the broadcast so in that
+ * case we do not update the time.
+ */
+ @VisibleForTesting
+ public long mLastBroadcastVoltageUpdateTime;
+
private Led mLed;
private boolean mSentLowBatteryBroadcast = false;
@@ -211,7 +318,8 @@ public final class BatteryService extends SystemService {
private final CopyOnWriteArraySet<BatteryManagerInternal.ChargingPolicyChangeListener>
mChargingPolicyChangeListeners = new CopyOnWriteArraySet<>();
- private static final Bundle BATTERY_CHANGED_OPTIONS = BroadcastOptions.makeBasic()
+ @VisibleForTesting
+ public static final Bundle BATTERY_CHANGED_OPTIONS = BroadcastOptions.makeBasic()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
.toBundle();
@@ -234,6 +342,25 @@ public final class BatteryService extends SystemService {
private static final int MSG_BROADCAST_POWER_CONNECTION_CHANGED = 2;
private static final int MSG_BROADCAST_BATTERY_LOW_OKAY = 3;
+ /**
+ * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We
+ * only send the broadcast and update the temperature value when the temp change is greater or
+ * equals to 1 degree celsius.
+ */
+ private static final int ABSOLUTE_DECI_CELSIUS_DIFF_FOR_TEMP_UPDATE = 10;
+ /**
+ * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We
+ * only send the broadcast if the last voltage was updated at least 20s seconds back and has a
+ * fluctuation of at least 1%.
+ */
+ private static final int TIME_DIFF_FOR_VOLTAGE_UPDATE_MS = 20000;
+ /**
+ * The value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We
+ * only send the broadcast if the last voltage was updated at least 20s seconds back and has a
+ * fluctuation of at least 1%.
+ */
+ private static final float BASE_POINT_DIFF_FOR_VOLTAGE_UPDATE = 0.01f;
+
private final Handler.Callback mLocalCallback = msg -> {
switch (msg.what) {
case MSG_BROADCAST_BATTERY_CHANGED: {
@@ -283,10 +410,19 @@ public final class BatteryService extends SystemService {
};
public BatteryService(Context context) {
+ this(context, Objects.requireNonNull(Looper.myLooper(),
+ "BatteryService uses handler!! Can't create handler inside thread that has not "
+ + "called Looper.prepare()"));
+ }
+
+ @VisibleForTesting
+ public BatteryService(Context context, @NonNull Looper looper) {
super(context);
+ Objects.requireNonNull(looper);
+
mContext = context;
- mHandler = new Handler(mLocalCallback, true /*async*/);
+ mHandler = new Handler(looper, mLocalCallback, true /*async*/);
mLed = new Led(context, getLocalService(LightsManager.class));
mBatteryStats = BatteryStatsService.getService();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -436,7 +572,7 @@ public final class BatteryService extends SystemService {
private boolean shouldSendBatteryLowLocked() {
final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
- final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
+ final boolean oldPlugged = mLastBroadcastPlugType != BATTERY_PLUGGED_NONE;
/* The ACTION_BATTERY_LOW broadcast is sent in these situations:
* - is just un-plugged (previously was plugged) and battery level is
@@ -447,7 +583,7 @@ public final class BatteryService extends SystemService {
return !plugged
&& mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mLowBatteryWarningLevel
- && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel
+ && (oldPlugged || mLastBroadcastBatteryLevel > mLowBatteryWarningLevel
|| mHealthInfo.batteryLevel > mLastLowBatteryWarningLevel);
}
@@ -515,7 +651,13 @@ public final class BatteryService extends SystemService {
}
}
- private void update(android.hardware.health.HealthInfo info) {
+ /**
+ * Updates the healthInfo and triggers the broadcast.
+ *
+ * @param info the new health info
+ */
+ @VisibleForTesting
+ public void update(android.hardware.health.HealthInfo info) {
traceBegin("HealthInfoUpdate");
Trace.traceCounter(
@@ -556,8 +698,8 @@ public final class BatteryService extends SystemService {
long dischargeDuration = 0;
mBatteryLevelCritical =
- mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
+ mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
mPlugType = plugType(mHealthInfo);
if (DEBUG) {
@@ -591,24 +733,28 @@ public final class BatteryService extends SystemService {
mHandler.post(this::notifyChargingPolicyChanged);
}
+ final boolean includeChargeCounter =
+ !com.android.server.flags.Flags.rateLimitBatteryChangedBroadcast()
+ && mHealthInfo.batteryChargeCounterUah != mLastBroadcastChargeCounter;
+
if (force
- || (mHealthInfo.batteryStatus != mLastBatteryStatus
- || mHealthInfo.batteryHealth != mLastBatteryHealth
- || mHealthInfo.batteryPresent != mLastBatteryPresent
- || mHealthInfo.batteryLevel != mLastBatteryLevel
- || mPlugType != mLastPlugType
- || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
- || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
- || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
- || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
- || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
- || mInvalidCharger != mLastInvalidCharger
- || mHealthInfo.batteryCycleCount != mLastBatteryCycleCount
- || mHealthInfo.chargingState != mLastChargingState
- || mHealthInfo.batteryCapacityLevel != mLastBatteryCapacityLevel)) {
-
- if (mPlugType != mLastPlugType) {
- if (mLastPlugType == BATTERY_PLUGGED_NONE) {
+ || (mHealthInfo.batteryStatus != mLastBroadcastBatteryStatus
+ || mHealthInfo.batteryHealth != mLastBroadcastBatteryHealth
+ || mHealthInfo.batteryPresent != mLastBroadcastBatteryPresent
+ || mHealthInfo.batteryLevel != mLastBroadcastBatteryLevel
+ || mPlugType != mLastBroadcastPlugType
+ || mHealthInfo.batteryVoltageMillivolts != mLastBroadcastBatteryVoltage
+ || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBroadcastBatteryTemperature
+ || mHealthInfo.maxChargingCurrentMicroamps != mLastBroadcastMaxChargingCurrent
+ || mHealthInfo.maxChargingVoltageMicrovolts != mLastBroadcastMaxChargingVoltage
+ || includeChargeCounter
+ || mInvalidCharger != mLastBroadcastInvalidCharger
+ || mHealthInfo.batteryCycleCount != mLastBroadcastBatteryCycleCount
+ || mHealthInfo.chargingState != mLastBroadcastChargingState
+ || mHealthInfo.batteryCapacityLevel != mLastBroadcastBatteryCapacityLevel)) {
+
+ if (mPlugType != mLastBroadcastPlugType) {
+ if (mLastBroadcastPlugType == BATTERY_PLUGGED_NONE) {
// discharging -> charging
mChargeStartLevel = mHealthInfo.batteryLevel;
mChargeStartTime = SystemClock.elapsedRealtime();
@@ -622,7 +768,8 @@ public final class BatteryService extends SystemService {
// There's no value in this data unless we've discharged at least once and the
// battery level has changed; so don't log until it does.
- if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
+ if (mDischargeStartTime != 0
+ && mDischargeStartLevel != mHealthInfo.batteryLevel) {
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
@@ -639,7 +786,7 @@ public final class BatteryService extends SystemService {
if (mChargeStartTime != 0 && chargeDuration != 0) {
final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
builder.setType(MetricsEvent.TYPE_DISMISS);
- builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType);
+ builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastBroadcastPlugType);
builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS,
chargeDuration);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
@@ -651,19 +798,20 @@ public final class BatteryService extends SystemService {
mChargeStartTime = 0;
}
}
- if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
- mHealthInfo.batteryHealth != mLastBatteryHealth ||
- mHealthInfo.batteryPresent != mLastBatteryPresent ||
- mPlugType != mLastPlugType) {
+ if (mHealthInfo.batteryStatus != mLastBroadcastBatteryStatus
+ || mHealthInfo.batteryHealth != mLastBroadcastBatteryHealth
+ || mHealthInfo.batteryPresent != mLastBroadcastBatteryPresent
+ || mPlugType != mLastBroadcastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
- mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
+ mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
+ mHealthInfo.batteryPresent ? 1 : 0,
mPlugType, mHealthInfo.batteryTechnology);
SystemProperties.set(
"debug.tracing.battery_status",
Integer.toString(mHealthInfo.batteryStatus));
SystemProperties.set("debug.tracing.plug_type", Integer.toString(mPlugType));
}
- if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
+ if (mHealthInfo.batteryLevel != mLastBroadcastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(
@@ -672,8 +820,8 @@ public final class BatteryService extends SystemService {
mHealthInfo.batteryVoltageMillivolts,
mHealthInfo.batteryTemperatureTenthsCelsius);
}
- if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
- mPlugType == BATTERY_PLUGGED_NONE) {
+ if (mBatteryLevelCritical && !mLastBroadcastBatteryLevelCritical
+ && mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
// if the battery is about to die.
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
@@ -684,7 +832,7 @@ public final class BatteryService extends SystemService {
// Should we now switch in to low battery mode?
if (mPlugType == BATTERY_PLUGGED_NONE
&& mHealthInfo.batteryStatus !=
- BatteryManager.BATTERY_STATUS_UNKNOWN
+ BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
@@ -692,7 +840,7 @@ public final class BatteryService extends SystemService {
// Should we now switch out of low battery mode?
if (mPlugType != BATTERY_PLUGGED_NONE) {
mBatteryLevelLow = false;
- } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
+ } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
mBatteryLevelLow = false;
} else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
// If being forced, the previous state doesn't matter, we will just
@@ -706,7 +854,7 @@ public final class BatteryService extends SystemService {
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
- if (mPlugType != 0 && mLastPlugType == 0) {
+ if (mPlugType != 0 && mLastBroadcastPlugType == 0) {
final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
@@ -726,8 +874,7 @@ public final class BatteryService extends SystemService {
}
});
}
- }
- else if (mPlugType == 0 && mLastPlugType != 0) {
+ } else if (mPlugType == 0 && mLastBroadcastPlugType != 0) {
final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
@@ -797,8 +944,14 @@ public final class BatteryService extends SystemService {
// We are doing this after sending the above broadcasts, so anything processing
// them will get the new sequence number at that point. (See for example how testing
// of JobScheduler's BatteryController works.)
- sendBatteryChangedIntentLocked(force);
- if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
+
+ boolean rateLimitBatteryChangedBroadcast = rateLimitBatteryChangedBroadcast(force);
+
+ if (!rateLimitBatteryChangedBroadcast) {
+ sendBatteryChangedIntentLocked(force);
+ }
+ if (mLastBroadcastBatteryLevel != mHealthInfo.batteryLevel
+ || mLastBroadcastPlugType != mPlugType) {
sendBatteryLevelChangedIntentLocked();
}
@@ -811,21 +964,24 @@ public final class BatteryService extends SystemService {
logOutlierLocked(dischargeDuration);
}
- mLastBatteryStatus = mHealthInfo.batteryStatus;
- mLastBatteryHealth = mHealthInfo.batteryHealth;
- mLastBatteryPresent = mHealthInfo.batteryPresent;
- mLastBatteryLevel = mHealthInfo.batteryLevel;
- mLastPlugType = mPlugType;
- mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
- mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
- mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
- mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
- mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
- mLastBatteryLevelCritical = mBatteryLevelCritical;
- mLastInvalidCharger = mInvalidCharger;
- mLastBatteryCycleCount = mHealthInfo.batteryCycleCount;
- mLastChargingState = mHealthInfo.chargingState;
- mLastBatteryCapacityLevel = mHealthInfo.batteryCapacityLevel;
+ // Only update the values when we send the broadcast
+ if (!rateLimitBatteryChangedBroadcast) {
+ mLastBroadcastBatteryStatus = mHealthInfo.batteryStatus;
+ mLastBroadcastBatteryHealth = mHealthInfo.batteryHealth;
+ mLastBroadcastBatteryPresent = mHealthInfo.batteryPresent;
+ mLastBroadcastBatteryLevel = mHealthInfo.batteryLevel;
+ mLastBroadcastPlugType = mPlugType;
+ mLastBroadcastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
+ mLastBroadcastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
+ mLastBroadcastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
+ mLastBroadcastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
+ mLastBroadcastChargeCounter = mHealthInfo.batteryChargeCounterUah;
+ mLastBroadcastBatteryLevelCritical = mBatteryLevelCritical;
+ mLastBroadcastInvalidCharger = mInvalidCharger;
+ mLastBroadcastBatteryCycleCount = mHealthInfo.batteryCycleCount;
+ mLastBroadcastChargingState = mHealthInfo.chargingState;
+ mLastBroadcastBatteryCapacityLevel = mHealthInfo.batteryCapacityLevel;
+ }
}
}
@@ -1089,6 +1245,74 @@ public final class BatteryService extends SystemService {
}
}
+ /**
+ * Rate limit's the broadcast based on the changes in temp, voltage and chargeCounter.
+ */
+ private boolean rateLimitBatteryChangedBroadcast(boolean forceUpdate) {
+ if (!com.android.server.flags.Flags.rateLimitBatteryChangedBroadcast()) {
+ return false;
+ }
+ if (mLastBroadcastBatteryVoltage == 0 || mLastBroadcastBatteryTemperature == 0) {
+ mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+ return false;
+ }
+
+ final boolean voltageUpdated =
+ mLastBroadcastBatteryVoltage != mHealthInfo.batteryVoltageMillivolts;
+ final boolean temperatureUpdated =
+ mLastBroadcastBatteryTemperature != mHealthInfo.batteryTemperatureTenthsCelsius;
+ final boolean otherStatesUpdated = forceUpdate
+ || mHealthInfo.batteryStatus != mLastBroadcastBatteryStatus
+ || mHealthInfo.batteryHealth != mLastBroadcastBatteryHealth
+ || mHealthInfo.batteryPresent != mLastBroadcastBatteryPresent
+ || mHealthInfo.batteryLevel != mLastBroadcastBatteryLevel
+ || mPlugType != mLastBroadcastPlugType
+ || mHealthInfo.maxChargingCurrentMicroamps != mLastBroadcastMaxChargingCurrent
+ || mHealthInfo.maxChargingVoltageMicrovolts != mLastBroadcastMaxChargingVoltage
+ || mInvalidCharger != mLastBroadcastInvalidCharger
+ || mHealthInfo.batteryCycleCount != mLastBroadcastBatteryCycleCount
+ || mHealthInfo.chargingState != mLastBroadcastChargingState
+ || mHealthInfo.batteryCapacityLevel != mLastBroadcastBatteryCapacityLevel;
+
+ // We only rate limit based on changes in the temp, voltage.
+ if (otherStatesUpdated) {
+
+ if (voltageUpdated) {
+ mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+ }
+ return false;
+ }
+
+ final float basePointDiff =
+ (float) (mLastBroadcastBatteryVoltage - mHealthInfo.batteryVoltageMillivolts)
+ / mLastBroadcastBatteryVoltage;
+
+ // We only send the broadcast if voltage change is greater than 1% and last voltage
+ // update was sent at least 20 seconds back.
+ if (voltageUpdated
+ && abs(basePointDiff) >= BASE_POINT_DIFF_FOR_VOLTAGE_UPDATE
+ && SystemClock.elapsedRealtime() - mLastBroadcastVoltageUpdateTime
+ >= TIME_DIFF_FOR_VOLTAGE_UPDATE_MS) {
+ mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+
+ return false;
+ }
+
+ // Only send the broadcast if the temperature update is greater than 1 degree celsius.
+ if (temperatureUpdated
+ && abs(
+ mLastBroadcastBatteryTemperature - mHealthInfo.batteryTemperatureTenthsCelsius)
+ >= ABSOLUTE_DECI_CELSIUS_DIFF_FOR_TEMP_UPDATE) {
+
+ if (voltageUpdated) {
+ mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime();
+ }
+ return false;
+ }
+
+ return true;
+ }
+
class Shell extends ShellCommand {
@Override
public int onCommand(String cmd) {
@@ -1399,6 +1623,10 @@ public final class BatteryService extends SystemService {
pw.println(" level: " + mHealthInfo.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts);
+ pw.println(" Time when the latest updated value of the voltage was sent via "
+ + "battery changed broadcast: " + mLastBroadcastVoltageUpdateTime);
+ pw.println(" The last voltage value sent via the battery changed broadcast: "
+ + mLastBroadcastBatteryVoltage);
pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
pw.println(" technology: " + mHealthInfo.batteryTechnology);
pw.println(" Charging state: " + mHealthInfo.chargingState);
@@ -1457,6 +1685,11 @@ public final class BatteryService extends SystemService {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
+ @VisibleForTesting
+ public Handler getHandlerForTest() {
+ return mHandler;
+ }
+
@SuppressLint("AndroidFrameworkRequiresPermission")
private static void sendBroadcastToAllUsers(Context context, Intent intent,
Bundle options) {
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 23cee9db2138..1588e0421675 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -53,6 +53,7 @@ import com.android.server.am.DropboxRateLimiter;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -400,9 +401,18 @@ public class BootReceiver extends BroadcastReceiver {
Slog.w(TAG, "Tombstone too large to add to DropBox: " + tombstone.toPath());
return;
}
- // Read the proto tombstone file as bytes.
- final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
+ // Read the proto tombstone file as bytes.
+ // Previously used Files.readAllBytes() which internally creates a ThreadLocal BufferCache
+ // via ChannelInputStream that isn't properly released. Switched to
+ // FileInputStream.transferTo() which avoids the NIO channels completely,
+ // preventing the memory leak while maintaining the same functionality.
+ final byte[] tombstoneBytes;
+ try (FileInputStream fis = new FileInputStream(tombstone);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ fis.transferTo(baos);
+ tombstoneBytes = baos.toByteArray();
+ }
final File tombstoneProtoWithHeaders = File.createTempFile(
tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
Files.setPosixFilePermissions(
diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java
index ee6d808aa549..8362079b9009 100644
--- a/services/core/java/com/android/server/ConsumerIrService.java
+++ b/services/core/java/com/android/server/ConsumerIrService.java
@@ -30,6 +30,8 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
+import com.android.server.utils.LazyJniRegistrar;
+
public class ConsumerIrService extends IConsumerIrService.Stub {
private static final String TAG = "ConsumerIrService";
@@ -39,6 +41,10 @@ public class ConsumerIrService extends IConsumerIrService.Stub {
private static native int halTransmit(int carrierFrequency, int[] pattern);
private static native int[] halGetCarrierFrequencies();
+ static {
+ LazyJniRegistrar.registerConsumerIrService();
+ }
+
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
private final boolean mHasNativeHal;
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index dbf144f0c63e..95ae11e28218 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -18,22 +18,30 @@ package com.android.server;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.ISerialManager;
import android.hardware.SerialManagerInternal;
import android.os.ParcelFileDescriptor;
import android.os.PermissionEnforcer;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import java.io.File;
+import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.function.Supplier;
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SerialService extends ISerialManager.Stub {
+ private static final String TAG = "SerialService";
+
private final Context mContext;
@GuardedBy("mSerialPorts")
@@ -50,7 +58,7 @@ public class SerialService extends ISerialManager.Stub {
final String[] serialPorts = getSerialPorts(context);
for (String serialPort : serialPorts) {
mSerialPorts.put(serialPort, () -> {
- return native_open(serialPort);
+ return tryOpen(serialPort);
});
}
}
@@ -130,5 +138,14 @@ public class SerialService extends ISerialManager.Stub {
}
};
- private native ParcelFileDescriptor native_open(String path);
+ private static @Nullable ParcelFileDescriptor tryOpen(String path) {
+ try {
+ FileDescriptor fd = Os.open(path, OsConstants.O_RDWR | OsConstants.O_NOCTTY, 0);
+ return new ParcelFileDescriptor(fd);
+ } catch (ErrnoException e) {
+ Slog.e(TAG, "Could not open: " + path, e);
+ // We return null to preserve API semantics from earlier implementation variants.
+ return null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 9d27731cecd4..b7bc4e4827ef 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -96,6 +96,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
+import android.os.PermissionEnforcer;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -3653,10 +3654,16 @@ class StorageManagerService extends IStorageManager.Stub
return mInternalStorageSize;
}
- @EnforcePermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@Override
public int getInternalStorageRemainingLifetime() throws RemoteException {
- super.getInternalStorageRemainingLifetime_enforcePermission();
+ PermissionEnforcer.fromContext(mContext)
+ .enforcePermissionAnyOf(
+ new String[] {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.ALLOCATE_AGGRESSIVE
+ },
+ getCallingPid(),
+ getCallingUid());
return mVold.getStorageRemainingLifetime();
}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 1082e6214935..99772c39bef1 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -47,6 +47,7 @@ import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.RoSystemFeatures;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.permission.PermissionAllowlist;
@@ -2000,6 +2001,13 @@ public class SystemConfig {
private void readSplitPermission(XmlPullParser parser, File permFile)
throws IOException, XmlPullParserException {
+ // If trunkstable feature flag disabled for this split permission, skip this tag.
+ if (ParsingPackageUtils.getAconfigFlags()
+ .skipCurrentElement(/* pkg= */ null, parser, /* allowNoNamespace= */ true)) {
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+
String splitPerm = parser.getAttributeValue(null, "name");
if (splitPerm == null) {
Slog.w(TAG, "<split-permission> without name in " + permFile + " at "
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 72a9a2d6de26..fa228627c255 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -88,6 +88,7 @@ import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.MediaQualityStatus;
+import android.telephony.satellite.NtnSignalStrength;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -440,6 +441,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private boolean[] mCarrierRoamingNtnEligible = null;
private List<IntArray> mCarrierRoamingNtnAvailableServices;
+ private NtnSignalStrength[] mCarrierRoamingNtnSignalStrength;
// Local cache to check if Satellite Modem is enabled
private AtomicBoolean mIsSatelliteEnabled;
@@ -745,6 +747,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mSCBMDuration = copyOf(mSCBMDuration, mNumPhones);
mCarrierRoamingNtnMode = copyOf(mCarrierRoamingNtnMode, mNumPhones);
mCarrierRoamingNtnEligible = copyOf(mCarrierRoamingNtnEligible, mNumPhones);
+ if (mCarrierRoamingNtnSignalStrength != null) {
+ mCarrierRoamingNtnSignalStrength = copyOf(
+ mCarrierRoamingNtnSignalStrength, mNumPhones);
+ } else {
+ mCarrierRoamingNtnSignalStrength = new NtnSignalStrength[mNumPhones];
+ }
// ds -> ss switch.
if (mNumPhones < oldNumPhones) {
cutListToSize(mCellInfo, mNumPhones);
@@ -807,6 +815,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCarrierRoamingNtnMode[i] = false;
mCarrierRoamingNtnEligible[i] = false;
mCarrierRoamingNtnAvailableServices.add(i, new IntArray());
+ mCarrierRoamingNtnSignalStrength[i] = new NtnSignalStrength(
+ NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE);
}
}
}
@@ -883,6 +893,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCarrierRoamingNtnMode = new boolean[numPhones];
mCarrierRoamingNtnEligible = new boolean[numPhones];
mCarrierRoamingNtnAvailableServices = new ArrayList<>();
+ mCarrierRoamingNtnSignalStrength = new NtnSignalStrength[numPhones];
mIsSatelliteEnabled = new AtomicBoolean();
mWasSatelliteEnabledNotified = new AtomicBoolean();
@@ -932,6 +943,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCarrierRoamingNtnMode[i] = false;
mCarrierRoamingNtnEligible[i] = false;
mCarrierRoamingNtnAvailableServices.add(i, new IntArray());
+ mCarrierRoamingNtnSignalStrength[i] = new NtnSignalStrength(
+ NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE);
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1565,6 +1578,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
+ if (events.contains(
+ TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED)) {
+ try {
+ r.callback.onCarrierRoamingNtnSignalStrengthChanged(
+ mCarrierRoamingNtnSignalStrength[r.phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
}
@@ -3803,6 +3825,44 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
+
+ /**
+ * Notify external listeners that carrier roaming non-terrestrial network
+ * signal strength changed.
+ * @param subId subscription ID.
+ * @param ntnSignalStrength non-terrestrial network signal strength.
+ */
+ public void notifyCarrierRoamingNtnSignalStrengthChanged(int subId,
+ @NonNull NtnSignalStrength ntnSignalStrength) {
+ if (!checkNotifyPermission("notifyCarrierRoamingNtnSignalStrengthChanged")) {
+ log("nnotifyCarrierRoamingNtnSignalStrengthChanged: caller does not have required "
+ + "permissions.");
+ return;
+ }
+
+ if (VDBG) {
+ log("notifyCarrierRoamingNtnSignalStrengthChanged: "
+ + "subId=" + subId + " ntnSignalStrength=" + ntnSignalStrength.getLevel());
+ }
+
+ synchronized (mRecords) {
+ int phoneId = getPhoneIdFromSubId(subId);
+ mCarrierRoamingNtnSignalStrength[phoneId] = ntnSignalStrength;
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCarrierRoamingNtnSignalStrengthChanged(ntnSignalStrength);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -3858,6 +3918,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mSCBMDuration=" + mSCBMDuration[i]);
pw.println("mCarrierRoamingNtnMode=" + mCarrierRoamingNtnMode[i]);
pw.println("mCarrierRoamingNtnEligible=" + mCarrierRoamingNtnEligible[i]);
+ pw.println("mCarrierRoamingNtnSignalStrength="
+ + mCarrierRoamingNtnSignalStrength[i]);
// We need to obfuscate package names, and primitive arrays' native toString is ugly
Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
diff --git a/services/core/java/com/android/server/TradeInModeService.java b/services/core/java/com/android/server/TradeInModeService.java
index 9ad550b6caf9..70a033086261 100644
--- a/services/core/java/com/android/server/TradeInModeService.java
+++ b/services/core/java/com/android/server/TradeInModeService.java
@@ -110,6 +110,8 @@ public final class TradeInModeService extends SystemService {
stopTradeInMode();
} else {
watchForSetupCompletion();
+ watchForNetworkChange();
+ watchForAccountsCreated();
}
}
}
diff --git a/services/core/java/com/android/server/adaptiveauth/OWNERS b/services/core/java/com/android/server/adaptiveauth/OWNERS
deleted file mode 100644
index b18810564d88..000000000000
--- a/services/core/java/com/android/server/adaptiveauth/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-hainingc@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1c3569dd52d0..dfddc089e4a4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -337,6 +337,8 @@ import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.Process;
+import android.os.ProfilingServiceHelper;
+import android.os.ProfilingTrigger;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -666,6 +668,8 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
private static final boolean ENABLE_PROC_LOCK = true;
+ private static final int DEFAULT_INTENT_CREATOR_UID = -1;
+
/**
* The lock for process management.
*
@@ -1089,7 +1093,18 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void onReportFullyDrawn(long id, long timestampNanos) {
- mProcessList.getAppStartInfoTracker().onActivityReportFullyDrawn(id, timestampNanos);
+ ApplicationStartInfo startInfo = mProcessList.getAppStartInfoTracker()
+ .onActivityReportFullyDrawn(id, timestampNanos);
+
+ if (android.os.profiling.Flags.systemTriggeredProfilingNew()
+ && startInfo != null
+ && startInfo.getStartType() == ApplicationStartInfo.START_TYPE_COLD
+ && startInfo.getPackageName() != null) {
+ ProfilingServiceHelper.getInstance().onProfilingTriggerOccurred(
+ startInfo.getRealUid(),
+ startInfo.getPackageName(),
+ ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN);
+ }
}
};
@@ -5155,6 +5170,11 @@ public class ActivityManagerService extends IActivityManager.Stub
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
if (pkgs != null) {
for (String pkg : pkgs) {
@@ -11332,7 +11352,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
pw.println(" mFgsStartTempAllowList:");
final long currentTimeNow = System.currentTimeMillis();
- final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
+ final long tempAllowlistCurrentTime =
+ com.android.server.deviceidle.Flags.useCpuTimeForTempAllowlist()
+ ? SystemClock.uptimeMillis() : SystemClock.elapsedRealtime();
mFgsStartTempAllowList.forEach((uid, entry) -> {
pw.print(" " + UserHandle.formatUid(uid) + ": ");
entry.second.dump(pw);
@@ -11340,7 +11362,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Convert entry.mExpirationTime, which is an elapsed time since boot,
// to a time since epoch (i.e. System.currentTimeMillis()-based time.)
final long expirationInCurrentTime =
- currentTimeNow - elapsedRealtimeNow + entry.first;
+ currentTimeNow - tempAllowlistCurrentTime + entry.first;
TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow);
pw.println();
});
@@ -19287,36 +19309,37 @@ public class ActivityManagerService extends IActivityManager.Stub
public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
if (!preventIntentRedirect()) return;
- if (intent == null || intent.getExtraIntentKeys() == null) return;
- for (String key : intent.getExtraIntentKeys()) {
- try {
- Intent extraIntent = intent.getParcelableExtra(key, Intent.class);
- if (extraIntent == null) {
- Slog.w(TAG, "The key {" + key
- + "} does not correspond to an intent in the extra bundle.");
- continue;
- }
- IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent,
- creatorPackage);
- if (creatorToken != null) {
- extraIntent.setCreatorToken(creatorToken);
- Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
- + creatorPackage + "; intent: " + intent);
- FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED,
- creatorToken.getCreatorUid());
- }
- } catch (Exception e) {
- Slog.wtf(TAG,
- "Something went wrong when trying to add creator token for embedded "
- + "intents of intent: ."
- + intent, e);
+ if (intent == null) return;
+
+ String targetPackage = intent.getComponent() != null
+ ? intent.getComponent().getPackageName()
+ : intent.getPackage();
+ final boolean isCreatorSameAsTarget = creatorPackage != null && creatorPackage.equals(
+ targetPackage);
+ final boolean noExtraIntentKeys =
+ intent.getExtraIntentKeys() == null || intent.getExtraIntentKeys().isEmpty();
+ final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : Binder.getCallingUid();
+
+ intent.forEachNestedCreatorToken(extraIntent -> {
+ if (isCreatorSameAsTarget) {
+ FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, true);
+ return;
}
- }
+ IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, creatorUid,
+ creatorPackage);
+ if (creatorToken != null) {
+ extraIntent.setCreatorToken(creatorToken);
+ // TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329
+ Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
+ + creatorPackage + "; intent: " + extraIntent);
+ FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, false);
+ }
+ });
}
- private IntentCreatorToken createIntentCreatorToken(Intent intent, String creatorPackage) {
+ private IntentCreatorToken createIntentCreatorToken(Intent intent, int creatorUid,
+ String creatorPackage) {
if (IntentCreatorToken.isValid(intent)) return null;
- int creatorUid = getCallingUid();
IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent);
IntentCreatorToken token;
synchronized (sIntentCreatorTokenCache) {
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 9fc0bf920969..6d594ac38187 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -20,7 +20,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.content.pm.ApplicationInfo;
-import android.os.Process;
+import android.os.ProfilingServiceHelper;
+import android.os.ProfilingTrigger;
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArraySet;
@@ -240,6 +241,15 @@ class AnrHelper {
|| startTime < SELF_ONLY_AFTER_BOOT_MS;
r.appNotResponding(onlyDumpSelf);
final long endTime = SystemClock.uptimeMillis();
+
+ if (android.os.profiling.Flags.systemTriggeredProfilingNew() && r.mAppInfo != null
+ && r.mAppInfo.packageName != null) {
+ ProfilingServiceHelper.getInstance().onProfilingTriggerOccurred(
+ r.mUid,
+ r.mAppInfo.packageName,
+ ProfilingTrigger.TRIGGER_TYPE_ANR);
+ }
+
Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
+ (endTime - startTime) + "ms, latency " + reportLatency
+ (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index aca6d0b0b967..3913d2f2ea92 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ApplicationStartInfo;
import android.app.Flags;
import android.app.IApplicationStartInfoCompleteListener;
@@ -419,23 +420,25 @@ public final class AppStartInfoTracker {
* Should only be called for Activity launch sequences from an instance of
* {@link ActivityMetricsLaunchObserver}.
*/
- void onActivityReportFullyDrawn(long id, long timestampNanos) {
+ @Nullable
+ ApplicationStartInfo onActivityReportFullyDrawn(long id, long timestampNanos) {
synchronized (mLock) {
if (!mEnabled) {
- return;
+ return null;
}
int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
- return;
+ return null;
}
ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null) {
mInProgressRecords.removeAt(index);
- return;
+ return null;
}
info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
timestampNanos);
mInProgressRecords.removeAt(index);
+ return info;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 7c563abea22f..2eb9f3cb600f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -124,6 +124,7 @@ import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
@@ -195,6 +196,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
private final PowerAttributor mPowerAttributor;
+ private final PowerManagerFlags mPowerManagerFlags = new PowerManagerFlags();
private volatile boolean mMonitorEnabled = true;
private boolean mRailsStatsCollectionEnabled = true;
@@ -453,7 +455,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
com.android.internal.R.integer.config_accumulatedBatteryUsageStatsSpanSize);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
mPowerAttributor, mPowerProfile, mCpuScalingPolicies,
- mPowerStatsStore, accumulatedBatteryUsageStatsSpanSize, Clock.SYSTEM_CLOCK);
+ mPowerStatsStore, accumulatedBatteryUsageStatsSpanSize, Clock.SYSTEM_CLOCK,
+ mMonotonicClock);
mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
@@ -616,6 +619,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
BatteryConsumer.POWER_COMPONENT_ANY,
Flags.streamlinedMiscBatteryStats());
+ mStats.setMoveWscLoggingToNotifierEnabled(
+ mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled());
+
mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
@@ -759,6 +765,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
public void noteWakingAlarmBatch(long elapsedMillis, int... uids) {
noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, elapsedMillis, uids);
}
+
+ @Override
+ public int getOwnerUid(int uid) {
+ if (Process.isSdkSandboxUid(uid)) {
+ return Process.getAppUidForSdkSandboxUid(uid);
+ }
+ return mPowerStatsUidResolver.mapUid(uid);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index b0f880710eb6..8a128582c507 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -593,7 +593,7 @@ class BroadcastController {
originalStickyCallingUid, BackgroundStartPrivileges.NONE,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
null /* filterExtrasForReceiver */,
- broadcast.originalCallingAppProcessState);
+ broadcast.originalCallingAppProcessState, mService.mPlatformCompat);
queue.enqueueBroadcastLocked(r);
}
}
@@ -1631,7 +1631,7 @@ class BroadcastController {
receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
ordered, sticky, false, userId,
backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
- callerAppProcessState);
+ callerAppProcessState, mService.mPlatformCompat);
broadcastSentEventRecord.setBroadcastRecord(r);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index f908c67d7ec9..38df10a0bc8c 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -42,6 +42,8 @@ import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions.DeliveryGroupPolicy;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -55,10 +57,12 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.PrintWriterPrinter;
+import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.compat.PlatformCompat;
import dalvik.annotation.optimization.NeverCompile;
@@ -77,6 +81,15 @@ import java.util.function.BiFunction;
* An active intent broadcast.
*/
final class BroadcastRecord extends Binder {
+ /**
+ * Limit the scope of the priority values to the process level. This means that priority values
+ * will only influence the order of broadcast delivery within the same process.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
+ @VisibleForTesting
+ static final long CHANGE_LIMIT_PRIORITY_SCOPE = 371307720L;
+
final @NonNull Intent intent; // the original intent that generated us
final @Nullable ComponentName targetComp; // original component name set on the intent
final @Nullable ProcessRecord callerApp; // process that sent this
@@ -417,13 +430,13 @@ final class BroadcastRecord extends Binder {
@NonNull BackgroundStartPrivileges backgroundStartPrivileges,
boolean timeoutExempt,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
- int callerAppProcessState) {
+ int callerAppProcessState, PlatformCompat platformCompat) {
this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid,
callingUid, callerInstantApp, resolvedType, requiredPermissions,
excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp,
resultTo, resultCode, resultData, resultExtras, serialized, sticky,
initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt,
- filterExtrasForReceiver, callerAppProcessState);
+ filterExtrasForReceiver, callerAppProcessState, platformCompat);
}
BroadcastRecord(BroadcastQueue _queue,
@@ -439,7 +452,7 @@ final class BroadcastRecord extends Binder {
@NonNull BackgroundStartPrivileges backgroundStartPrivileges,
boolean timeoutExempt,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
- int callerAppProcessState) {
+ int callerAppProcessState, PlatformCompat platformCompat) {
if (_intent == null) {
throw new NullPointerException("Can't construct with a null intent");
}
@@ -466,7 +479,8 @@ final class BroadcastRecord extends Binder {
urgent = calculateUrgent(_intent, _options);
deferUntilActive = calculateDeferUntilActive(_callingUid,
_options, _resultTo, _serialized, urgent);
- blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized);
+ blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(
+ receivers, _serialized, platformCompat);
scheduledTime = new long[delivery.length];
terminalTime = new long[delivery.length];
resultToApp = _resultToApp;
@@ -730,7 +744,8 @@ final class BroadcastRecord extends Binder {
}
/**
- * Determine if the result of {@link #calculateBlockedUntilBeyondCount(List, boolean)}
+ * Determine if the result of
+ * {@link #calculateBlockedUntilBeyondCount(List, boolean, PlatformCompat)}
* has prioritized tranches of receivers.
*/
@VisibleForTesting
@@ -754,37 +769,121 @@ final class BroadcastRecord extends Binder {
*/
@VisibleForTesting
static @NonNull int[] calculateBlockedUntilBeyondCount(
- @NonNull List<Object> receivers, boolean ordered) {
+ @NonNull List<Object> receivers, boolean ordered, PlatformCompat platformCompat) {
final int N = receivers.size();
final int[] blockedUntilBeyondCount = new int[N];
- int lastPriority = 0;
- int lastPriorityIndex = 0;
- for (int i = 0; i < N; i++) {
- if (ordered) {
- // When sending an ordered broadcast, we need to block this
- // receiver until all previous receivers have terminated
+ if (ordered) {
+ // When sending an ordered broadcast, we need to block this
+ // receiver until all previous receivers have terminated
+ for (int i = 0; i < N; i++) {
blockedUntilBeyondCount[i] = i;
+ }
+ } else {
+ if (Flags.limitPriorityScope()) {
+ final boolean[] changeEnabled = calculateChangeStateForReceivers(
+ receivers, CHANGE_LIMIT_PRIORITY_SCOPE, platformCompat);
+
+ // Priority of the previous tranche
+ int lastTranchePriority = 0;
+ // Priority of the current tranche
+ int currentTranchePriority = 0;
+ // Index of the last receiver in the previous tranche
+ int lastTranchePriorityIndex = -1;
+ // Index of the last receiver with change disabled in the previous tranche
+ int lastTrancheChangeDisabledIndex = -1;
+ // Index of the last receiver with change disabled in the current tranche
+ int currentTrancheChangeDisabledIndex = -1;
+
+ for (int i = 0; i < N; i++) {
+ final int thisPriority = getReceiverPriority(receivers.get(i));
+ if (i == 0) {
+ currentTranchePriority = thisPriority;
+ if (!changeEnabled[i]) {
+ currentTrancheChangeDisabledIndex = i;
+ }
+ continue;
+ }
+
+ // Check if a new priority tranche has started
+ if (thisPriority != currentTranchePriority) {
+ // Update tranche boundaries and reset the disabled index.
+ if (currentTrancheChangeDisabledIndex != -1) {
+ lastTrancheChangeDisabledIndex = currentTrancheChangeDisabledIndex;
+ }
+ lastTranchePriority = currentTranchePriority;
+ lastTranchePriorityIndex = i - 1;
+ currentTranchePriority = thisPriority;
+ currentTrancheChangeDisabledIndex = -1;
+ }
+ if (!changeEnabled[i]) {
+ currentTrancheChangeDisabledIndex = i;
+
+ // Since the change is disabled, block the current receiver until the
+ // last receiver in the previous tranche.
+ blockedUntilBeyondCount[i] = lastTranchePriorityIndex + 1;
+ } else if (thisPriority != lastTranchePriority) {
+ // If the changeId was disabled for an earlier receiver and the current
+ // receiver has a different priority, block the current receiver
+ // until that earlier receiver.
+ if (lastTrancheChangeDisabledIndex != -1) {
+ blockedUntilBeyondCount[i] = lastTrancheChangeDisabledIndex + 1;
+ }
+ }
+ }
+ // If the entire list is in the same priority tranche or no receivers had
+ // changeId disabled, mark as -1 to indicate that none of them need to wait
+ if (N > 0 && (lastTranchePriorityIndex == -1
+ || (lastTrancheChangeDisabledIndex == -1
+ && currentTrancheChangeDisabledIndex == -1))) {
+ Arrays.fill(blockedUntilBeyondCount, -1);
+ }
} else {
// When sending a prioritized broadcast, we only need to wait
// for the previous tranche of receivers to be terminated
- final int thisPriority = getReceiverPriority(receivers.get(i));
- if ((i == 0) || (thisPriority != lastPriority)) {
- lastPriority = thisPriority;
- lastPriorityIndex = i;
- blockedUntilBeyondCount[i] = i;
- } else {
- blockedUntilBeyondCount[i] = lastPriorityIndex;
+ int lastPriority = 0;
+ int lastPriorityIndex = 0;
+ for (int i = 0; i < N; i++) {
+ final int thisPriority = getReceiverPriority(receivers.get(i));
+ if ((i == 0) || (thisPriority != lastPriority)) {
+ lastPriority = thisPriority;
+ lastPriorityIndex = i;
+ blockedUntilBeyondCount[i] = i;
+ } else {
+ blockedUntilBeyondCount[i] = lastPriorityIndex;
+ }
+ }
+ // If the entire list is in the same priority tranche, mark as -1 to
+ // indicate that none of them need to wait
+ if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {
+ Arrays.fill(blockedUntilBeyondCount, -1);
}
}
}
- // If the entire list is in the same priority tranche, mark as -1 to
- // indicate that none of them need to wait
- if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {
- Arrays.fill(blockedUntilBeyondCount, -1);
- }
return blockedUntilBeyondCount;
}
+ @VisibleForTesting
+ static @NonNull boolean[] calculateChangeStateForReceivers(@NonNull List<Object> receivers,
+ long changeId, PlatformCompat platformCompat) {
+ final SparseBooleanArray changeStateForUids = new SparseBooleanArray();
+ final int count = receivers.size();
+ final boolean[] changeStateForReceivers = new boolean[count];
+ for (int i = 0; i < count; ++i) {
+ final int receiverUid = getReceiverUid(receivers.get(i));
+ final boolean isChangeEnabled;
+ final int idx = changeStateForUids.indexOfKey(receiverUid);
+ if (idx >= 0) {
+ isChangeEnabled = changeStateForUids.valueAt(idx);
+ } else {
+ isChangeEnabled = platformCompat.isChangeEnabledByUidInternalNoLogging(
+ changeId, receiverUid);
+ changeStateForUids.put(receiverUid, isChangeEnabled);
+ }
+ changeStateForReceivers[i] = isChangeEnabled;
+ }
+ return changeStateForReceivers;
+ }
+
static int getReceiverUid(@NonNull Object receiver) {
if (receiver instanceof BroadcastFilter) {
return ((BroadcastFilter) receiver).owningUid;
diff --git a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
index f4a931f89551..d2af84cf3d30 100644
--- a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
+++ b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
@@ -19,7 +19,6 @@ package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerService.checkComponentPermission;
import static com.android.server.am.BroadcastQueue.TAG;
-import static com.android.server.am.Flags.usePermissionManagerForBroadcastDeliveryCheck;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -289,33 +288,16 @@ public class BroadcastSkipPolicy {
if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- final AttributionSource[] attributionSources;
- if (usePermissionManagerForBroadcastDeliveryCheck()) {
- attributionSources = createAttributionSourcesForResolveInfo(info);
- } else {
- attributionSources = null;
- }
+ final AttributionSource[] attributionSources =
+ createAttributionSourcesForResolveInfo(info);
for (int i = 0; i < r.requiredPermissions.length; i++) {
String requiredPermission = r.requiredPermissions[i];
- try {
- if (usePermissionManagerForBroadcastDeliveryCheck()) {
- perm = hasPermissionForDataDelivery(
- requiredPermission,
- "Broadcast delivered to " + info.activityInfo.name,
- attributionSources)
- ? PackageManager.PERMISSION_GRANTED
- : PackageManager.PERMISSION_DENIED;
- } else {
- perm = AppGlobals.getPackageManager()
- .checkPermission(
- requiredPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- }
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
+ perm = hasPermissionForDataDelivery(
+ requiredPermission,
+ "Broadcast delivered to " + info.activityInfo.name,
+ attributionSources)
+ ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED;
if (perm != PackageManager.PERMISSION_GRANTED) {
return "Permission Denial: receiving "
+ r.intent + " to "
@@ -324,15 +306,6 @@ public class BroadcastSkipPolicy {
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")";
}
- if (!usePermissionManagerForBroadcastDeliveryCheck()) {
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
- if (!noteOpForManifestReceiver(appOp, r, info, component)) {
- return "Skipping delivery to " + info.activityInfo.packageName
- + " due to required appop " + appOp;
- }
- }
- }
}
}
if (r.appOp != AppOpsManager.OP_NONE) {
@@ -452,35 +425,20 @@ public class BroadcastSkipPolicy {
// Check that the receiver has the required permission(s) to receive this broadcast.
if (r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- final AttributionSource attributionSource;
- if (usePermissionManagerForBroadcastDeliveryCheck()) {
- attributionSource =
- new AttributionSource.Builder(filter.receiverList.uid)
- .setPid(filter.receiverList.pid)
- .setPackageName(filter.packageName)
- .setAttributionTag(filter.featureId)
- .build();
- } else {
- attributionSource = null;
- }
+ final AttributionSource attributionSource =
+ new AttributionSource.Builder(filter.receiverList.uid)
+ .setPid(filter.receiverList.pid)
+ .setPackageName(filter.packageName)
+ .setAttributionTag(filter.featureId)
+ .build();
for (int i = 0; i < r.requiredPermissions.length; i++) {
String requiredPermission = r.requiredPermissions[i];
- final int perm;
- if (usePermissionManagerForBroadcastDeliveryCheck()) {
- perm = hasPermissionForDataDelivery(
- requiredPermission,
- "Broadcast delivered to registered receiver " + filter.receiverId,
- attributionSource)
- ? PackageManager.PERMISSION_GRANTED
- : PackageManager.PERMISSION_DENIED;
- } else {
- perm = checkComponentPermission(
- requiredPermission,
- filter.receiverList.pid,
- filter.receiverList.uid,
- -1 /* owningUid */,
- true /* exported */);
- }
+ final int perm = hasPermissionForDataDelivery(
+ requiredPermission,
+ "Broadcast delivered to registered receiver " + filter.receiverId,
+ attributionSource)
+ ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED;
if (perm != PackageManager.PERMISSION_GRANTED) {
return "Permission Denial: receiving "
+ r.intent.toString()
@@ -491,24 +449,6 @@ public class BroadcastSkipPolicy {
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")";
}
- if (!usePermissionManagerForBroadcastDeliveryCheck()) {
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
- && mService.getAppOpsManager().noteOpNoThrow(appOp,
- filter.receiverList.uid, filter.packageName, filter.featureId,
- "Broadcast delivered to registered receiver " + filter.receiverId)
- != AppOpsManager.MODE_ALLOWED) {
- return "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(
- requiredPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")";
- }
- }
}
}
if ((r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 416c11090515..da5b1fd1c079 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -101,7 +101,7 @@ import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executor;
-public final class CachedAppOptimizer {
+public class CachedAppOptimizer {
// Flags stored in the DeviceConfig API.
@VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
diff --git a/services/core/java/com/android/server/am/FgsTempAllowList.java b/services/core/java/com/android/server/am/FgsTempAllowList.java
index c28655655765..5569b6db3285 100644
--- a/services/core/java/com/android/server/am/FgsTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsTempAllowList.java
@@ -29,7 +29,7 @@ import java.util.function.BiConsumer;
/**
* List of keys that have expiration time.
- * If the expiration time is less than current elapsedRealtime, the key has expired.
+ * If the expiration time is less than current uptime, the key has expired.
* Otherwise it is valid (or allowed).
*
* <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p>
@@ -42,7 +42,7 @@ public class FgsTempAllowList<E> {
private static final int DEFAULT_MAX_SIZE = 100;
/**
- * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime),
+ * The value is Pair type, Pair.first is the expirationTime(in cpu uptime),
* Pair.second is the optional information entry about this key.
*/
private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>();
@@ -82,7 +82,9 @@ public class FgsTempAllowList<E> {
}
// The temp allowlist should be a short list with only a few entries in it.
// for a very large list, HashMap structure should be used.
- final long now = SystemClock.elapsedRealtime();
+ final long now = com.android.server.deviceidle.Flags.useCpuTimeForTempAllowlist()
+ ? SystemClock.uptimeMillis()
+ : SystemClock.elapsedRealtime();
final int size = mTempAllowList.size();
if (size > mMaxSize) {
Slog.w(TAG_AM, "FgsTempAllowList length:" + size + " exceeds maxSize"
@@ -112,12 +114,15 @@ public class FgsTempAllowList<E> {
final int index = mTempAllowList.indexOfKey(uid);
if (index < 0) {
return null;
- } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) {
+ }
+ final long timeNow = com.android.server.deviceidle.Flags.useCpuTimeForTempAllowlist()
+ ? SystemClock.uptimeMillis()
+ : SystemClock.elapsedRealtime();
+ if (mTempAllowList.valueAt(index).first < timeNow) {
mTempAllowList.removeAt(index);
return null;
- } else {
- return mTempAllowList.valueAt(index);
}
+ return mTempAllowList.valueAt(index);
}
}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index c1d55971bdce..ab7cd5f29660 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -63,6 +63,9 @@ per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
+# Activity Security
+per-file ActivityManager* = file:/ACTIVITY_SECURITY_OWNERS
+
# Londoners
michaelwr@google.com #{LAST_RESORT_SUGGESTION}
narayan@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index de3e2c90efe1..b84bf6b90711 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
@@ -114,6 +115,7 @@ import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
+import static com.android.server.am.ProcessList.PREVIOUS_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
import static com.android.server.am.ProcessList.SCHED_GROUP_DEFAULT;
import static com.android.server.am.ProcessList.SCHED_GROUP_FOREGROUND_WINDOW;
@@ -154,6 +156,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -468,7 +471,6 @@ public class OomAdjuster {
}
Process.setThreadPriority(tid, priority);
}
-
}
// TODO(b/346822474): hook up global state usage.
@@ -498,7 +500,8 @@ public class OomAdjuster {
}
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
- ServiceThread adjusterThread, GlobalState globalState, Injector injector) {
+ ServiceThread adjusterThread, GlobalState globalState,
+ CachedAppOptimizer cachedAppOptimizer, Injector injector) {
mService = service;
mGlobalState = globalState;
mInjector = injector;
@@ -507,33 +510,17 @@ public class OomAdjuster {
mActiveUids = activeUids;
mConstants = mService.mConstants;
- mCachedAppOptimizer = new CachedAppOptimizer(mService);
+ mCachedAppOptimizer = cachedAppOptimizer;
mCacheOomRanker = new CacheOomRanker(service);
mLogger = new OomAdjusterDebugLogger(this, mService.mConstants);
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
- final int pid = msg.arg1;
- final int group = msg.arg2;
- if (pid == ActivityManagerService.MY_PID) {
- // Skip setting the process group for system_server, keep it as default.
- return true;
- }
- final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- if (traceEnabled) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
- + msg.obj + " to " + group);
- }
- try {
- android.os.Process.setProcessGroup(pid, group);
- } catch (Exception e) {
- if (DEBUG_ALL) {
- Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
- }
- } finally {
- if (traceEnabled) {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
+ final int group = msg.what;
+ final ProcessRecord app = (ProcessRecord) msg.obj;
+ setProcessGroup(app.getPid(), group, app.processName);
+ if (Flags.phantomProcessesFix()) {
+ mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, group);
}
return true;
});
@@ -544,8 +531,31 @@ public class OomAdjuster {
}
void setProcessGroup(int pid, int group, String processName) {
+ if (pid == ActivityManagerService.MY_PID) {
+ // Skip setting the process group for system_server, keep it as default.
+ return;
+ }
+ final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ if (traceEnabled) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
+ + processName + " to " + group);
+ }
+ try {
+ android.os.Process.setProcessGroup(pid, group);
+ } catch (Exception e) {
+ if (DEBUG_ALL) {
+ Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
+ }
+ } finally {
+ if (traceEnabled) {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+ }
+
+ void setAppAndChildProcessGroup(ProcessRecord app, int group) {
mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
- 0 /* unused */, pid, group, processName));
+ group, app));
}
void initSettings() {
@@ -696,7 +706,7 @@ public class OomAdjuster {
// In case the app goes from non-cached to cached but it doesn't have other reachable
// processes, its adj could be still unknown as of now, assign one.
processes.add(app);
- assignCachedAdjIfNecessary(processes);
+ applyLruAdjust(processes);
applyOomAdjLSP(app, false, mInjector.getUptimeMillis(),
mInjector.getElapsedRealtimeMillis(), oomAdjReason);
}
@@ -1086,7 +1096,7 @@ public class OomAdjuster {
}
mProcessesInCycle.clear();
- assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+ applyLruAdjust(mProcessList.getLruProcessesLOSP());
postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime, true);
@@ -1148,8 +1158,9 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
+ protected void applyLruAdjust(ArrayList<ProcessRecord> lruList) {
final int numLru = lruList.size();
+ int nextPreviousAppAdj = PREVIOUS_APP_ADJ;
if (mConstants.USE_TIERED_CACHED_ADJ) {
final long now = mInjector.getUptimeMillis();
int uiTargetAdj = 10;
@@ -1159,9 +1170,12 @@ public class OomAdjuster {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
- if (!app.isKilledByAm() && app.getThread() != null
- && (state.getCurAdj() >= UNKNOWN_ADJ
- || (state.hasShownUi() && state.getCurAdj() >= CACHED_APP_MIN_ADJ))) {
+ final int curAdj = state.getCurAdj();
+ if (PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ) {
+ state.setCurAdj(nextPreviousAppAdj);
+ nextPreviousAppAdj = Math.min(nextPreviousAppAdj + 1, PREVIOUS_APP_MAX_ADJ);
+ } else if (!app.isKilledByAm() && app.getThread() != null && (curAdj >= UNKNOWN_ADJ
+ || (state.hasShownUi() && curAdj >= CACHED_APP_MIN_ADJ))) {
final ProcessServiceRecord psr = app.mServices;
int targetAdj = CACHED_APP_MIN_ADJ;
@@ -1228,10 +1242,13 @@ public class OomAdjuster {
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
- // If we haven't yet assigned the final cached adj
- // to the process, do that now.
- if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
- >= UNKNOWN_ADJ) {
+ final int curAdj = state.getCurAdj();
+ if (PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ) {
+ state.setCurAdj(nextPreviousAppAdj);
+ nextPreviousAppAdj = Math.min(nextPreviousAppAdj + 1, PREVIOUS_APP_MAX_ADJ);
+ } else if (!app.isKilledByAm() && app.getThread() != null
+ && curAdj >= UNKNOWN_ADJ) {
+ // If we haven't yet assigned the final cached adj to the process, do that now.
final ProcessServiceRecord psr = app.mServices;
switch (state.getCurProcState()) {
case PROCESS_STATE_LAST_ACTIVITY:
@@ -2582,6 +2599,7 @@ public class OomAdjuster {
}
capability |= getDefaultCapability(app, procState);
+ capability |= getCpuCapability(app, now);
// Procstates below BFGS should never have this capability.
if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
@@ -2724,8 +2742,12 @@ public class OomAdjuster {
if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
app.mOptRecord.shouldNotFreezeReason()
| client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
- // Bail out early, as we only care about the return value for a dryrun.
- return true;
+ if (Flags.useCpuTimeCapability()) {
+ // Do nothing, capability updated check will handle the dryrun output.
+ } else {
+ // Bail out early, as we only care about the return value for a dryrun.
+ return true;
+ }
}
}
@@ -2736,6 +2758,8 @@ public class OomAdjuster {
// we check the final procstate, and remove it if the procsate is below BFGS.
capability |= getBfslCapabilityFromClient(client);
+ capability |= getCpuCapabilityFromClient(client);
+
if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) {
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
capability |= cstate.getCurCapability();
@@ -2794,9 +2818,14 @@ public class OomAdjuster {
app.mOptRecord.shouldNotFreezeReason()
| ProcessCachedOptimizerRecord
.SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT, mAdjSeq)) {
- // Bail out early, as we only care about the return value for a dryrun.
- return true;
+ if (Flags.useCpuTimeCapability()) {
+ // Do nothing, capability updated check will handle the dryrun output.
+ } else {
+ // Bail out early, as we only care about the return value for a dryrun.
+ return true;
+ }
}
+ capability |= PROCESS_CAPABILITY_CPU_TIME;
}
// Not doing bind OOM management, so treat
// this guy more like a started service.
@@ -3038,9 +3067,14 @@ public class OomAdjuster {
app.mOptRecord.shouldNotFreezeReason()
| ProcessCachedOptimizerRecord
.SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY, mAdjSeq)) {
- // Bail out early, as we only care about the return value for a dryrun.
- return true;
+ if (Flags.useCpuTimeCapability()) {
+ // Do nothing, capability updated check will handle the dryrun output.
+ } else {
+ // Bail out early, as we only care about the return value for a dryrun.
+ return true;
+ }
}
+ capability |= PROCESS_CAPABILITY_CPU_TIME;
}
}
if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
@@ -3093,9 +3127,24 @@ public class OomAdjuster {
capability &= ~PROCESS_CAPABILITY_BFSL;
}
if (!updated) {
- updated = adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup
- || (capability != prevCapability
- && (capability & prevCapability) == prevCapability);
+ if (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup) {
+ updated = true;
+ }
+
+ if (Flags.useCpuTimeCapability()) {
+ if ((capability != prevCapability)
+ && ((capability & prevCapability) == prevCapability)) {
+ updated = true;
+ }
+ } else {
+ // Ignore PROCESS_CAPABILITY_CPU_TIME in capability comparison
+ final int curFiltered = capability & ~PROCESS_CAPABILITY_CPU_TIME;
+ final int prevFiltered = prevCapability & ~PROCESS_CAPABILITY_CPU_TIME;
+ if ((curFiltered != prevFiltered)
+ && ((curFiltered & prevFiltered) == prevFiltered)) {
+ updated = true;
+ }
+ }
}
if (dryRun) {
@@ -3171,6 +3220,8 @@ public class OomAdjuster {
// we check the final procstate, and remove it if the procsate is below BFGS.
capability |= getBfslCapabilityFromClient(client);
+ capability |= getCpuCapabilityFromClient(client);
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
@@ -3181,8 +3232,12 @@ public class OomAdjuster {
if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
app.mOptRecord.shouldNotFreezeReason()
| client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
- // Bail out early, as we only care about the return value for a dryrun.
- return true;
+ if (Flags.useCpuTimeCapability()) {
+ // Do nothing, capability updated check will handle the dryrun output.
+ } else {
+ // Bail out early, as we only care about the return value for a dryrun.
+ return true;
+ }
}
}
@@ -3258,10 +3313,25 @@ public class OomAdjuster {
capability &= ~PROCESS_CAPABILITY_BFSL;
}
- if (dryRun && (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup
- || (capability != prevCapability
- && (capability & prevCapability) == prevCapability))) {
- return true;
+ if (dryRun) {
+ if (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup) {
+ return true;
+ }
+
+ if (Flags.useCpuTimeCapability()) {
+ if ((capability != prevCapability)
+ && ((capability & prevCapability) == prevCapability)) {
+ return true;
+ }
+ } else {
+ // Ignore PROCESS_CAPABILITY_CPU_TIME in capability comparison
+ final int curFiltered = capability & ~PROCESS_CAPABILITY_CPU_TIME;
+ final int prevFiltered = prevCapability & ~PROCESS_CAPABILITY_CPU_TIME;
+ if ((curFiltered != prevFiltered)
+ && ((curFiltered & prevFiltered) == prevFiltered)) {
+ return true;
+ }
+ }
}
if (adj < prevRawAdj) {
@@ -3313,6 +3383,29 @@ public class OomAdjuster {
return baseCapabilities | networkCapabilities;
}
+ private static int getCpuCapability(ProcessRecord app, long nowUptime) {
+ final UidRecord uidRec = app.getUidRecord();
+ if (uidRec != null && uidRec.isCurAllowListed()) {
+ // Process has user visible activities.
+ return PROCESS_CAPABILITY_CPU_TIME;
+ }
+ if (UserHandle.isCore(app.uid)) {
+ // Make sure all system components are not frozen.
+ return PROCESS_CAPABILITY_CPU_TIME;
+ }
+ if (app.mState.getCachedHasVisibleActivities()) {
+ // Process has user visible activities.
+ return PROCESS_CAPABILITY_CPU_TIME;
+ }
+ if (app.mServices.hasUndemotedShortForegroundService(nowUptime)) {
+ // It running a short fgs, just give it cpu time.
+ return PROCESS_CAPABILITY_CPU_TIME;
+ }
+ // TODO(b/370817323): Populate this method with all of the reasons to keep a process
+ // unfrozen.
+ return 0;
+ }
+
/**
* @return the BFSL capability from a client (of a service binding or provider).
*/
@@ -3361,6 +3454,15 @@ public class OomAdjuster {
}
/**
+ * @return the CPU capability from a client (of a service binding or provider).
+ */
+ private static int getCpuCapabilityFromClient(ProcessRecord client) {
+ // Just grant CPU capability every time
+ // TODO(b/370817323): Populate with reasons to not propagate cpu capability across bindings.
+ return client.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME;
+ }
+
+ /**
* Checks if for the given app and client, there's a cycle that should skip over the client
* for now or use partial values to evaluate the effect of the client binding.
* @param app
@@ -3495,8 +3597,7 @@ public class OomAdjuster {
processGroup = THREAD_GROUP_DEFAULT;
break;
}
- setProcessGroup(app.getPid(), processGroup, app.processName);
- mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, processGroup);
+ setAppAndChildProcessGroup(app, processGroup);
try {
final int renderThreadTid = app.getRenderThreadTid();
if (curSchedGroup == SCHED_GROUP_TOP_APP) {
@@ -3941,6 +4042,39 @@ public class OomAdjuster {
mCacheOomRanker.dump(pw);
}
+ /**
+ * Return whether or not a process should be frozen.
+ */
+ boolean getFreezePolicy(ProcessRecord proc) {
+ // Reasons to not freeze:
+ if (Flags.useCpuTimeCapability()) {
+ if ((proc.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME) != 0) {
+ /// App is important enough (see {@link #getCpuCapability}) or bound by something
+ /// important enough to not be frozen.
+ return false;
+ }
+ } else {
+ // The CPU capability handling covers all setShouldNotFreeze paths. Must check
+ // shouldNotFreeze, if the CPU capability is not being used.
+ if (proc.mOptRecord.shouldNotFreeze()) {
+ return false;
+ }
+ }
+
+ if (proc.mOptRecord.isFreezeExempt()) {
+ return false;
+ }
+
+ // Reasons to freeze:
+ if (proc.mState.getCurAdj() >= FREEZER_CUTOFF_ADJ) {
+ // Oomscore is in a high enough state, it is safe to freeze.
+ return true;
+ }
+
+ // Default, do not freeze a process.
+ return false;
+ }
+
@GuardedBy({"mService", "mProcLock"})
void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason,
boolean immediate, int oldOomAdj) {
@@ -3955,43 +4089,44 @@ public class OomAdjuster {
(state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ)
|| oldOomAdj == UNKNOWN_ADJ;
final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
- if ((oomAdjChanged || shouldNotFreezeChanged)
+ final boolean hasCpuCapability =
+ (PROCESS_CAPABILITY_CPU_TIME & app.mState.getCurCapability())
+ == PROCESS_CAPABILITY_CPU_TIME;
+ final boolean usedToHaveCpuCapability =
+ (PROCESS_CAPABILITY_CPU_TIME & app.mState.getSetCapability())
+ == PROCESS_CAPABILITY_CPU_TIME;
+ final boolean cpuCapabilityChanged = hasCpuCapability != usedToHaveCpuCapability;
+ if ((oomAdjChanged || shouldNotFreezeChanged || cpuCapabilityChanged)
&& Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER,
CachedAppOptimizer.ATRACE_FREEZER_TRACK,
"updateAppFreezeStateLSP " + app.processName
+ + " pid: " + app.getPid()
+ " isFreezeExempt: " + opt.isFreezeExempt()
+ " isFrozen: " + opt.isFrozen()
+ " shouldNotFreeze: " + opt.shouldNotFreeze()
+ " shouldNotFreezeReason: " + opt.shouldNotFreezeReason()
+ " curAdj: " + state.getCurAdj()
+ " oldOomAdj: " + oldOomAdj
- + " immediate: " + immediate);
+ + " immediate: " + immediate
+ + " cpuCapability: " + hasCpuCapability);
}
}
- if (app.mOptRecord.isFreezeExempt()) {
- return;
- }
-
- // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
- if (opt.isFrozen() && opt.shouldNotFreeze()) {
- mCachedAppOptimizer.unfreezeAppLSP(app,
- CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
- return;
- }
-
- // Use current adjustment when freezing, set adjustment when unfreezing.
- if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen()
- && !opt.shouldNotFreeze()) {
- if (!immediate) {
- mCachedAppOptimizer.freezeAppAsyncLSP(app);
- } else {
+ if (getFreezePolicy(app)) {
+ // This process should be frozen.
+ if (immediate && !opt.isFrozen()) {
+ // And it will be frozen immediately.
mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app);
+ } else if (!opt.isFrozen() || !opt.isPendingFreeze()) {
+ mCachedAppOptimizer.freezeAppAsyncLSP(app);
+ }
+ } else {
+ // This process should not be frozen.
+ if (opt.isFrozen() || opt.isPendingFreeze()) {
+ mCachedAppOptimizer.unfreezeAppLSP(app,
+ CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
}
- } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) {
- mCachedAppOptimizer.unfreezeAppLSP(app,
- CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
}
}
@@ -4015,7 +4150,8 @@ public class OomAdjuster {
final int size = processes.size();
for (int i = 0; i < size; i++) {
ProcessRecord proc = processes.get(i);
- mCachedAppOptimizer.unfreezeTemporarily(proc, reason);
+ mCachedAppOptimizer.unfreezeTemporarily(proc,
+ CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(reason));
}
processes.clear();
}
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index e452c45156c9..1b7e8f0bd244 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -54,6 +54,7 @@ import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_AP
import static com.android.server.am.ProcessList.PERSISTENT_PROC_ADJ;
import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
+import static com.android.server.am.ProcessList.PREVIOUS_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
import static com.android.server.am.ProcessList.SERVICE_ADJ;
import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
@@ -757,8 +758,9 @@ public class OomAdjusterModernImpl extends OomAdjuster {
OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
ActiveUids activeUids, ServiceThread adjusterThread, GlobalState globalState,
- Injector injector) {
- super(service, processList, activeUids, adjusterThread, globalState, injector);
+ CachedAppOptimizer cachedAppOptimizer, Injector injector) {
+ super(service, processList, activeUids, adjusterThread, globalState, cachedAppOptimizer,
+ injector);
}
private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes(
@@ -968,7 +970,7 @@ public class OomAdjusterModernImpl extends OomAdjuster {
mTmpOomAdjusterArgs.update(topApp, now, UNKNOWN_ADJ, oomAdjReason, null, true);
computeConnectionsLSP();
- assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+ applyLruAdjust(mProcessList.getLruProcessesLOSP());
postUpdateOomAdjInnerLSP(oomAdjReason, mActiveUids, now, nowElapsed, oldTime, true);
}
@@ -1049,20 +1051,24 @@ public class OomAdjusterModernImpl extends OomAdjuster {
// Now traverse and compute the connections of processes with changed importance.
computeConnectionsLSP();
- boolean unassignedAdj = false;
+ boolean needLruAdjust = false;
for (int i = 0, size = reachables.size(); i < size; i++) {
final ProcessStateRecord state = reachables.get(i).mState;
state.setReachable(false);
state.setCompletedAdjSeq(mAdjSeq);
- if (state.getCurAdj() >= UNKNOWN_ADJ) {
- unassignedAdj = true;
+ final int curAdj = state.getCurAdj();
+ // Processes assigned the PREV oomscore will have a laddered oomscore with respect to
+ // their positions in the LRU list. i.e. prev+0, prev+1, prev+2, etc.
+ final boolean isPrevApp = PREVIOUS_APP_ADJ <= curAdj && curAdj <= PREVIOUS_APP_MAX_ADJ;
+ if (curAdj >= UNKNOWN_ADJ || (Flags.oomadjusterPrevLaddering() && isPrevApp)) {
+ needLruAdjust = true;
}
}
// If all processes have an assigned adj, no need to calculate and assign cached adjs.
- if (unassignedAdj) {
+ if (needLruAdjust) {
// TODO: b/319163103 - optimize cache adj assignment to not require the whole lru list.
- assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+ applyLruAdjust(mProcessList.getLruProcessesLOSP());
}
// Repopulate any uid record that may have changed.
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index bfdced77a1b3..123780fb7567 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -548,6 +548,7 @@ public final class PhantomProcessList {
*/
void setProcessGroupForPhantomProcessOfApp(final ProcessRecord app, final int group) {
synchronized (mLock) {
+ lookForPhantomProcessesLocked(app);
final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
if (array == null) {
return;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cdb01889c139..f86474f0dcaf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -225,6 +225,7 @@ public final class ProcessList {
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
public static final int PREVIOUS_APP_ADJ = 700;
+ public static final int PREVIOUS_APP_MAX_ADJ = Flags.oomadjusterPrevLaddering() ? 799 : 700;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 5cb8b954a2ba..364497491785 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -256,18 +256,24 @@ final class ProcessServiceRecord {
}
// Now we need to look at all short-FGS within the process and see if all of them are
// procstate-timed-out or not.
+ return !hasUndemotedShortForegroundService(nowUptime);
+ }
+
+ boolean hasUndemotedShortForegroundService(long nowUptime) {
for (int i = mServices.size() - 1; i >= 0; i--) {
final ServiceRecord sr = mServices.valueAt(i);
if (!sr.isShortFgs() || !sr.hasShortFgsInfo()) {
continue;
}
if (sr.getShortFgsInfo().getProcStateDemoteTime() >= nowUptime) {
- return false;
+ // This short fgs has not timed out yet.
+ return true;
}
}
- return true;
+ return false;
}
+
int getReportedForegroundServiceTypes() {
return mRepFgServiceTypes;
}
diff --git a/services/core/java/com/android/server/am/ProcessStateController.java b/services/core/java/com/android/server/am/ProcessStateController.java
index 01468c640f6c..57899228e6ad 100644
--- a/services/core/java/com/android/server/am/ProcessStateController.java
+++ b/services/core/java/com/android/server/am/ProcessStateController.java
@@ -29,6 +29,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.ServiceThread;
/**
@@ -44,13 +45,14 @@ public class ProcessStateController {
private final GlobalState mGlobalState = new GlobalState();
private ProcessStateController(ActivityManagerService ams, ProcessList processList,
- ActiveUids activeUids, ServiceThread handlerThread, OomAdjuster.Injector oomAdjInjector,
+ ActiveUids activeUids, ServiceThread handlerThread,
+ CachedAppOptimizer cachedAppOptimizer, OomAdjuster.Injector oomAdjInjector,
boolean useOomAdjusterModernImpl) {
mOomAdjuster = useOomAdjusterModernImpl
? new OomAdjusterModernImpl(ams, processList, activeUids, handlerThread,
- mGlobalState, oomAdjInjector)
+ mGlobalState, cachedAppOptimizer, oomAdjInjector)
: new OomAdjuster(ams, processList, activeUids, handlerThread, mGlobalState,
- oomAdjInjector);
+ cachedAppOptimizer, oomAdjInjector);
}
/**
@@ -594,6 +596,7 @@ public class ProcessStateController {
private final ActiveUids mActiveUids;
private ServiceThread mHandlerThread = null;
+ private CachedAppOptimizer mCachedAppOptimizer = null;
private OomAdjuster.Injector mOomAdjInjector = null;
private boolean mUseOomAdjusterModernImpl = false;
@@ -610,24 +613,38 @@ public class ProcessStateController {
if (mHandlerThread == null) {
mHandlerThread = OomAdjuster.createAdjusterThread();
}
+ if (mCachedAppOptimizer == null) {
+ mCachedAppOptimizer = new CachedAppOptimizer(mAms);
+ }
if (mOomAdjInjector == null) {
mOomAdjInjector = new OomAdjuster.Injector();
}
return new ProcessStateController(mAms, mProcessList, mActiveUids, mHandlerThread,
- mOomAdjInjector, mUseOomAdjusterModernImpl);
+ mCachedAppOptimizer, mOomAdjInjector, mUseOomAdjusterModernImpl);
}
/**
* For Testing Purposes. Set what thread OomAdjuster will offload tasks on to.
*/
+ @VisibleForTesting
public Builder setHandlerThread(ServiceThread handlerThread) {
mHandlerThread = handlerThread;
return this;
}
/**
+ * For Testing Purposes. Set the CachedAppOptimzer used by OomAdjuster.
+ */
+ @VisibleForTesting
+ public Builder setCachedAppOptimizer(CachedAppOptimizer cachedAppOptimizer) {
+ mCachedAppOptimizer = cachedAppOptimizer;
+ return this;
+ }
+
+ /**
* For Testing Purposes. Set an injector for OomAdjuster.
*/
+ @VisibleForTesting
public Builder setOomAdjusterInjector(OomAdjuster.Injector injector) {
mOomAdjInjector = injector;
return this;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 8dc7c7345f79..3dd5ec9a3834 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -42,6 +42,7 @@ import android.aconfigd.Aconfigd.StorageReturnMessages;
import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
import static com.android.aconfig_new_storage.Flags.supportImmediateLocalOverrides;
import static com.android.aconfig_new_storage.Flags.supportClearLocalOverridesImmediately;
+import static com.android.aconfig.flags.Flags.enableSystemAconfigdRust;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -144,7 +145,6 @@ public class SettingsToPropertiesMapper {
"android_core_networking",
"android_health_services",
"android_sdk",
- "android_stylus",
"aoc",
"app_widgets",
"arc_next",
@@ -209,6 +209,7 @@ public class SettingsToPropertiesMapper {
"pixel_continuity",
"pixel_perf",
"pixel_sensors",
+ "pixel_state_server",
"pixel_system_sw_video",
"pixel_video_sw",
"pixel_watch",
@@ -456,9 +457,11 @@ public class SettingsToPropertiesMapper {
static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) {
// connect to aconfigd socket
LocalSocket client = new LocalSocket();
+ String socketName = enableSystemAconfigdRust()
+ ? "aconfigd_system" : "aconfigd";
try{
client.connect(new LocalSocketAddress(
- "aconfigd", LocalSocketAddress.Namespace.RESERVED));
+ socketName, LocalSocketAddress.Namespace.RESERVED));
Slog.d(TAG, "connected to aconfigd socket");
} catch (IOException ioe) {
logErr("failed to connect to aconfigd socket", ioe);
diff --git a/services/core/java/com/android/server/am/broadcasts_flags.aconfig b/services/core/java/com/android/server/am/broadcasts_flags.aconfig
index b1185d552941..7f169db7dcec 100644
--- a/services/core/java/com/android/server/am/broadcasts_flags.aconfig
+++ b/services/core/java/com/android/server/am/broadcasts_flags.aconfig
@@ -7,4 +7,12 @@ flag {
description: "Restrict priority values defined by non-system apps"
is_fixed_read_only: true
bug: "369487976"
+}
+
+flag {
+ name: "limit_priority_scope"
+ namespace: "backstage_power"
+ description: "Limit the scope of receiver priorities to within a process"
+ is_fixed_read_only: true
+ bug: "369487976"
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 56cfdfb7edde..711b163ea424 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -90,14 +90,6 @@ flag {
flag {
namespace: "backstage_power"
- name: "use_permission_manager_for_broadcast_delivery_check"
- description: "Use PermissionManager API for broadcast delivery permission checks."
- bug: "315468967"
- is_fixed_read_only: true
-}
-
-flag {
- namespace: "backstage_power"
name: "trace_receiver_registration"
description: "Add tracing for broadcast receiver registration and un-registration"
bug: "336385821"
@@ -242,4 +234,29 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "oomadjuster_prev_laddering"
+ namespace: "system_performance"
+ is_fixed_read_only: true
+ description: "Add +X to the prev scores according to their positions in the process LRU list"
+ bug: "359912586"
+}
+
+flag {
+ name: "phantom_processes_fix"
+ namespace: "backstage_power"
+ description: "Make sure setProcessGroupForPhantomProcessOfApp deals with phantom processes properly"
+ bug: "375058190"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "use_cpu_time_capability"
+ namespace: "backstage_power"
+ description: "Use PROCESS_CAPABILITY_CPU_TIME to control unfreeze state."
+ bug: "370817323"
+}
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 5db6dc7ccc15..6ccb3ee8bcc9 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -235,6 +235,9 @@ public class AppBindingService extends Binder {
}
final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
if (Intent.ACTION_USER_REMOVED.equals(action)) {
onUserRemoved(userId);
diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
index ae93991d3945..0855815b67a9 100644
--- a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
@@ -65,27 +65,31 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
@Override
public boolean setGlobalRestriction(Object clientToken, int code, boolean restricted) {
+ boolean changed;
if (restricted) {
if (!mGlobalRestrictions.containsKey(clientToken)) {
mGlobalRestrictions.put(clientToken, new SparseBooleanArray());
}
SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
Objects.requireNonNull(restrictedCodes);
- boolean changed = !restrictedCodes.get(code);
+ changed = !restrictedCodes.get(code);
restrictedCodes.put(code, true);
- return changed;
} else {
SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
if (restrictedCodes == null) {
return false;
}
- boolean changed = restrictedCodes.get(code);
+ changed = restrictedCodes.get(code);
restrictedCodes.delete(code);
if (restrictedCodes.size() == 0) {
mGlobalRestrictions.remove(clientToken);
}
- return changed;
}
+
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
+ return changed;
}
@Override
@@ -104,7 +108,11 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
@Override
public boolean clearGlobalRestrictions(Object clientToken) {
- return mGlobalRestrictions.remove(clientToken) != null;
+ boolean changed = mGlobalRestrictions.remove(clientToken) != null;
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
+ return changed;
}
@RequiresPermission(anyOf = {
@@ -122,6 +130,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
changed |= putUserRestrictionExclusions(clientToken, userIds[i],
excludedPackageTags);
}
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
return changed;
}
@@ -191,6 +202,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
changed |= mUserRestrictions.remove(clientToken) != null;
changed |= mUserRestrictionExcludedPackageTags.remove(clientToken) != null;
notifyAllUserRestrictions(allUserRestrictedCodes);
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
return changed;
}
@@ -244,6 +258,9 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
}
}
+ if (changed) {
+ AppOpsManager.invalidateAppOpModeCache();
+ }
return changed;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 702ad9541af8..5e74d67905a6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -998,6 +998,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void onUidModeChanged(int uid, int code, int mode,
String persistentDeviceId) {
+ AppOpsManager.invalidateAppOpModeCache();
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid, AppOpsService.this,
code, uid, false, persistentDeviceId));
@@ -1006,6 +1007,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void onPackageModeChanged(String packageName, int userId, int code,
int mode) {
+ AppOpsManager.invalidateAppOpModeCache();
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForPkg, AppOpsService.this,
packageName, code, mode, userId));
@@ -1032,6 +1034,11 @@ public class AppOpsService extends IAppOpsService.Stub {
// To migrate storageFile to recentAccessesFile, these reads must be called in this order.
readRecentAccesses();
mAppOpsCheckingService.readState();
+ // The system property used by the cache is created the first time it is written, that only
+ // happens inside invalidateCache(). Until the service calls invalidateCache() the property
+ // will not exist and the nonce will be UNSET.
+ AppOpsManager.invalidateAppOpModeCache();
+ AppOpsManager.disableAppOpModeCache();
}
public void publish() {
@@ -2830,6 +2837,13 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int checkOperationRaw(int code, int uid, String packageName,
@Nullable String attributionTag) {
+ if (Binder.getCallingPid() != Process.myPid()
+ && Flags.appopAccessTrackingLoggingEnabled()) {
+ FrameworkStatsLog.write(
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+ false);
+ }
return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
Context.DEVICE_ID_DEFAULT, true /*raw*/);
}
@@ -2837,6 +2851,13 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int checkOperationRawForDevice(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, int virtualDeviceId) {
+ if (Binder.getCallingPid() != Process.myPid()
+ && Flags.appopAccessTrackingLoggingEnabled()) {
+ FrameworkStatsLog.write(
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+ APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+ false);
+ }
return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
virtualDeviceId, true /*raw*/);
}
@@ -2894,8 +2915,14 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED;
}
}
- return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
- virtualDeviceId, raw);
+
+ if (Flags.appopModeCachingEnabled()) {
+ return getAppOpMode(code, uid, resolvedPackageName, attributionTag, virtualDeviceId,
+ raw, true);
+ } else {
+ return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
+ virtualDeviceId, raw);
+ }
}
/**
@@ -2961,6 +2988,54 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ /**
+ * This method unifies mode checking logic between checkOperationUnchecked and
+ * noteOperationUnchecked. It can replace those two methods once the flag is fully rolled out.
+ *
+ * @param isCheckOp This param is only used in user's op restriction. When checking if a package
+ * can bypass user's restriction we should account for attributionTag as well.
+ * But existing checkOp APIs don't accept attributionTag so we added a hack to
+ * skip attributionTag check for checkOp. After we add an overload of checkOp
+ * that accepts attributionTag we should remove this param.
+ */
+ private @Mode int getAppOpMode(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, int virtualDeviceId, boolean raw, boolean isCheckOp) {
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag);
+ } catch (SecurityException e) {
+ logVerifyAndGetBypassFailure(uid, e, "getAppOpMode");
+ return MODE_IGNORED;
+ }
+
+ if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
+ return MODE_IGNORED;
+ }
+
+ synchronized (this) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, virtualDeviceId,
+ pvr.bypass, isCheckOp)) {
+ return MODE_IGNORED;
+ }
+ if (isOpAllowedForUid(uid)) {
+ return MODE_ALLOWED;
+ }
+
+ int switchCode = AppOpsManager.opToSwitch(code);
+ int rawUidMode = mAppOpsCheckingService.getUidMode(uid,
+ getPersistentId(virtualDeviceId), switchCode);
+
+ if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) {
+ return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode);
+ }
+
+ int rawPackageMode = mAppOpsCheckingService.getPackageMode(packageName, switchCode,
+ UserHandle.getUserId(uid));
+ return raw ? rawPackageMode : evaluateForegroundMode(uid, switchCode, rawPackageMode);
+ }
+ }
+
+
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
return mCheckOpsDelegateDispatcher.checkAudioOperation(code, usage, uid, packageName);
@@ -3213,7 +3288,6 @@ public class AppOpsService extends IAppOpsService.Stub {
PackageVerificationResult pvr;
try {
pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
- boolean wasNull = attributionTag == null;
if (!pvr.isAttributionTagValid) {
attributionTag = null;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 09de89445122..34d4fb02ad99 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -40,6 +40,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
+import android.media.AudioDescriptor;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
@@ -47,6 +48,7 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioManager.AudioDeviceCategory;
import android.media.AudioPort;
+import android.media.AudioProfile;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
@@ -619,6 +621,8 @@ public class AudioDeviceInventory {
final int mGroupId;
@NonNull String mPeerDeviceAddress;
@NonNull String mPeerIdentityDeviceAddress;
+ @NonNull List<AudioProfile> mAudioProfiles;
+ @NonNull List<AudioDescriptor> mAudioDescriptors;
/** Disabled operating modes for this device. Use a negative logic so that by default
* an empty list means all modes are allowed.
@@ -627,7 +631,8 @@ public class AudioDeviceInventory {
DeviceInfo(int deviceType, String deviceName, String address,
String identityAddress, int codecFormat,
- int groupId, String peerAddress, String peerIdentityAddress) {
+ int groupId, String peerAddress, String peerIdentityAddress,
+ List<AudioProfile> profiles, List<AudioDescriptor> descriptors) {
mDeviceType = deviceType;
mDeviceName = TextUtils.emptyIfNull(deviceName);
mDeviceAddress = TextUtils.emptyIfNull(address);
@@ -639,6 +644,16 @@ public class AudioDeviceInventory {
mGroupId = groupId;
mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress);
mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(peerIdentityAddress);
+ mAudioProfiles = profiles;
+ mAudioDescriptors = descriptors;
+ }
+
+ DeviceInfo(int deviceType, String deviceName, String address,
+ String identityAddress, int codecFormat,
+ int groupId, String peerAddress, String peerIdentityAddress) {
+ this(deviceType, deviceName, address, identityAddress, codecFormat,
+ groupId, peerAddress, peerIdentityAddress,
+ new ArrayList<>(), new ArrayList<>());
}
/** Constructor for all devices except A2DP sink and LE Audio */
@@ -646,6 +661,13 @@ public class AudioDeviceInventory {
this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT);
}
+ /** Constructor for HDMI OUT, HDMI ARC/EARC sink devices */
+ DeviceInfo(int deviceType, String deviceName, String address,
+ List<AudioProfile> profiles, List<AudioDescriptor> descriptors) {
+ this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT,
+ BluetoothLeAudio.GROUP_ID_INVALID, null, null, profiles, descriptors);
+ }
+
/** Constructor for A2DP sink devices */
DeviceInfo(int deviceType, String deviceName, String address,
String identityAddress, int codecFormat) {
@@ -1194,27 +1216,31 @@ public class AudioDeviceInventory {
}
/*package*/ void onToggleHdmi() {
- MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi")
- .set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HDMI));
+ final int[] hdmiDevices = { AudioSystem.DEVICE_OUT_HDMI,
+ AudioSystem.DEVICE_OUT_HDMI_ARC, AudioSystem.DEVICE_OUT_HDMI_EARC };
+
synchronized (mDevicesLock) {
- // Is HDMI connected?
- final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, "");
- final DeviceInfo di = mConnectedDevices.get(key);
- if (di == null) {
- Log.e(TAG, "invalid null DeviceInfo in onToggleHdmi");
- mmi.set(MediaMetrics.Property.EARLY_RETURN, "invalid null DeviceInfo").record();
- return;
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ boolean isHdmiDevice = Arrays.stream(hdmiDevices).anyMatch(device ->
+ device == di.mDeviceType);
+ if (isHdmiDevice) {
+ MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi")
+ .set(MediaMetrics.Property.DEVICE,
+ AudioSystem.getDeviceName(di.mDeviceType));
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.convertInternalDeviceToDeviceType(di.mDeviceType),
+ di.mDeviceAddress, di.mDeviceName, di.mAudioProfiles,
+ di.mAudioDescriptors);
+ // Toggle HDMI to retrigger broadcast with proper formats.
+ setWiredDeviceConnectionState(ada,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, "onToggleHdmi"); // disconnect
+ setWiredDeviceConnectionState(ada,
+ AudioSystem.DEVICE_STATE_AVAILABLE, "onToggleHdmi"); // reconnect
+ mmi.record();
+ }
}
- // Toggle HDMI to retrigger broadcast with proper formats.
- setWiredDeviceConnectionState(
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect
- setWiredDeviceConnectionState(
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
- AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect
}
- mmi.record();
}
@GuardedBy("mDevicesLock")
@@ -1818,7 +1844,15 @@ public class AudioDeviceInventory {
.printSlog(EventLogger.Event.ALOGE, TAG));
return false;
}
- mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
+
+ if (device == AudioSystem.DEVICE_OUT_HDMI ||
+ device == AudioSystem.DEVICE_OUT_HDMI_ARC ||
+ device == AudioSystem.DEVICE_OUT_HDMI_EARC) {
+ mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName,
+ address, attributes.getAudioProfiles(), attributes.getAudioDescriptors()));
+ } else {
+ mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
+ }
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
status = true;
} else if (!connect && isConnected) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 985155d0d891..6ba356990cac 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -493,7 +493,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_INIT_ADI_DEVICE_STATES = 103;
private static final int MSG_INIT_INPUT_GAINS = 104;
- private static final int MSG_SET_INPUT_GAIN_INDEX = 105;
+ private static final int MSG_APPLY_INPUT_GAIN_INDEX = 105;
private static final int MSG_PERSIST_INPUT_GAIN_INDEX = 106;
// end of messages handled under wakelock
@@ -1626,7 +1626,6 @@ public class AudioService extends IAudioService.Stub
new InputDeviceVolumeHelper(
mSettings,
mContentResolver,
- mSettingsLock,
System.INPUT_GAIN_INDEX_SETTINGS);
}
@@ -5804,7 +5803,7 @@ public class AudioService extends IAudioService.Stub
// to persist).
sendMsg(
mAudioHandler,
- MSG_SET_INPUT_GAIN_INDEX,
+ MSG_APPLY_INPUT_GAIN_INDEX,
SENDMSG_QUEUE,
/*arg1*/ index,
/*arg2*/ 0,
@@ -5813,22 +5812,22 @@ public class AudioService extends IAudioService.Stub
}
}
- private void setInputGainIndexInt(@NonNull AudioDeviceAttributes ada, int index) {
+ private void onApplyInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) {
// TODO(b/364923030): call AudioSystem to apply input gain in native layer.
// Post a persist input gain msg.
sendMsg(
mAudioHandler,
MSG_PERSIST_INPUT_GAIN_INDEX,
- SENDMSG_QUEUE,
- /*arg1*/ index,
+ SENDMSG_REPLACE,
+ /*arg1*/ 0,
/*arg2*/ 0,
/*obj*/ ada,
PERSIST_DELAY);
}
- private void persistInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) {
- mInputDeviceVolumeHelper.persistInputGainIndex(ada, index);
+ private void onPersistInputGainIndex(@NonNull AudioDeviceAttributes ada) {
+ mInputDeviceVolumeHelper.persistInputGainIndex(ada);
}
/**
@@ -9194,16 +9193,6 @@ public class AudioService extends IAudioService.Stub
mIndexMin = MIN_STREAM_VOLUME[streamType] * 10;
mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
- final int status = AudioSystem.initStreamVolume(
- streamType, MIN_STREAM_VOLUME[streamType], MAX_STREAM_VOLUME[streamType]);
- if (status != AudioSystem.AUDIO_STATUS_OK) {
- sLifecycleLogger.enqueue(new EventLogger.StringEvent(
- "VSS() stream:" + streamType + " initStreamVolume=" + status)
- .printLog(ALOGE, TAG));
- sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
- "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
- }
-
updateIndexFactors();
mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex()
@@ -9268,6 +9257,19 @@ public class AudioService extends IAudioService.Stub
mIndexMinNoPerm = mIndexMin;
}
}
+
+ final int status = AudioSystem.initStreamVolume(
+ mStreamType, mIndexMin / 10, mIndexMax / 10);
+ sVolumeLogger.enqueue(new EventLogger.StringEvent(
+ "updateIndexFactors() stream:" + mStreamType + " index min/max:"
+ + mIndexMin / 10 + "/" + mIndexMax / 10 + " indexStepFactor:"
+ + mIndexStepFactor).printSlog(ALOGI, TAG));
+ if (status != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueue(new EventLogger.StringEvent(
+ "Failed initStreamVolume with status=" + status).printSlog(ALOGE, TAG));
+ sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
+ "updateIndexFactors()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
+ }
}
/**
@@ -10213,12 +10215,12 @@ public class AudioService extends IAudioService.Stub
vgs.persistVolumeGroup(msg.arg1);
break;
- case MSG_SET_INPUT_GAIN_INDEX:
- setInputGainIndexInt((AudioDeviceAttributes) msg.obj, msg.arg1);
+ case MSG_APPLY_INPUT_GAIN_INDEX:
+ onApplyInputGainIndex((AudioDeviceAttributes) msg.obj, msg.arg1);
break;
case MSG_PERSIST_INPUT_GAIN_INDEX:
- persistInputGainIndex((AudioDeviceAttributes) msg.obj, msg.arg1);
+ onPersistInputGainIndex((AudioDeviceAttributes) msg.obj);
break;
case MSG_PERSIST_RINGER_MODE:
diff --git a/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java b/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java
index d83dca629d74..d7d1ac96d650 100644
--- a/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java
+++ b/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,10 +23,10 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.UserHandle;
import android.util.IntArray;
+import android.util.Log;
import android.util.SparseIntArray;
import java.util.HashSet;
@@ -43,73 +43,59 @@ import java.util.Set;
private final SettingsAdapter mSettings;
private final ContentResolver mContentResolver;
- private final Object mSettingsLock;
private final String mInputGainIndexSettingsName;
// A map between device internal type (e.g. AudioSystem.DEVICE_IN_BUILTIN_MIC) to its input gain
// index.
private final SparseIntArray mInputGainIndexMap;
- private final Set<Integer> mSupportedDeviceTypes;
+ private final Set<Integer> mSupportedDeviceTypes = new HashSet<>();
InputDeviceVolumeHelper(
SettingsAdapter settings,
ContentResolver contentResolver,
- Object settingsLock,
String settingsName) {
mSettings = settings;
mContentResolver = contentResolver;
- mSettingsLock = settingsLock;
mInputGainIndexSettingsName = settingsName;
IntArray internalDeviceTypes = new IntArray();
int status = AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS, internalDeviceTypes);
- mInputGainIndexMap =
- new SparseIntArray(
- status == AudioManager.SUCCESS
- ? internalDeviceTypes.size()
- : AudioSystem.DEVICE_IN_ALL_SET.size());
-
- if (status == AudioManager.SUCCESS) {
- Set<Integer> supportedDeviceTypes = new HashSet<>();
- for (int i = 0; i < internalDeviceTypes.size(); i++) {
- supportedDeviceTypes.add(internalDeviceTypes.get(i));
- }
- mSupportedDeviceTypes = supportedDeviceTypes;
- } else {
- mSupportedDeviceTypes = AudioSystem.DEVICE_IN_ALL_SET;
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, "AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS) failed. status:"
+ + status);
+ }
+
+ // Note that in a rare case, if AudioSystem.getSupportedDeviceTypes call fails, both
+ // mInputGainIndexMap and mSupportedDeviceTypes will be empty.
+ mInputGainIndexMap = new SparseIntArray(internalDeviceTypes.size());
+ for (int i = 0; i < internalDeviceTypes.size(); i++) {
+ mSupportedDeviceTypes.add(internalDeviceTypes.get(i));
}
readSettings();
}
- public void readSettings() {
+ private void readSettings() {
synchronized (InputDeviceVolumeHelper.class) {
for (int inputDeviceType : mSupportedDeviceTypes) {
// Retrieve current input gain for device. If no input gain stored for current
// device, use default input gain.
- int index;
- if (!hasValidSettingsName()) {
- index = INDEX_DEFAULT;
- } else {
- String name = getSettingNameForDevice(inputDeviceType);
- index =
- mSettings.getSystemIntForUser(
- mContentResolver, name, INDEX_DEFAULT, UserHandle.USER_CURRENT);
- }
+ String name = getSettingNameForDevice(inputDeviceType);
+ int index = name == null
+ ? INDEX_DEFAULT
+ : mSettings.getSystemIntForUser(
+ mContentResolver, name, INDEX_DEFAULT, UserHandle.USER_CURRENT);
mInputGainIndexMap.put(inputDeviceType, getValidIndex(index));
}
}
}
- public boolean hasValidSettingsName() {
- return mInputGainIndexSettingsName != null && !mInputGainIndexSettingsName.isEmpty();
- }
-
- public @Nullable String getSettingNameForDevice(int inputDeviceType) {
- if (!hasValidSettingsName()) {
+ private @Nullable String getSettingNameForDevice(int inputDeviceType) {
+ if (mInputGainIndexSettingsName == null || mInputGainIndexSettingsName.isEmpty()) {
return null;
}
+
final String suffix = AudioSystem.getInputDeviceName(inputDeviceType);
if (suffix.isEmpty()) {
return mInputGainIndexSettingsName;
@@ -158,29 +144,27 @@ import java.util.Set;
ensureValidInputDeviceType(inputDeviceType);
int oldIndex;
- synchronized (mSettingsLock) {
- synchronized (InputDeviceVolumeHelper.class) {
- oldIndex = getInputGainIndex(ada);
- index = getValidIndex(index);
-
- if (oldIndex == index) {
- return false;
- }
+ synchronized (InputDeviceVolumeHelper.class) {
+ oldIndex = getInputGainIndex(ada);
+ index = getValidIndex(index);
- mInputGainIndexMap.put(inputDeviceType, index);
- return true;
+ if (oldIndex == index) {
+ return false;
}
+
+ mInputGainIndexMap.put(inputDeviceType, index);
+ return true;
}
}
- public void persistInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) {
+ public void persistInputGainIndex(@NonNull AudioDeviceAttributes ada) {
int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType());
- ensureValidInputDeviceType(inputDeviceType);
-
- if (hasValidSettingsName()) {
+ String name = getSettingNameForDevice(inputDeviceType);
+ if (name != null) {
+ int index = getInputGainIndex(ada);
mSettings.putSystemIntForUser(
mContentResolver,
- getSettingNameForDevice(inputDeviceType),
+ name,
index,
UserHandle.USER_CURRENT);
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 1da62d74f0df..1604e94e5a6d 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1068,6 +1068,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
switch (attr.getUsage()) {
case AudioAttributes.USAGE_MEDIA:
case AudioAttributes.USAGE_GAME:
+ case AudioAttributes.USAGE_SPEAKER_CLEANUP:
return 1000;
case AudioAttributes.USAGE_ALARM:
case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 00280c8f9c04..65780238ede4 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -288,11 +288,13 @@ public class BiometricService extends SystemService {
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public SettingObserver(Context context, Handler handler,
- List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
+ List<BiometricService.EnabledOnKeyguardCallback> callbacks,
+ UserManager userManager, FingerprintManager fingerprintManager,
+ FaceManager faceManager) {
super(handler);
mContentResolver = context.getContentResolver();
mCallbacks = callbacks;
- mUserManager = context.getSystemService(UserManager.class);
+ mUserManager = userManager;
final boolean hasFingerprint = context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
@@ -304,7 +306,7 @@ public class BiometricService extends SystemService {
Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q
&& hasFace && !hasFingerprint;
- addBiometricListenersForMandatoryBiometrics(context);
+ addBiometricListenersForMandatoryBiometrics(context, fingerprintManager, faceManager);
updateContentObserver();
}
@@ -431,11 +433,21 @@ public class BiometricService extends SystemService {
public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) {
if (!mMandatoryBiometricsEnabled.containsKey(userId)) {
+ Slog.d(TAG, "update mb toggle for user " + userId);
updateMandatoryBiometricsForAllProfiles(userId);
}
if (!mMandatoryBiometricsRequirementsSatisfied.containsKey(userId)) {
+ Slog.d(TAG, "update mb reqs for user " + userId);
updateMandatoryBiometricsRequirementsForAllProfiles(userId);
}
+
+ Slog.d(TAG, mMandatoryBiometricsEnabled.getOrDefault(userId,
+ DEFAULT_MANDATORY_BIOMETRICS_STATUS)
+ + " " + mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
+ DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
+ + " " + getEnabledForApps(userId)
+ + " " + (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+ || mFaceEnrolledForUser.getOrDefault(userId, false /* default */)));
return mMandatoryBiometricsEnabled.getOrDefault(userId,
DEFAULT_MANDATORY_BIOMETRICS_STATUS)
&& mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
@@ -456,11 +468,23 @@ public class BiometricService extends SystemService {
private void updateMandatoryBiometricsForAllProfiles(int userId) {
int effectiveUserId = userId;
- if (mUserManager.getMainUser() != null) {
- effectiveUserId = mUserManager.getMainUser().getIdentifier();
+ final UserInfo parentProfile = mUserManager.getProfileParent(userId);
+
+ if (parentProfile != null) {
+ effectiveUserId = parentProfile.id;
}
- for (int profileUserId: mUserManager.getEnabledProfileIds(effectiveUserId)) {
- mMandatoryBiometricsEnabled.put(profileUserId,
+
+ final int[] enabledProfileIds = mUserManager.getEnabledProfileIds(effectiveUserId);
+ if (enabledProfileIds != null) {
+ for (int profileUserId : enabledProfileIds) {
+ mMandatoryBiometricsEnabled.put(profileUserId,
+ Settings.Secure.getIntForUser(
+ mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
+ DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0,
+ effectiveUserId) != 0);
+ }
+ } else {
+ mMandatoryBiometricsEnabled.put(userId,
Settings.Secure.getIntForUser(
mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0,
@@ -470,11 +494,24 @@ public class BiometricService extends SystemService {
private void updateMandatoryBiometricsRequirementsForAllProfiles(int userId) {
int effectiveUserId = userId;
- if (mUserManager.getMainUser() != null) {
- effectiveUserId = mUserManager.getMainUser().getIdentifier();
+ final UserInfo parentProfile = mUserManager.getProfileParent(userId);
+
+ if (parentProfile != null) {
+ effectiveUserId = parentProfile.id;
}
- for (int profileUserId: mUserManager.getEnabledProfileIds(effectiveUserId)) {
- mMandatoryBiometricsRequirementsSatisfied.put(profileUserId,
+
+ final int[] enabledProfileIds = mUserManager.getEnabledProfileIds(effectiveUserId);
+ if (enabledProfileIds != null) {
+ for (int profileUserId : enabledProfileIds) {
+ mMandatoryBiometricsRequirementsSatisfied.put(profileUserId,
+ Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+ DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS
+ ? 1 : 0,
+ effectiveUserId) != 0);
+ }
+ } else {
+ mMandatoryBiometricsRequirementsSatisfied.put(userId,
Settings.Secure.getIntForUser(mContentResolver,
Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS ? 1 : 0,
@@ -482,10 +519,8 @@ public class BiometricService extends SystemService {
}
}
- private void addBiometricListenersForMandatoryBiometrics(Context context) {
- final FingerprintManager fingerprintManager = context.getSystemService(
- FingerprintManager.class);
- final FaceManager faceManager = context.getSystemService(FaceManager.class);
+ private void addBiometricListenersForMandatoryBiometrics(Context context,
+ FingerprintManager fingerprintManager, FaceManager faceManager) {
if (fingerprintManager != null) {
fingerprintManager.addAuthenticatorsRegisteredCallback(
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -1169,7 +1204,9 @@ public class BiometricService extends SystemService {
*/
public SettingObserver getSettingObserver(Context context, Handler handler,
List<EnabledOnKeyguardCallback> callbacks) {
- return new SettingObserver(context, handler, callbacks);
+ return new SettingObserver(context, handler, callbacks, context.getSystemService(
+ UserManager.class), context.getSystemService(FingerprintManager.class),
+ context.getSystemService(FaceManager.class));
}
/**
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index e8fa41749473..afdc0c0294a6 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -119,9 +119,16 @@ class PreAuthInfo {
== BiometricManager.Authenticators.IDENTITY_CHECK;
boolean isMandatoryBiometricsAuthentication = false;
+ final int effectiveUserId;
+ if (Flags.effectiveUserBp()) {
+ effectiveUserId = userManager.getCredentialOwnerProfile(userId);
+ } else {
+ effectiveUserId = userId;
+ }
+
if (dropCredentialFallback(promptInfo.getAuthenticators(),
settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
- userId), trustManager)) {
+ effectiveUserId), trustManager)) {
isMandatoryBiometricsAuthentication = true;
promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
if (promptInfo.getNegativeButtonText() == null) {
@@ -145,13 +152,6 @@ class PreAuthInfo {
final List<BiometricSensor> eligibleSensors = new ArrayList<>();
final List<Pair<BiometricSensor, Integer>> ineligibleSensors = new ArrayList<>();
- final int effectiveUserId;
- if (Flags.effectiveUserBp()) {
- effectiveUserId = userManager.getCredentialOwnerProfile(userId);
- } else {
- effectiveUserId = userId;
- }
-
if (biometricRequested) {
for (BiometricSensor sensor : sensors) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index 6c933665a2e2..394b5617a813 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -77,6 +77,16 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi
}
@Override
+ public void cancel() {
+ super.cancel();
+ try {
+ mInvalidationCallback.onCompleted();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to complete invalidation client due to exception: " + e);
+ }
+ }
+
+ @Override
public int getProtoEnum() {
return BiometricsProto.CM_INVALIDATE;
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 6e38733f04c2..471b7b4ddfc8 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -167,6 +167,12 @@ public abstract class VirtualDeviceManagerInternal {
*/
public abstract int getDeviceIdForDisplayId(int displayId);
+ /** Returns the dim duration for the displays of the device with the given ID. */
+ public abstract long getDimDurationMillisForDeviceId(int deviceId);
+
+ /** Returns the screen off timeout of the displays of the device with the given ID. */
+ public abstract long getScreenOffTimeoutMillisForDeviceId(int deviceId);
+
/**
* Gets the persistent ID for the VirtualDevice with the given device ID.
*
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index a40dd7919402..c3d88e3e6eb1 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -50,6 +50,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
* <p>Note, this class is not thread safe so callers must ensure thread safety.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatChange extends CompatibilityChangeInfo {
/**
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 79025d00d128..e89f43bd7196 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -42,6 +42,7 @@ import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.Config;
import com.android.server.compat.overrides.ChangeOverrides;
@@ -63,6 +64,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -72,12 +74,16 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <p>It stores the default configuration for each change, and any per-package overrides that have
* been configured.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class CompatConfig {
private static final String TAG = "CompatConfig";
private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
+ private static final String APP_COMPAT_DATA_DIR_RAVENWOOD = "/ravenwood-data/";
+ private static final String OVERRIDES_FILE_RAVENWOOD = "compat-config.xml";
+
private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>();
private final OverrideValidatorImpl mOverrideValidator;
@@ -98,19 +104,32 @@ final class CompatConfig {
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
- config.initConfigFromLib(Environment.buildPath(
+ config.loadConfigFiles();
+ config.initOverrides();
+ config.invalidateCache();
+ return config;
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private void loadConfigFiles() {
+ initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
- config.initConfigFromLib(Environment.buildPath(
+ initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
for (ApexManager.ActiveApexInfo apex : apexes) {
- config.initConfigFromLib(Environment.buildPath(
+ initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
- config.initOverrides();
- config.invalidateCache();
- return config;
+ }
+
+ @SuppressWarnings("unused")
+ private void loadConfigFiles$ravenwood() {
+ final var configDir = new File(
+ RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath()
+ + APP_COMPAT_DATA_DIR_RAVENWOOD);
+ initConfigFromLib(configDir, (file) -> file.getName().endsWith(OVERRIDES_FILE_RAVENWOOD));
}
/**
@@ -678,12 +697,25 @@ final class CompatConfig {
return changeInfos;
}
+ /**
+ * Load all config files in a given directory.
+ */
void initConfigFromLib(File libraryDir) {
+ initConfigFromLib(libraryDir, (file) -> true);
+ }
+
+ /**
+ * Load config files in a given directory, but only the ones that match {@code includingFilter}.
+ */
+ void initConfigFromLib(File libraryDir, Predicate<File> includingFilter) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.d(TAG, "No directory " + libraryDir + ", skipping");
return;
}
for (File f : libraryDir.listFiles()) {
+ if (!includingFilter.test(f)) {
+ continue;
+ }
Slog.d(TAG, "Found a config file: " + f.getPath());
//TODO(b/138222363): Handle duplicate ids across config files.
readConfig(f);
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index e3b6d032b7f0..362c69797161 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -45,6 +45,7 @@ import com.android.internal.compat.OverrideAllowedState;
/**
* Implementation of the policy for allowing compat change overrides.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class OverrideValidatorImpl extends IOverrideValidator.Stub {
private AndroidBuildClassifier mAndroidBuildClassifier;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 8d64383b32b9..eeac26031719 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -32,10 +32,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -65,6 +67,7 @@ import java.util.Map;
/**
* System server internal API for gating and reporting compatibility changes.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PlatformCompat extends IPlatformCompat.Stub {
private static final String TAG = "Compatibility";
@@ -73,8 +76,10 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
private final AndroidBuildClassifier mBuildClassifier;
+ private Boolean mIsWear;
public PlatformCompat(Context context) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mBuildClassifier = new AndroidBuildClassifier();
@@ -85,6 +90,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
PlatformCompat(Context context, CompatConfig compatConfig,
AndroidBuildClassifier buildClassifier,
ChangeReporter changeReporter) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mChangeReporter = changeReporter;
mCompatConfig = compatConfig;
@@ -507,14 +513,22 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
private ApplicationInfo fixTargetSdk(ApplicationInfo appInfo, int uid) {
+
+ // mIsWear doesn't need to be locked, ok if executes twice
+ if (mIsWear == null) {
+ mIsWear = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
// b/282922910 - we don't want apps sharing system uid and targeting
// older target sdk to impact all system uid apps
- if (Flags.systemUidTargetSystemSdk() && uid == Process.SYSTEM_UID) {
+ if (Flags.systemUidTargetSystemSdk() && !mIsWear &&
+ uid == Process.SYSTEM_UID) {
appInfo.targetSdkVersion = Build.VERSION.SDK_INT;
}
return appInfo;
}
+ @android.ravenwood.annotation.RavenwoodReplace
private void killPackage(String packageName) {
int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName,
0, UserHandle.myUserId());
@@ -528,6 +542,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
killUid(UserHandle.getAppId(uid));
}
+ @SuppressWarnings("unused")
+ private void killPackage$ravenwood(String packageName) {
+ // TODO Maybe crash if the package is the self.
+ Slog.w(TAG, "killPackage() is ignored on Ravenwood: packageName=" + packageName);
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
private void killUid(int appId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -542,6 +563,12 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
}
+ @SuppressWarnings("unused")
+ private void killUid$ravenwood(int appId) {
+ // TODO Maybe crash if the UID is the self.
+ Slog.w(TAG, "killUid() is ignored on Ravenwood: appId=" + appId);
+ }
+
private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) {
for (Long changeId : changeIds) {
if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
index 5d7af650db0b..7a3feb515706 100644
--- a/services/core/java/com/android/server/compat/PlatformCompatNative.java
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -23,6 +23,7 @@ import com.android.internal.compat.IPlatformCompatNative;
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PlatformCompatNative extends IPlatformCompatNative.Stub {
private final PlatformCompat mPlatformCompat;
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
index e8762a3e935c..0ec68792a886 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -46,6 +46,7 @@ import java.util.regex.Pattern;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class AppCompatOverridesParser {
/**
* Flag for specifying all compat change IDs owned by a namespace. See {@link
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index fe002ce00d32..8637d2dfe565 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -68,6 +68,7 @@ import java.util.Set;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AppCompatOverridesService {
private static final String TAG = "AppCompatOverridesService";
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 4ad7c10a1444..d2c044fdbb5e 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -255,6 +255,11 @@ final class DisplayDeviceInfo {
public static final int DIFF_MODE_ID = 1 << 7;
/**
+ * Diff result: The frame rate override list differs.
+ */
+ public static final int DIFF_FRAME_RATE_OVERRIDE = 1 << 8;
+
+ /**
* Diff result: Catch-all for "everything changed"
*/
public static final int DIFF_EVERYTHING = 0XFFFFFFFF;
@@ -523,6 +528,9 @@ final class DisplayDeviceInfo {
if (modeId != other.modeId) {
diff |= DIFF_MODE_ID;
}
+ if (!Arrays.equals(frameRateOverrides, other.frameRateOverrides)) {
+ diff |= DIFF_FRAME_RATE_OVERRIDE;
+ }
if (!Objects.equals(name, other.name)
|| !Objects.equals(uniqueId, other.uniqueId)
|| width != other.width
@@ -546,7 +554,6 @@ final class DisplayDeviceInfo {
|| !Objects.equals(deviceProductInfo, other.deviceProductInfo)
|| ownerUid != other.ownerUid
|| !Objects.equals(ownerPackageName, other.ownerPackageName)
- || !Arrays.equals(frameRateOverrides, other.frameRateOverrides)
|| !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 086f8a94d9b8..5f7bc4effa1b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -27,6 +27,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.display.utils.DebugUtils;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@@ -177,18 +178,22 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {
"handleDisplayDeviceChanged");
}
int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
- if (diff == DisplayDeviceInfo.DIFF_STATE) {
+ if (diff == 0) {
+ Slog.i(TAG, "Display device same: " + info);
+ } else if (diff == DisplayDeviceInfo.DIFF_STATE) {
Slog.i(TAG, "Display device changed state: \"" + info.name
+ "\", " + Display.stateToString(info.state));
} else if (diff == DisplayDeviceInfo.DIFF_ROTATION) {
Slog.i(TAG, "Display device rotated: \"" + info.name
+ "\", " + Surface.rotationToString(info.rotation));
- } else if (diff
- == (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS)) {
+ } else if ((diff &
+ (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS
+ | DisplayDeviceInfo.DIFF_FRAME_RATE_OVERRIDE)) != 0) {
Slog.i(TAG, "Display device changed render timings: \"" + info.name
+ "\", renderFrameRate=" + info.renderFrameRate
+ ", presentationDeadlineNanos=" + info.presentationDeadlineNanos
- + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos);
+ + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos
+ + ", frameRateOverrides=" + Arrays.toString(info.frameRateOverrides));
} else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) {
if (DEBUG) {
Slog.i(TAG, "Display device changed committed state: \"" + info.name
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index f5a75c7d1c38..5a2610b00772 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -25,7 +25,7 @@ import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.Manifest.permission.RESTRICT_DISPLAY_MODES;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
-import static android.hardware.display.DisplayManager.EventFlag;
+import static android.hardware.display.DisplayManagerGlobal.InternalEventFlag;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -96,6 +96,7 @@ import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -118,6 +119,7 @@ import android.os.IBinder.DeathRecipient;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
+import android.os.PermissionEnforcer;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -1390,16 +1392,16 @@ public final class DisplayManagerService extends SystemService {
}
private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid,
- int callingUid, @EventFlag long eventFlagsMask) {
+ int callingUid, @InternalEventFlag long internalEventFlagsMask) {
synchronized (mSyncRoot) {
CallbackRecord record = mCallbacks.get(callingPid);
if (record != null) {
- record.updateEventFlagsMask(eventFlagsMask);
+ record.updateEventFlagsMask(internalEventFlagsMask);
return;
}
- record = new CallbackRecord(callingPid, callingUid, callback, eventFlagsMask);
+ record = new CallbackRecord(callingPid, callingUid, callback, internalEventFlagsMask);
try {
IBinder binder = callback.asBinder();
binder.linkToDeath(record, 0);
@@ -4009,7 +4011,7 @@ public final class DisplayManagerService extends SystemService {
public final int mPid;
public final int mUid;
private final IDisplayManagerCallback mCallback;
- private @DisplayManager.EventFlag AtomicLong mEventFlagsMask;
+ private @InternalEventFlag AtomicLong mInternalEventFlagsMask;
private final String mPackageName;
public boolean mWifiDisplayScanRequested;
@@ -4030,11 +4032,11 @@ public final class DisplayManagerService extends SystemService {
private boolean mFrozen;
CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
- @EventFlag long eventFlagsMask) {
+ @InternalEventFlag long internalEventFlagsMask) {
mPid = pid;
mUid = uid;
mCallback = callback;
- mEventFlagsMask = new AtomicLong(eventFlagsMask);
+ mInternalEventFlagsMask = new AtomicLong(internalEventFlagsMask);
mCached = false;
mFrozen = false;
@@ -4056,8 +4058,8 @@ public final class DisplayManagerService extends SystemService {
mPackageName = packageNames == null ? null : packageNames[0];
}
- public void updateEventFlagsMask(@EventFlag long eventFlag) {
- mEventFlagsMask.set(eventFlag);
+ public void updateEventFlagsMask(@InternalEventFlag long internalEventFlag) {
+ mInternalEventFlagsMask.set(internalEventFlag);
}
/**
@@ -4121,13 +4123,13 @@ public final class DisplayManagerService extends SystemService {
if (!shouldSendEvent(event)) {
if (extraLogging(mPackageName)) {
Slog.i(TAG,
- "Not sending displayEvent: " + event + " due to flag:"
- + mEventFlagsMask);
+ "Not sending displayEvent: " + event + " due to mask:"
+ + mInternalEventFlagsMask);
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
Trace.instant(Trace.TRACE_TAG_POWER,
- "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsFlag="
- + mEventFlagsMask);
+ "notifyDisplayEventAsync#notSendingEvent=" + event
+ + ",mInternalEventFlagsMask=" + mInternalEventFlagsMask);
}
// The client is not interested in this event, so do nothing.
return true;
@@ -4173,22 +4175,29 @@ public final class DisplayManagerService extends SystemService {
* Return true if the client is interested in this event.
*/
private boolean shouldSendEvent(@DisplayEvent int event) {
- final long flag = mEventFlagsMask.get();
+ final long mask = mInternalEventFlagsMask.get();
switch (event) {
case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
- return (flag & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
+ return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
- return (flag & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
+ return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
- return (flag & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0;
+ return (mask
+ & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED)
+ != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
- return (flag & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
+ return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
- return (flag & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0;
+ return (mask
+ & DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED)
+ != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED:
// fallthrough
case DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED:
- return (flag & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0;
+ return (mask
+ & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+ != 0;
default:
// This should never happen.
Slog.e(TAG, "Unknown display event " + event);
@@ -4314,6 +4323,10 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
final class BinderService extends IDisplayManager.Stub {
+ BinderService() {
+ super(PermissionEnforcer.fromContext(getContext()));
+ }
+
/**
* Returns information about the specified logical display.
*
@@ -4374,15 +4387,16 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public void registerCallback(IDisplayManagerCallback callback) {
- registerCallbackWithEventMask(callback, DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ registerCallbackWithEventMask(callback,
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED);
}
@Override // Binder call
@SuppressLint("AndroidFrameworkRequiresPermission") // Permission only required sometimes
public void registerCallbackWithEventMask(IDisplayManagerCallback callback,
- @EventFlag long eventFlagsMask) {
+ @InternalEventFlag long internalEventFlagsMask) {
if (callback == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -4391,7 +4405,9 @@ public final class DisplayManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
if (mFlags.isConnectedDisplayManagementEnabled()) {
- if ((eventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ if ((internalEventFlagsMask
+ & DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
mContext.enforceCallingOrSelfPermission(MANAGE_DISPLAYS,
"Permission required to get signals about connection events.");
}
@@ -4399,7 +4415,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid, callingUid, eventFlagsMask);
+ registerCallbackInternal(callback, callingPid, callingUid, internalEventFlagsMask);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -5192,6 +5208,25 @@ public final class DisplayManagerService extends SystemService {
}
return ddc.getDefaultDozeBrightness();
}
+
+ @EnforcePermission(MANAGE_DISPLAYS)
+ @Override // Binder call
+ public DisplayTopology getDisplayTopology() {
+ getDisplayTopology_enforcePermission();
+ if (mDisplayTopologyCoordinator == null) {
+ return null;
+ }
+ return mDisplayTopologyCoordinator.getTopology();
+ }
+
+ @EnforcePermission(MANAGE_DISPLAYS)
+ @Override // Binder call
+ public void setDisplayTopology(DisplayTopology topology) {
+ setDisplayTopology_enforcePermission();
+ if (mDisplayTopologyCoordinator != null) {
+ mDisplayTopologyCoordinator.setTopology(topology);
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a9ed0aaf324b..c90dfbf5456e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1595,7 +1595,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
// we broadcast this change through setting.
final float unthrottledBrightnessState = rawBrightnessState;
- DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest,
+ DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(
+ displayBrightnessState, mPowerRequest,
brightnessState, slowChange, /* displayState= */ state);
brightnessState = clampedState.getBrightness();
slowChange = clampedState.isSlowChange();
@@ -2003,7 +2004,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mCachedBrightnessInfo.brightnessMax.value,
mCachedBrightnessInfo.hbmMode.value,
mCachedBrightnessInfo.hbmTransitionPoint.value,
- mCachedBrightnessInfo.brightnessMaxReason.value);
+ mCachedBrightnessInfo.brightnessMaxReason.value,
+ mCachedBrightnessInfo.brightnessReason.value
+ == BrightnessReason.REASON_OVERRIDE);
}
}
@@ -2028,6 +2031,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@BrightnessInfo.BrightnessMaxReason int maxReason =
state != null ? state.getBrightnessMaxReason()
: BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ BrightnessReason brightnessReason = state != null ? state.getBrightnessReason()
+ : new BrightnessReason(BrightnessReason.REASON_UNKNOWN);
final float minBrightness = Math.max(stateMin, Math.min(
mBrightnessRangeController.getCurrentBrightnessMin(), stateMax));
final float maxBrightness = Math.min(
@@ -2055,6 +2060,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
changed |=
mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
maxReason);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessReason,
+ brightnessReason.getReason());
return changed;
}
}
@@ -2683,6 +2691,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
+ mCachedBrightnessInfo.hbmTransitionPoint.value);
pw.println(" mCachedBrightnessInfo.brightnessMaxReason ="
+ mCachedBrightnessInfo.brightnessMaxReason.value);
+ pw.println(" mCachedBrightnessInfo.brightnessReason ="
+ + mCachedBrightnessInfo.brightnessReason);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -3390,6 +3400,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
public MutableInt brightnessMaxReason =
new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+ public MutableInt brightnessReason = new MutableInt(BrightnessReason.REASON_UNKNOWN);
public boolean checkAndSetFloat(MutableFloat mf, float f) {
if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index b101e5893b97..47226861545f 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.hardware.display.DisplayTopology;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
@@ -33,7 +34,7 @@ import java.util.function.BooleanSupplier;
class DisplayTopologyCoordinator {
@GuardedBy("mLock")
- private final DisplayTopology mTopology;
+ private DisplayTopology mTopology;
/**
* Check if extended displays are enabled. If not, a topology is not needed.
@@ -76,6 +77,21 @@ class DisplayTopologyCoordinator {
}
/**
+ * @return A deep copy of the topology.
+ */
+ DisplayTopology getTopology() {
+ synchronized (mLock) {
+ return mTopology;
+ }
+ }
+
+ void setTopology(DisplayTopology topology) {
+ synchronized (mLock) {
+ mTopology = topology;
+ }
+ }
+
+ /**
* Print the object's state and debug information into the given stream.
* @param pw The stream to dump information to.
*/
@@ -108,6 +124,7 @@ class DisplayTopologyCoordinator {
&& info.displayGroupId == Display.DEFAULT_DISPLAY_GROUP;
}
+ @VisibleForTesting
static class Injector {
DisplayTopology getTopology() {
return new DisplayTopology();
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 421145390190..dabef84fec31 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -51,6 +51,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -64,6 +65,7 @@ import android.view.SurfaceControl;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
@@ -323,7 +325,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private int mWidth;
private int mHeight;
private int mDensityDpi;
- private float mRequestedRefreshRate;
+ private final float mRequestedRefreshRate;
private Surface mSurface;
private DisplayDeviceInfo mInfo;
private int mDisplayState;
@@ -332,7 +334,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private Display.Mode mMode;
private int mDisplayIdToMirror;
private boolean mIsWindowManagerMirroring;
- private DisplayCutout mDisplayCutout;
+ private final DisplayCutout mDisplayCutout;
+ private final float mDefaultBrightness;
+ private float mCurrentBrightness;
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
@@ -349,6 +353,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mDensityDpi = virtualDisplayConfig.getDensityDpi();
mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate();
mDisplayCutout = virtualDisplayConfig.getDisplayCutout();
+ mDefaultBrightness = virtualDisplayConfig.getDefaultBrightness();
+ mCurrentBrightness = mDefaultBrightness;
mMode = createMode(mWidth, mHeight, getRefreshRate());
mSurface = surface;
mFlags = flags;
@@ -457,6 +463,12 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mCallback.dispatchDisplayResumed();
}
}
+ if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()
+ && BrightnessUtils.isValidBrightnessValue(brightnessState)
+ && brightnessState != mCurrentBrightness) {
+ mCurrentBrightness = brightnessState;
+ mCallback.dispatchRequestedBrightnessChanged(mCurrentBrightness);
+ }
return null;
}
@@ -623,6 +635,10 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mInfo.state = mDisplayState;
}
+ mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
+ mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+ mInfo.brightnessDefault = mDefaultBrightness;
+
mInfo.ownerUid = mOwnerUid;
mInfo.ownerPackageName = mOwnerPackageName;
@@ -642,6 +658,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private static final int MSG_ON_DISPLAY_PAUSED = 0;
private static final int MSG_ON_DISPLAY_RESUMED = 1;
private static final int MSG_ON_DISPLAY_STOPPED = 2;
+ private static final int MSG_ON_REQUESTED_BRIGHTNESS_CHANGED = 3;
private final IVirtualDisplayCallback mCallback;
@@ -663,6 +680,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
case MSG_ON_DISPLAY_STOPPED:
mCallback.onStopped();
break;
+ case MSG_ON_REQUESTED_BRIGHTNESS_CHANGED:
+ mCallback.onRequestedBrightnessChanged((Float) msg.obj);
+ break;
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify listener of virtual display event.", e);
@@ -677,6 +697,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
sendEmptyMessage(MSG_ON_DISPLAY_RESUMED);
}
+ public void dispatchRequestedBrightnessChanged(float brightness) {
+ Message msg = obtainMessage(MSG_ON_REQUESTED_BRIGHTNESS_CHANGED, brightness);
+ sendMessage(msg);
+ }
+
public void dispatchDisplayStopped() {
sendEmptyMessage(MSG_ON_DISPLAY_STOPPED);
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index a10094fdfbb8..6e579bf161ee 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -172,17 +172,18 @@ public class BrightnessClamperController {
* Applies clamping
* Called in DisplayControllerHandler
*/
- public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request,
+ public DisplayBrightnessState clamp(DisplayBrightnessState displayBrightnessState,
+ DisplayManagerInternal.DisplayPowerRequest request,
float brightnessValue, boolean slowChange, int displayState) {
float cappedBrightness = Math.min(brightnessValue, mBrightnessCap);
- DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder();
+ DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from(
+ displayBrightnessState);
builder.setIsSlowChange(slowChange);
builder.setBrightness(cappedBrightness);
builder.setMaxBrightness(mBrightnessCap);
builder.setCustomAnimationRate(mCustomAnimationRate);
builder.setBrightnessMaxReason(getBrightnessMaxReason());
-
if (mClamperType != null) {
builder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
if (!mClamperApplied) {
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index c0aa4cc6fa24..71f17d1f411e 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -242,6 +242,11 @@ public class DisplayManagerFlags {
Flags::autoBrightnessModeBedtimeWear
);
+ private final FlagState mEnablePluginManagerFlagState = new FlagState(
+ Flags.FLAG_ENABLE_PLUGIN_MANAGER,
+ Flags::enablePluginManager
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -517,6 +522,10 @@ public class DisplayManagerFlags {
return mAutoBrightnessModeBedtimeWearFlagState.isEnabled();
}
+ public boolean isPluginManagerEnabled() {
+ return mEnablePluginManagerFlagState.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -568,6 +577,7 @@ public class DisplayManagerFlags {
pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled);
pw.println(" " + mHasArrSupport);
pw.println(" " + mAutoBrightnessModeBedtimeWearFlagState);
+ pw.println(" " + mEnablePluginManagerFlagState);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index a9bdccef2300..7850360c7dbf 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -446,3 +446,11 @@ flag {
bug: "365163968"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_plugin_manager"
+ namespace: "display_manager"
+ description: "Flag to enable DisplayManager plugins"
+ bug: "354059797"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 88562ab9ba2d..8423e1911764 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -2077,8 +2077,8 @@ public class DisplayModeDirector {
mDeviceConfigDisplaySettings.startListening();
mInjector.registerDisplayListener(this, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
+ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS);
}
private void setLoggingEnabled(boolean loggingEnabled) {
@@ -2878,8 +2878,8 @@ public class DisplayModeDirector {
}
mDisplayManagerInternal = mInjector.getDisplayManagerInternal();
mInjector.registerDisplayListener(this, mHandler,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ DisplayManager.EVENT_FLAG_DISPLAY_REMOVED,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS);
}
/**
@@ -3108,6 +3108,9 @@ public class DisplayModeDirector {
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler, long flags);
+ void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ Handler handler, long flags, long privateFlags);
+
Display getDisplay(int displayId);
Display[] getDisplays();
@@ -3175,6 +3178,12 @@ public class DisplayModeDirector {
}
@Override
+ public void registerDisplayListener(DisplayManager.DisplayListener listener,
+ Handler handler, long flags, long privateFlags) {
+ getDisplayManager().registerDisplayListener(listener, handler, flags, privateFlags);
+ }
+
+ @Override
public Display getDisplay(int displayId) {
return getDisplayManager().getDisplay(displayId);
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 19305dedcb06..0c04be10d06d 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_DREAM_SERVICE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.service.dreams.Flags.cleanupDreamSettingsOnUninstall;
import static android.service.dreams.Flags.dreamHandlesBeingObscured;
import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
@@ -64,12 +65,15 @@ import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.DumpUtils;
@@ -86,6 +90,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -155,6 +160,10 @@ public final class DreamManagerService extends SystemService {
private ComponentName mDreamOverlayServiceName;
private final AmbientDisplayConfiguration mDozeConfig;
+
+ /** Stores {@link PerUserPackageMonitor} to monitor dream uninstalls. */
+ private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
+
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@Nullable
@@ -218,6 +227,15 @@ public final class DreamManagerService extends SystemService {
}
}
+ private final class PerUserPackageMonitor extends PackageMonitor {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ super.onPackageRemoved(packageName, uid);
+ final int userId = getChangingUserId();
+ updateDreamOnPackageRemoved(packageName, userId);
+ }
+ }
+
public DreamManagerService(Context context) {
this(context, new DreamHandler(FgThread.get().getLooper()));
}
@@ -333,6 +351,37 @@ public final class DreamManagerService extends SystemService {
});
}
+ @Override
+ public void onUserStarting(@NonNull TargetUser user) {
+ super.onUserStarting(user);
+ if (cleanupDreamSettingsOnUninstall()) {
+ mHandler.post(() -> {
+ final int userId = user.getUserIdentifier();
+ if (!mPackageMonitors.contains(userId)) {
+ final PackageMonitor monitor = new PerUserPackageMonitor();
+ monitor.register(mContext, UserHandle.of(userId), mHandler);
+ mPackageMonitors.put(userId, monitor);
+ } else {
+ Slog.w(TAG, "Package monitor already registered for " + userId);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
+ super.onUserStopping(user);
+ if (cleanupDreamSettingsOnUninstall()) {
+ mHandler.post(() -> {
+ final PackageMonitor monitor = mPackageMonitors.removeReturnOld(
+ user.getUserIdentifier());
+ if (monitor != null) {
+ monitor.unregister();
+ }
+ });
+ }
+ }
+
private void dumpInternal(PrintWriter pw) {
synchronized (mLock) {
pw.println("DREAM MANAGER (dumpsys dreams)");
@@ -605,7 +654,7 @@ public final class DreamManagerService extends SystemService {
private ComponentName chooseDreamForUser(boolean doze, int userId) {
if (doze) {
ComponentName dozeComponent = getDozeComponent(userId);
- return validateDream(dozeComponent) ? dozeComponent : null;
+ return validateDream(dozeComponent, userId) ? dozeComponent : null;
}
if (mSystemDreamComponent != null) {
@@ -616,11 +665,11 @@ public final class DreamManagerService extends SystemService {
return dreams != null && dreams.length != 0 ? dreams[0] : null;
}
- private boolean validateDream(ComponentName component) {
+ private boolean validateDream(ComponentName component, int userId) {
if (component == null) return false;
- final ServiceInfo serviceInfo = getServiceInfo(component);
+ final ServiceInfo serviceInfo = getServiceInfo(component, userId);
if (serviceInfo == null) {
- Slog.w(TAG, "Dream " + component + " does not exist");
+ Slog.w(TAG, "Dream " + component + " does not exist on user " + userId);
return false;
} else if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP
&& !BIND_DREAM_SERVICE.equals(serviceInfo.permission)) {
@@ -647,7 +696,7 @@ public final class DreamManagerService extends SystemService {
List<ComponentName> validComponents = new ArrayList<>();
if (components != null) {
for (ComponentName component : components) {
- if (validateDream(component)) {
+ if (validateDream(component, userId)) {
validComponents.add(component);
}
}
@@ -664,6 +713,30 @@ public final class DreamManagerService extends SystemService {
return validComponents.toArray(new ComponentName[validComponents.size()]);
}
+ private void updateDreamOnPackageRemoved(String packageName, int userId) {
+ final ComponentName[] componentNames = componentsFromString(
+ Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_COMPONENTS,
+ userId));
+ if (componentNames != null) {
+ // Filter out any components in the removed package.
+ final ComponentName[] filteredComponents =
+ Arrays.stream(componentNames)
+ .filter((componentName -> !isSamePackage(packageName, componentName)))
+ .toArray(ComponentName[]::new);
+ if (filteredComponents.length != componentNames.length) {
+ setDreamComponentsForUser(userId, filteredComponents);
+ }
+ }
+ }
+
+ private static boolean isSamePackage(String packageName, ComponentName componentName) {
+ if (packageName == null || componentName == null) {
+ return false;
+ }
+ return TextUtils.equals(componentName.getPackageName(), packageName);
+ }
+
private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_COMPONENTS,
@@ -718,9 +791,10 @@ public final class DreamManagerService extends SystemService {
return userId == mainUserId;
}
- private ServiceInfo getServiceInfo(ComponentName name) {
+ private ServiceInfo getServiceInfo(ComponentName name, int userId) {
+ final Context userContext = mContext.createContextAsUser(UserHandle.of(userId), 0);
try {
- return name != null ? mContext.getPackageManager().getServiceInfo(name,
+ return name != null ? userContext.getPackageManager().getServiceInfo(name,
PackageManager.MATCH_DEBUG_TRIAGED_MISSING) : null;
} catch (NameNotFoundException e) {
return null;
@@ -813,7 +887,7 @@ public final class DreamManagerService extends SystemService {
private void writePulseGestureEnabled() {
ComponentName name = getDozeComponent();
- boolean dozeEnabled = validateDream(name);
+ boolean dozeEnabled = validateDream(name, ActivityManager.getCurrentUser());
LocalServices.getService(InputManagerInternal.class).setPulseGestureEnabled(dozeEnabled);
}
@@ -823,7 +897,10 @@ public final class DreamManagerService extends SystemService {
}
StringBuilder names = new StringBuilder();
for (ComponentName componentName : componentNames) {
- if (names.length() > 0) {
+ if (componentName == null) {
+ continue;
+ }
+ if (!names.isEmpty()) {
names.append(',');
}
names.append(componentName.flattenToString());
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index 69ba785b3b4f..eea5c982c537 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -67,3 +67,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "backstage_power"
+ name: "rate_limit_battery_changed_broadcast"
+ description: "Optimize the delivery of the battery changed broadcast by rate limiting the frequency of the updates"
+ bug: "362337621"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index f2e2f653f929..5b4c0337862b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -1001,7 +1001,7 @@ final class HdmiCecController {
try {
// Create an AIDL callback that can callback onHotplugEvent
mHdmiConnection.setCallback(new HdmiConnectionCallbackAidl(callback));
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e);
}
}
@@ -1134,7 +1134,7 @@ final class HdmiCecController {
i++;
}
return hdmiPortInfo;
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
HdmiLogger.error("Failed to get port information : ", e);
return null;
}
@@ -1144,7 +1144,7 @@ final class HdmiCecController {
public boolean nativeIsConnected(int port) {
try {
return mHdmiConnection.isConnected(port);
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
HdmiLogger.error("Failed to get connection info : ", e);
return false;
}
@@ -1158,7 +1158,7 @@ final class HdmiCecController {
HdmiLogger.error(
"Could not set HPD signal type for portId " + portId + " to " + signal
+ ". Error: ", sse.errorCode);
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
HdmiLogger.error(
"Could not set HPD signal type for portId " + portId + " to " + signal
+ ". Exception: ", e);
@@ -1169,7 +1169,7 @@ final class HdmiCecController {
public int nativeGetHpdSignalType(int portId) {
try {
return mHdmiConnection.getHpdSignal(portId);
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
HdmiLogger.error(
"Could not get HPD signal type for portId " + portId + ". Exception: ", e);
return Constants.HDMI_HPD_TYPE_PHYSICAL;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index b696c5481205..1b527daafd24 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -273,13 +273,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private class DelayedStandbyOnActiveSourceLostRunnable implements Runnable {
@Override
public void run() {
- if (mService.getPowerManagerInternal().wasDeviceIdleFor(
- STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS)) {
+ if (!isActiveSource()) {
mService.standby();
- } else {
- mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(),
- getDeviceInfo().getDeviceType(), Constants.ADDR_TV,
- "DelayedActiveSourceLostStandbyRunnable");
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index bf415a344f4c..7505c710f483 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -646,9 +646,9 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
int address = message.getSource();
int type = message.getParams()[2];
- if (!ActiveSource.of(address, path).equals(getActiveSource())) {
- HdmiLogger.debug("Check if a new device is connected to the active path");
- handleNewDeviceAtTheTailOfActivePath(path);
+ if (getActiveSource().logicalAddress != address && getActivePath() == path) {
+ HdmiLogger.debug("New logical address detected on the current active path.");
+ startRoutingControl(path, path, null);
}
startNewDeviceAction(ActiveSource.of(address, path), type);
return Constants.HANDLED;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 132d6fa377eb..0c5069f81774 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -771,6 +771,14 @@ public class HdmiControlService extends SystemService {
Slog.i(TAG, "Device does not support eARC.");
}
mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
+ if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) {
+ Slog.w(TAG, "Re-enable CEC on boot-up since it was disabled due to low energy "
+ + " mode.");
+ getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HDMI_CEC_CONTROL_ENABLED);
+ setWasCecDisabledOnStandbyByLowEnergyMode(false);
+ setCecEnabled(HDMI_CEC_CONTROL_ENABLED);
+ }
if (isCecControlEnabled()) {
initializeCec(INITIATED_BY_BOOT_UP);
} else {
diff --git a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
new file mode 100644
index 000000000000..aef207f9c027
--- /dev/null
+++ b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.XmlResourceParser;
+import android.hardware.input.AppLaunchData;
+import android.hardware.input.InputGestureData;
+import android.hardware.input.KeyGestureEvent;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+
+import com.android.internal.R;
+import com.android.internal.policy.IShortcutService;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Manages quick launch app shortcuts by parsing {@code bookmarks.xml} and intercepting the
+ * correct key combinations for the app shortcuts defined.
+ *
+ * Currently there are 2 ways of defining shortcuts:
+ * - Adding shortcuts to {@code bookmarks.xml}
+ * - Calling into {@code registerShortcutKey()}.
+ */
+final class AppLaunchShortcutManager {
+ private static final String TAG = "AppShortcutManager";
+
+ private static final String TAG_BOOKMARKS = "bookmarks";
+ private static final String TAG_BOOKMARK = "bookmark";
+
+ private static final String ATTRIBUTE_PACKAGE = "package";
+ private static final String ATTRIBUTE_CLASS = "class";
+ private static final String ATTRIBUTE_SHORTCUT = "shortcut";
+ private static final String ATTRIBUTE_CATEGORY = "category";
+ private static final String ATTRIBUTE_SHIFT = "shift";
+ private static final String ATTRIBUTE_ROLE = "role";
+
+ private static final int SHORTCUT_CODE_META_MASK =
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON
+ | KeyEvent.META_META_ON;
+
+ private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
+
+ /* Table of Application Launch keys. Maps from key codes to intent categories.
+ *
+ * These are special keys that are used to launch particular kinds of applications,
+ * such as a web browser. HID defines nearly a hundred of them in the Consumer (0x0C)
+ * usage page. We don't support quite that many yet...
+ */
+ private static final SparseArray<String> sApplicationLaunchKeyCategories;
+ private static final SparseArray<String> sApplicationLaunchKeyRoles;
+ static {
+ sApplicationLaunchKeyRoles = new SparseArray<>();
+ sApplicationLaunchKeyCategories = new SparseArray<>();
+ sApplicationLaunchKeyRoles.append(
+ KeyEvent.KEYCODE_EXPLORER, RoleManager.ROLE_BROWSER);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_ENVELOPE, Intent.CATEGORY_APP_EMAIL);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_CONTACTS, Intent.CATEGORY_APP_CONTACTS);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_CALENDAR, Intent.CATEGORY_APP_CALENDAR);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_MUSIC, Intent.CATEGORY_APP_MUSIC);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR);
+ }
+
+ private final Context mContext;
+ private boolean mSearchKeyShortcutPending = false;
+ private boolean mConsumeSearchKeyUp = true;
+ private final Map<InputGestureData.Trigger, InputGestureData> mBookmarks = new HashMap<>();
+
+ @SuppressLint("MissingPermission")
+ AppLaunchShortcutManager(Context context) {
+ mContext = context;
+ }
+
+ public void systemRunning() {
+ loadShortcuts();
+ }
+
+ private void loadShortcuts() {
+ try {
+ XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks);
+ XmlUtils.beginDocument(parser, TAG_BOOKMARKS);
+ KeyCharacterMap virtualKcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+
+ if (!TAG_BOOKMARK.equals(parser.getName())) {
+ Log.w(TAG, "TAG_BOOKMARK not found");
+ break;
+ }
+
+ String packageName = parser.getAttributeValue(null, ATTRIBUTE_PACKAGE);
+ String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS);
+ String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
+ String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT);
+ String roleName = parser.getAttributeValue(null, ATTRIBUTE_ROLE);
+
+ // TODO(b/358569822): Shift bookmarks to use keycode instead of shortcutChar
+ int keycode = KeyEvent.KEYCODE_UNKNOWN;
+ String shortcut = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT);
+ if (!TextUtils.isEmpty(shortcut)) {
+ KeyEvent[] events = virtualKcm.getEvents(new char[]{shortcut.toLowerCase(
+ Locale.ROOT).charAt(0)});
+ // Single key press can generate the character
+ if (events != null && events.length == 2) {
+ keycode = events[0].getKeyCode();
+ }
+ }
+ if (keycode == KeyEvent.KEYCODE_UNKNOWN) {
+ Log.w(TAG, "Keycode required for bookmark with category=" + categoryName
+ + " packageName=" + packageName + " className=" + className
+ + " role=" + roleName + " shiftName=" + shiftName
+ + " shortcut=" + shortcut);
+ continue;
+ }
+
+ final boolean isShiftShortcut = (shiftName != null && shiftName.toLowerCase(
+ Locale.ROOT).equals("true"));
+ AppLaunchData launchData = null;
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+ launchData = AppLaunchData.createLaunchDataForComponent(packageName, className);
+ } else if (!TextUtils.isEmpty(categoryName)) {
+ launchData = AppLaunchData.createLaunchDataForCategory(categoryName);
+ } else if (!TextUtils.isEmpty(roleName)) {
+ launchData = AppLaunchData.createLaunchDataForRole(roleName);
+ }
+ if (launchData != null) {
+ Log.d(TAG, "adding shortcut " + launchData + "shift="
+ + isShiftShortcut + " keycode=" + keycode);
+ // All bookmarks are based on Action key
+ int modifierState =
+ KeyEvent.META_META_ON | (isShiftShortcut ? KeyEvent.META_SHIFT_ON : 0);
+ InputGestureData bookmark = new InputGestureData.Builder()
+ .setTrigger(InputGestureData.createKeyTrigger(keycode, modifierState))
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAppLaunchData(launchData)
+ .build();
+ mBookmarks.put(bookmark.getTrigger(), bookmark);
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Got exception parsing bookmarks.", e);
+ }
+ }
+
+ public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
+ throws RemoteException {
+ IShortcutService service = mShortcutKeyServices.get(shortcutCode);
+ if (service != null && service.asBinder().pingBinder()) {
+ throw new RemoteException("Key: " + shortcutCode + ", already exists.");
+ }
+
+ mShortcutKeyServices.put(shortcutCode, shortcutService);
+ }
+
+ /**
+ * Handle the shortcut to {@link IShortcutService}
+ * @param keyCode The key code of the event.
+ * @param metaState The meta key modifier state.
+ * @return True if invoked the shortcut, otherwise false.
+ */
+ private boolean handleShortcutService(int keyCode, int metaState) {
+ final long shortcutCodeMeta = metaState & SHORTCUT_CODE_META_MASK;
+ if (shortcutCodeMeta == 0) {
+ return false;
+ }
+ long shortcutCode = keyCode | (shortcutCodeMeta << Integer.SIZE);
+ IShortcutService shortcutService = mShortcutKeyServices.get(shortcutCode);
+ if (shortcutService != null) {
+ try {
+ shortcutService.notifyShortcutKeyPressed(shortcutCode);
+ } catch (RemoteException e) {
+ Log.w(TAG,
+ "Shortcut key service not found, deleting shortcut code: " + shortcutCode);
+ mShortcutKeyServices.delete(shortcutCode);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle the shortcut to Launch application.
+ *
+ * @param keyEvent The key event.
+ */
+ @SuppressLint("MissingPermission")
+ @Nullable
+ private AppLaunchData interceptShortcut(KeyEvent keyEvent) {
+ final int keyCode = keyEvent.getKeyCode();
+ final int modifierState = keyEvent.getMetaState() & SHORTCUT_CODE_META_MASK;
+ // Shortcuts are invoked through Search+key, so intercept those here
+ // Any printing key that is chorded with Search should be consumed
+ // even if no shortcut was invoked. This prevents text from being
+ // inadvertently inserted when using a keyboard that has built-in macro
+ // shortcut keys (that emit Search+x) and some of them are not registered.
+ if (mSearchKeyShortcutPending) {
+ KeyCharacterMap kcm = keyEvent.getKeyCharacterMap();
+ if (kcm != null && kcm.isPrintingKey(keyCode)) {
+ mConsumeSearchKeyUp = true;
+ mSearchKeyShortcutPending = false;
+ } else {
+ return null;
+ }
+ } else if (modifierState == 0) {
+ AppLaunchData appLaunchData = null;
+ // Handle application launch keys.
+ String role = sApplicationLaunchKeyRoles.get(keyCode);
+ String category = sApplicationLaunchKeyCategories.get(keyCode);
+ if (!TextUtils.isEmpty(role)) {
+ appLaunchData = AppLaunchData.createLaunchDataForRole(role);
+ } else if (!TextUtils.isEmpty(category)) {
+ appLaunchData = AppLaunchData.createLaunchDataForCategory(category);
+ }
+
+ return appLaunchData;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+ return null;
+ }
+ InputGestureData gesture = mBookmarks.get(
+ InputGestureData.createKeyTrigger(keyCode, modifierState));
+ if (gesture == null) {
+ return null;
+ }
+ return gesture.getAction().appLaunchData();
+ }
+
+ /**
+ * Handle the shortcut from {@link KeyEvent}
+ *
+ * @param event Description of the key event.
+ */
+ public InterceptKeyResult interceptKey(KeyEvent event) {
+ if (event.getRepeatCount() != 0) {
+ return InterceptKeyResult.DO_NOTHING;
+ }
+
+ final int metaState = event.getModifiers();
+ final int keyCode = event.getKeyCode();
+ if (keyCode == KeyEvent.KEYCODE_SEARCH) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mSearchKeyShortcutPending = true;
+ mConsumeSearchKeyUp = false;
+ } else {
+ mSearchKeyShortcutPending = false;
+ if (mConsumeSearchKeyUp) {
+ mConsumeSearchKeyUp = false;
+ return InterceptKeyResult.CONSUME_KEY;
+ }
+ }
+ return InterceptKeyResult.DO_NOTHING;
+ }
+
+ if (event.getAction() != KeyEvent.ACTION_DOWN) {
+ return InterceptKeyResult.DO_NOTHING;
+ }
+
+ // Intercept shortcuts defined in bookmarks or through application launch keycodes
+ AppLaunchData appLaunchData = interceptShortcut(event);
+
+ // TODO(b/358569822): Ideally shortcut service custom shortcuts should be either
+ // migrated to bookmarks or customizable shortcut APIs.
+ if (appLaunchData == null && handleShortcutService(keyCode, metaState)) {
+ return InterceptKeyResult.CONSUME_KEY;
+ }
+
+ return new InterceptKeyResult(/* consumed =*/ false, appLaunchData);
+ }
+
+ /**
+ * @return a list of {@link InputGestureData} containing the application launch shortcuts parsed
+ * at boot time from {@code bookmarks.xml}.
+ */
+ public List<InputGestureData> getBookmarks() {
+ return new ArrayList<>(mBookmarks.values());
+ }
+
+ public void dump(IndentingPrintWriter ipw) {
+ ipw.println("AppLaunchShortcutManager:");
+ ipw.increaseIndent();
+ for (InputGestureData data : mBookmarks.values()) {
+ ipw.println(data);
+ }
+ ipw.decreaseIndent();
+ }
+
+ public record InterceptKeyResult(boolean consumed, @Nullable AppLaunchData appLaunchData) {
+ private static final InterceptKeyResult DO_NOTHING = new InterceptKeyResult(false, null);
+ private static final InterceptKeyResult CONSUME_KEY = new InterceptKeyResult(true, null);
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index f4bd402e63a2..6f3540221b63 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -16,11 +16,23 @@
package com.android.server.input;
+import static android.hardware.input.InputGestureData.createKeyTrigger;
+
+import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
+import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
+import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
+import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
+import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.hardware.input.InputGestureData;
import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
+import android.hardware.input.KeyGestureEvent;
+import android.os.SystemProperties;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -29,15 +41,17 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* A thread-safe component of {@link InputManagerService} responsible for managing pre-defined input
* gestures and custom gestures defined by other system components using Input APIs.
*
- * TODO(b/365064144): Add implementation to persist data, identify clashes with existing shortcuts.
+ * TODO(b/365064144): Add implementation to persist data.
*
*/
final class InputGestureManager {
@@ -47,13 +61,254 @@ final class InputGestureManager {
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON
| KeyEvent.META_META_ON;
- @GuardedBy("mCustomInputGestures")
+ private final Context mContext;
+
+ private static final Object mGestureLock = new Object();
+ @GuardedBy("mGestureLock")
private final SparseArray<Map<InputGestureData.Trigger, InputGestureData>>
mCustomInputGestures = new SparseArray<>();
+ @GuardedBy("mGestureLock")
+ private final Map<InputGestureData.Trigger, InputGestureData> mSystemShortcuts =
+ new HashMap<>();
+
+ @GuardedBy("mGestureLock")
+ private final Set<InputGestureData.Trigger> mBlockListedTriggers = new HashSet<>(Set.of(
+ createKeyTrigger(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_Z,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_X, KeyEvent.META_CTRL_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON),
+ createKeyTrigger(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON)
+ ));
+
+ public InputGestureManager(Context context) {
+ mContext = context;
+ }
+
+ public void systemRunning() {
+ initSystemShortcuts();
+ blockListBookmarkedTriggers();
+ }
+
+ private void initSystemShortcuts() {
+ // Initialize all system shortcuts
+ List<InputGestureData> systemShortcuts = new ArrayList<>(List.of(
+ createKeyGesture(
+ KeyEvent.KEYCODE_A,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_H,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_ENTER,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_I,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_L,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_N,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_N,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_S,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DEL,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_ESCAPE,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_UP,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_SLASH,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
+ ),
+ createKeyGesture(
+ KeyEvent.KEYCODE_TAB,
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS
+ )
+ ));
+ if (newBugreportKeyboardShortcut() && "1".equals(SystemProperties.get("ro.debuggable"))) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_DEL,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT
+ ));
+ }
+ if (enableMoveToNextDisplayShortcut()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_D,
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
+ ));
+ }
+ if (enableTalkbackAndMagnifierKeyGestures()) {
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_T,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_MINUS,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_S,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK));
+ }
+ if (keyboardA11yShortcutControl()) {
+ if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_3,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS
+ ));
+ }
+ if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_4,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS
+ ));
+ }
+ if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_5,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS
+ ));
+ }
+ if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_6,
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS
+ ));
+ }
+ if (enableTaskResizingKeyboardShortcuts()) {
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_LEFT_BRACKET,
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW
+ ));
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_RIGHT_BRACKET,
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW
+ ));
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_EQUALS,
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW
+ ));
+ systemShortcuts.add(createKeyGesture(
+ KeyEvent.KEYCODE_MINUS,
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE
+ ));
+ }
+ }
+ synchronized (mGestureLock) {
+ for (InputGestureData systemShortcut : systemShortcuts) {
+ mSystemShortcuts.put(systemShortcut.getTrigger(), systemShortcut);
+ }
+ }
+ }
+
+ private void blockListBookmarkedTriggers() {
+ synchronized (mGestureLock) {
+ InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
+ for (InputGestureData bookmark : im.getAppLaunchBookmarks()) {
+ mBlockListedTriggers.add(bookmark.getTrigger());
+ }
+ }
+ }
+
@InputManager.CustomInputGestureResult
public int addCustomInputGesture(int userId, InputGestureData newGesture) {
- synchronized (mCustomInputGestures) {
+ synchronized (mGestureLock) {
+ if (mBlockListedTriggers.contains(newGesture.getTrigger())
+ || mSystemShortcuts.containsKey(newGesture.getTrigger())) {
+ return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE;
+ }
+ if (newGesture.getTrigger() instanceof InputGestureData.KeyTrigger keyTrigger) {
+ if (KeyEvent.isModifierKey(keyTrigger.getKeycode()) ||
+ KeyEvent.isSystemKey(keyTrigger.getKeycode())) {
+ return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE;
+ }
+ }
if (!mCustomInputGestures.contains(userId)) {
mCustomInputGestures.put(userId, new HashMap<>());
}
@@ -69,7 +324,7 @@ final class InputGestureManager {
@InputManager.CustomInputGestureResult
public int removeCustomInputGesture(int userId, InputGestureData data) {
- synchronized (mCustomInputGestures) {
+ synchronized (mGestureLock) {
if (!mCustomInputGestures.contains(userId)) {
return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST;
}
@@ -80,26 +335,50 @@ final class InputGestureManager {
return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST;
}
customGestures.remove(data.getTrigger());
- if (customGestures.size() == 0) {
+ if (customGestures.isEmpty()) {
mCustomInputGestures.remove(userId);
}
return InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS;
}
}
- public void removeAllCustomInputGestures(int userId) {
- synchronized (mCustomInputGestures) {
- mCustomInputGestures.remove(userId);
+ public void removeAllCustomInputGestures(int userId, @Nullable InputGestureData.Filter filter) {
+ synchronized (mGestureLock) {
+ Map<InputGestureData.Trigger, InputGestureData> customGestures =
+ mCustomInputGestures.get(userId);
+ if (customGestures == null) {
+ return;
+ }
+ if (filter == null) {
+ mCustomInputGestures.remove(userId);
+ return;
+ }
+ customGestures.entrySet().removeIf(entry -> filter.matches(entry.getValue()));
+ if (customGestures.isEmpty()) {
+ mCustomInputGestures.remove(userId);
+ }
}
}
@NonNull
- public List<InputGestureData> getCustomInputGestures(int userId) {
- synchronized (mCustomInputGestures) {
+ public List<InputGestureData> getCustomInputGestures(int userId,
+ @Nullable InputGestureData.Filter filter) {
+ synchronized (mGestureLock) {
if (!mCustomInputGestures.contains(userId)) {
return List.of();
}
- return new ArrayList<>(mCustomInputGestures.get(userId).values());
+ Map<InputGestureData.Trigger, InputGestureData> customGestures =
+ mCustomInputGestures.get(userId);
+ if (filter == null) {
+ return new ArrayList<>(customGestures.values());
+ }
+ List<InputGestureData> result = new ArrayList<>();
+ for (InputGestureData customGesture : customGestures.values()) {
+ if (filter.matches(customGesture)) {
+ result.add(customGesture);
+ }
+ }
+ return result;
}
}
@@ -109,7 +388,7 @@ final class InputGestureManager {
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
return null;
}
- synchronized (mCustomInputGestures) {
+ synchronized (mGestureLock) {
Map<InputGestureData.Trigger, InputGestureData> customGestures =
mCustomInputGestures.get(userId);
if (customGestures == null) {
@@ -120,10 +399,60 @@ final class InputGestureManager {
}
}
+ @Nullable
+ public InputGestureData getCustomGestureForTouchpadGesture(@UserIdInt int userId,
+ int touchpadGestureType) {
+ if (touchpadGestureType == InputGestureData.TOUCHPAD_GESTURE_TYPE_UNKNOWN) {
+ return null;
+ }
+ synchronized (mGestureLock) {
+ Map<InputGestureData.Trigger, InputGestureData> customGestures =
+ mCustomInputGestures.get(userId);
+ if (customGestures == null) {
+ return null;
+ }
+ return customGestures.get(InputGestureData.createTouchpadTrigger(touchpadGestureType));
+ }
+ }
+
+ @Nullable
+ public InputGestureData getSystemShortcutForKeyEvent(KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+ return null;
+ }
+ synchronized (mGestureLock) {
+ int modifierState = event.getMetaState() & KEY_GESTURE_META_MASK;
+ return mSystemShortcuts.get(InputGestureData.createKeyTrigger(keyCode, modifierState));
+ }
+ }
+
+ private static InputGestureData createKeyGesture(int keycode, int modifierState,
+ int keyGestureType) {
+ return new InputGestureData.Builder()
+ .setTrigger(createKeyTrigger(keycode, modifierState))
+ .setKeyGestureType(keyGestureType)
+ .build();
+ }
+
public void dump(IndentingPrintWriter ipw) {
ipw.println("InputGestureManager:");
ipw.increaseIndent();
- synchronized (mCustomInputGestures) {
+ synchronized (mGestureLock) {
+ ipw.println("System Shortcuts:");
+ ipw.increaseIndent();
+ for (InputGestureData systemShortcut : mSystemShortcuts.values()) {
+ ipw.println(systemShortcut);
+ }
+ ipw.decreaseIndent();
+ ipw.println("Blocklisted Triggers:");
+ ipw.increaseIndent();
+ for (InputGestureData.Trigger blocklistedTrigger : mBlockListedTriggers) {
+ ipw.println(blocklistedTrigger);
+ }
+ ipw.decreaseIndent();
+ ipw.println("Custom Gestures:");
+ ipw.increaseIndent();
int size = mCustomInputGestures.size();
for (int i = 0; i < size; i++) {
Map<InputGestureData.Trigger, InputGestureData> customGestures =
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 1c5bd59fa386..265e4531a10a 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -23,11 +23,13 @@ import android.graphics.PointF;
import android.hardware.display.DisplayViewport;
import android.hardware.input.KeyGestureEvent;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.SparseBooleanArray;
import android.view.InputChannel;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
+import com.android.internal.policy.IShortcutService;
import java.util.List;
@@ -278,6 +280,15 @@ public abstract class InputManagerInternal {
*/
public abstract void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor);
+
+ /**
+ * Register shortcuts for input manager to dispatch.
+ * Shortcut code is packed as (metaState << Integer.SIZE) | keyCode
+ * @hide
+ */
+ public abstract void registerShortcutKey(long shortcutCode,
+ IShortcutService shortcutKeyReceiver) throws RemoteException;
+
/**
* Set whether the given input device can wake up the kernel from sleep
* when it generates input events. By default, usually only internal (built-in)
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 26929f5999eb..f4dd71706761 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -130,6 +130,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -2313,6 +2314,13 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
@SuppressWarnings("unused")
+ private void notifyTouchpadThreeFingerTap() {
+ mKeyGestureController.handleTouchpadGesture(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP);
+ }
+
+ // Native callback.
+ @SuppressWarnings("unused")
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
if (DEBUG) {
Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
@@ -2989,35 +2997,41 @@ public class InputManagerService extends IInputManager.Stub
@Override
@PermissionManuallyEnforced
- public int addCustomInputGesture(@NonNull AidlInputGestureData inputGestureData) {
+ public int addCustomInputGesture(@UserIdInt int userId,
+ @NonNull AidlInputGestureData inputGestureData) {
enforceManageKeyGesturePermission();
Objects.requireNonNull(inputGestureData);
- return mKeyGestureController.addCustomInputGesture(UserHandle.getCallingUserId(),
- inputGestureData);
+ return mKeyGestureController.addCustomInputGesture(userId, inputGestureData);
}
@Override
@PermissionManuallyEnforced
- public int removeCustomInputGesture(@NonNull AidlInputGestureData inputGestureData) {
+ public int removeCustomInputGesture(@UserIdInt int userId,
+ @NonNull AidlInputGestureData inputGestureData) {
enforceManageKeyGesturePermission();
Objects.requireNonNull(inputGestureData);
- return mKeyGestureController.removeCustomInputGesture(UserHandle.getCallingUserId(),
- inputGestureData);
+ return mKeyGestureController.removeCustomInputGesture(userId, inputGestureData);
}
@Override
@PermissionManuallyEnforced
- public void removeAllCustomInputGestures() {
+ public void removeAllCustomInputGestures(@UserIdInt int userId, int tag) {
enforceManageKeyGesturePermission();
- mKeyGestureController.removeAllCustomInputGestures(UserHandle.getCallingUserId());
+ mKeyGestureController.removeAllCustomInputGestures(userId, InputGestureData.Filter.of(tag));
+ }
+
+ @Override
+ public AidlInputGestureData[] getCustomInputGestures(@UserIdInt int userId, int tag) {
+ return mKeyGestureController.getCustomInputGestures(userId,
+ InputGestureData.Filter.of(tag));
}
@Override
- public AidlInputGestureData[] getCustomInputGestures() {
- return mKeyGestureController.getCustomInputGestures(UserHandle.getCallingUserId());
+ public AidlInputGestureData[] getAppLaunchBookmarks() {
+ return mKeyGestureController.getAppLaunchBookmarks();
}
private void handleCurrentUserChanged(@UserIdInt int userId) {
@@ -3564,6 +3578,12 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
+ public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
+ throws RemoteException {
+ mKeyGestureController.registerShortcutKey(shortcutCode, shortcutKeyReceiver);
+ }
+
+ @Override
public boolean setKernelWakeEnabled(int deviceId, boolean enabled) {
return mNative.setKernelWakeEnabled(deviceId, enabled);
}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index d1a6d3b9bb00..420db9041dc2 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -143,6 +143,10 @@ class InputSettingsObserver extends ContentObserver {
observer.accept("just booted");
}
+ // TODO(b/365063048): add an entry to mObservers that calls this instead, once we have a
+ // setting that can be observed.
+ updateTouchpadThreeFingerTapShortcutEnabled();
+
configureUserActivityPokeInterval();
}
@@ -205,6 +209,11 @@ class InputSettingsObserver extends ContentObserver {
mNative.setTouchpadRightClickZoneEnabled(InputSettings.useTouchpadRightClickZone(mContext));
}
+ private void updateTouchpadThreeFingerTapShortcutEnabled() {
+ mNative.setTouchpadThreeFingerTapShortcutEnabled(
+ InputSettings.useTouchpadThreeFingerTapShortcut(mContext));
+ }
+
private void updateShowTouches() {
mNative.setShowTouches(getBoolean(Settings.System.SHOW_TOUCHES, false));
}
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index ebeef651c19b..bb0b19009962 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -20,12 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE;
-import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
-import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
-import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
-import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
-import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
-import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts;
+import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import android.annotation.BinderThread;
import android.annotation.MainThread;
@@ -54,7 +49,6 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
@@ -69,6 +63,7 @@ import android.view.KeyEvent;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.IShortcutService;
import com.android.server.policy.KeyCombinationManager;
import java.util.ArrayDeque;
@@ -119,7 +114,8 @@ final class KeyGestureController {
private final int mSystemPid;
private final KeyCombinationManager mKeyCombinationManager;
private final SettingsObserver mSettingsObserver;
- private final InputGestureManager mInputGestureManager = new InputGestureManager();
+ private final AppLaunchShortcutManager mAppLaunchShortcutManager;
+ private final InputGestureManager mInputGestureManager;
private static final Object mUserLock = new Object();
@UserIdInt
@GuardedBy("mUserLock")
@@ -131,7 +127,6 @@ final class KeyGestureController {
private boolean mPendingHideRecentSwitcher;
// Platform behaviors
- private boolean mEnableBugReportKeyboardShortcut;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -178,13 +173,13 @@ final class KeyGestureController {
});
mKeyCombinationManager = new KeyCombinationManager(mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
+ mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
+ mInputGestureManager = new InputGestureManager(mContext);
initBehaviors();
initKeyCombinationRules();
}
private void initBehaviors() {
- mEnableBugReportKeyboardShortcut = "1".equals(SystemProperties.get("ro.debuggable"));
-
PackageManager pm = mContext.getPackageManager();
mHasFeatureWatch = pm.hasSystemFeature(FEATURE_WATCH);
mHasFeatureLeanback = pm.hasSystemFeature(FEATURE_LEANBACK);
@@ -217,7 +212,7 @@ final class KeyGestureController {
}
private void initKeyCombinationRules() {
- if (!useKeyGestureEventHandler() || !useKeyGestureEventHandlerMultiPressGestures()) {
+ if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
return;
}
// TODO(b/358569822): Handle Power, Back key properly since key combination gesture is
@@ -437,11 +432,14 @@ final class KeyGestureController {
public void systemRunning() {
mSettingsObserver.observe();
+ mAppLaunchShortcutManager.systemRunning();
+ mInputGestureManager.systemRunning();
}
public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
- if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()
+ && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
return mKeyCombinationManager.interceptKey(event, interactive);
}
return false;
@@ -457,15 +455,18 @@ final class KeyGestureController {
final long keyConsumed = -1;
final long keyNotConsumed = 0;
- if (mKeyCombinationManager.isKeyConsumed(event)) {
- return keyConsumed;
- }
+ if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
+ if (mKeyCombinationManager.isKeyConsumed(event)) {
+ return keyConsumed;
+ }
- if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
- final long now = SystemClock.uptimeMillis();
- final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode);
- if (now < interceptTimeout) {
- return interceptTimeout - now;
+ if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
+ final long now = SystemClock.uptimeMillis();
+ final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(
+ keyCode);
+ if (now < interceptTimeout) {
+ return interceptTimeout - now;
+ }
}
}
@@ -513,15 +514,34 @@ final class KeyGestureController {
mPendingCapsLockToggle = false;
}
+ // Handle App launch shortcuts
+ AppLaunchShortcutManager.InterceptKeyResult result = mAppLaunchShortcutManager.interceptKey(
+ event);
+ if (result.consumed()) {
+ return true;
+ }
+ if (result.appLaunchData() != null) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0, result.appLaunchData());
+ }
+
+ // Handle system shortcuts
+ if (firstDown) {
+ InputGestureData systemShortcut = mInputGestureManager.getSystemShortcutForKeyEvent(
+ event);
+ if (systemShortcut != null) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+ systemShortcut.getAction().keyGestureType(),
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ displayId, focusedToken, /* flags = */0,
+ systemShortcut.getAction().appLaunchData());
+ }
+ }
+
+ // Handle system keys
switch (keyCode) {
- case KeyEvent.KEYCODE_A:
- if (firstDown && event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
case KeyEvent.KEYCODE_RECENT_APPS:
if (firstDown) {
handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
@@ -544,257 +564,6 @@ final class KeyGestureController {
/* appLaunchData = */null);
}
return true;
- case KeyEvent.KEYCODE_H:
- case KeyEvent.KEYCODE_ENTER:
- if (firstDown && event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_I:
- if (firstDown && event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_L:
- if (firstDown && event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_N:
- if (firstDown && event.isMetaPressed()) {
- if (event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- } else {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_S:
- if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_T:
- if (keyboardA11yShortcutControl()) {
- if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_3:
- if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
- && keyboardA11yShortcutControl()) {
- if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_4:
- if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
- && keyboardA11yShortcutControl()) {
- if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_5:
- if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
- && keyboardA11yShortcutControl()) {
- if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_6:
- if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
- && keyboardA11yShortcutControl()) {
- if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_DEL:
- if (newBugreportKeyboardShortcut()) {
- if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed()
- && event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- // fall through
- case KeyEvent.KEYCODE_ESCAPE:
- if (firstDown && event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (firstDown && event.isMetaPressed()) {
- if (event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- } else if (event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- } else {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (firstDown && event.isMetaPressed()) {
- if (event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- } else if (event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_D:
- if (enableMoveToNextDisplayShortcut()) {
- if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_LEFT_BRACKET:
- if (enableTaskResizingKeyboardShortcuts()) {
- if (firstDown && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_RIGHT_BRACKET:
- if (enableTaskResizingKeyboardShortcuts()) {
- if (firstDown && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_EQUALS:
- if (enableTaskResizingKeyboardShortcuts()) {
- if (firstDown && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_MINUS:
- if (enableTaskResizingKeyboardShortcuts()) {
- if (firstDown && event.isAltPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
- KeyEvent.META_ALT_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- }
- break;
- case KeyEvent.KEYCODE_SLASH:
- if (firstDown && event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- }
- break;
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
if (down) {
@@ -938,12 +707,7 @@ final class KeyGestureController {
return true;
case KeyEvent.KEYCODE_TAB:
if (firstDown) {
- if (event.isMetaPressed()) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, /* appLaunchData = */null);
- } else if (!mPendingHideRecentSwitcher) {
+ if (!mPendingHideRecentSwitcher) {
final int shiftlessModifiers =
event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
if (KeyEvent.metaStateHasModifiers(
@@ -988,6 +752,28 @@ final class KeyGestureController {
}
}
break;
+ case KeyEvent.KEYCODE_LOCK:
+ if (enableNew25q2Keycodes()) {
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_LOCK},
+ /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken,
+ /* flags = */0, /* appLaunchData = */null);
+ }
+ }
+ return true;
+ case KeyEvent.KEYCODE_FULLSCREEN:
+ if (enableNew25q2Keycodes()) {
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_FULLSCREEN},
+ /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken,
+ /* flags = */0, /* appLaunchData = */null);
+ }
+ }
+ return true;
case KeyEvent.KEYCODE_ASSIST:
Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
return true;
@@ -1002,8 +788,12 @@ final class KeyGestureController {
Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
+ " interceptKeyBeforeQueueing");
return true;
+ case KeyEvent.KEYCODE_DO_NOT_DISTURB:
+ // TODO(b/365920375): Implement 25Q2 keycode implementation in system
+ return true;
}
+ // Handle custom shortcuts
if (firstDown) {
InputGestureData customGesture;
synchronized (mUserLock) {
@@ -1084,6 +874,13 @@ final class KeyGestureController {
/* appLaunchData = */null);
}
+ private void handleTouchpadGesture(@KeyGestureEvent.KeyGestureType int keyGestureType,
+ @Nullable AppLaunchData appLaunchData) {
+ handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, new int[0], /* modifierState= */0,
+ keyGestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ Display.DEFAULT_DISPLAY, /* focusedToken = */null, /* flags = */0, appLaunchData);
+ }
+
@VisibleForTesting
boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
@KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
@@ -1134,6 +931,20 @@ final class KeyGestureController {
handleKeyGesture(event, null /*focusedToken*/);
}
+ public void handleTouchpadGesture(int touchpadGestureType) {
+ // Handle custom shortcuts
+ InputGestureData customGesture;
+ synchronized (mUserLock) {
+ customGesture = mInputGestureManager.getCustomGestureForTouchpadGesture(mCurrentUserId,
+ touchpadGestureType);
+ }
+ if (customGesture == null) {
+ return;
+ }
+ handleTouchpadGesture(customGesture.getAction().keyGestureType(),
+ customGesture.getAction().appLaunchData());
+ }
+
@MainThread
public void setCurrentUserId(@UserIdInt int userId) {
synchronized (mUserLock) {
@@ -1237,13 +1048,16 @@ final class KeyGestureController {
}
@BinderThread
- public void removeAllCustomInputGestures(@UserIdInt int userId) {
- mInputGestureManager.removeAllCustomInputGestures(userId);
+ public void removeAllCustomInputGestures(@UserIdInt int userId,
+ @Nullable InputGestureData.Filter filter) {
+ mInputGestureManager.removeAllCustomInputGestures(userId, filter);
}
@BinderThread
- public AidlInputGestureData[] getCustomInputGestures(@UserIdInt int userId) {
- List<InputGestureData> customGestures = mInputGestureManager.getCustomInputGestures(userId);
+ public AidlInputGestureData[] getCustomInputGestures(@UserIdInt int userId,
+ @Nullable InputGestureData.Filter filter) {
+ List<InputGestureData> customGestures = mInputGestureManager.getCustomInputGestures(userId,
+ filter);
AidlInputGestureData[] result = new AidlInputGestureData[customGestures.size()];
for (int i = 0; i < customGestures.size(); i++) {
result[i] = customGestures.get(i).getAidlData();
@@ -1251,6 +1065,16 @@ final class KeyGestureController {
return result;
}
+ @BinderThread
+ public AidlInputGestureData[] getAppLaunchBookmarks() {
+ List<InputGestureData> bookmarks = mAppLaunchShortcutManager.getBookmarks();
+ AidlInputGestureData[] result = new AidlInputGestureData[bookmarks.size()];
+ for (int i = 0; i < bookmarks.size(); i++) {
+ result[i] = bookmarks.get(i).getAidlData();
+ }
+ return result;
+ }
+
private void onKeyGestureEventListenerDied(int pid) {
synchronized (mKeyGestureEventListenerRecords) {
mKeyGestureEventListenerRecords.remove(pid);
@@ -1322,6 +1146,15 @@ final class KeyGestureController {
}
}
+ public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
+ throws RemoteException {
+ mAppLaunchShortcutManager.registerShortcutKey(shortcutCode, shortcutKeyReceiver);
+ }
+
+ public List<InputGestureData> getBookmarks() {
+ return mAppLaunchShortcutManager.getBookmarks();
+ }
+
private void onKeyGestureHandlerDied(int pid) {
synchronized (mKeyGestureHandlerRecords) {
mKeyGestureHandlerRecords.remove(pid);
@@ -1425,6 +1258,7 @@ final class KeyGestureController {
public void dump(IndentingPrintWriter ipw) {
ipw.println("KeyGestureController:");
ipw.increaseIndent();
+ ipw.println("mCurrentUserId = " + mCurrentUserId);
ipw.println("mSystemPid = " + mSystemPid);
ipw.println("mPendingMetaAction = " + mPendingMetaAction);
ipw.println("mPendingCapsLockToggle = " + mPendingCapsLockToggle);
@@ -1464,6 +1298,7 @@ final class KeyGestureController {
}
ipw.decreaseIndent();
mKeyCombinationManager.dump("", ipw);
+ mAppLaunchShortcutManager.dump(ipw);
mInputGestureManager.dump(ipw);
}
}
diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java
index c3205afe14f2..0defd27eaae2 100644
--- a/services/core/java/com/android/server/input/KeyboardBacklightController.java
+++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java
@@ -20,6 +20,7 @@ import android.animation.ValueAnimator;
import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.hardware.input.IKeyboardBacklightListener;
import android.hardware.input.IKeyboardBacklightState;
@@ -81,9 +82,6 @@ final class KeyboardBacklightController implements
private static final String UEVENT_KEYBOARD_BACKLIGHT_TAG = "kbd_backlight";
@VisibleForTesting
- static final long USER_INACTIVITY_THRESHOLD_MILLIS = Duration.ofSeconds(30).toMillis();
-
- @VisibleForTesting
static final int[] DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL =
new int[DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS + 1];
@@ -112,6 +110,7 @@ final class KeyboardBacklightController implements
private AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener mAmbientListener;
private int mAmbientBacklightValue = 0;
+ private final int mUserInactivityThresholdMs;
static {
// Fixed brightness levels to avoid issues when converting back and forth from the
@@ -139,6 +138,9 @@ final class KeyboardBacklightController implements
mAnimatorFactory = animatorFactory;
mAmbientController = new AmbientKeyboardBacklightController(context, looper);
mUEventManager = uEventManager;
+ Resources res = mContext.getResources();
+ mUserInactivityThresholdMs = res.getInteger(
+ com.android.internal.R.integer.config_keyboardBacklightTimeoutMs);
}
@Override
@@ -300,7 +302,7 @@ final class KeyboardBacklightController implements
}
mHandler.removeMessages(MSG_NOTIFY_USER_INACTIVITY);
mHandler.sendEmptyMessageAtTime(MSG_NOTIFY_USER_INACTIVITY,
- SystemClock.uptimeMillis() + USER_INACTIVITY_THRESHOLD_MILLIS);
+ SystemClock.uptimeMillis() + mUserInactivityThresholdMs);
}
private void handleUserInactivity() {
diff --git a/services/core/java/com/android/server/input/KeyboardLedController.java b/services/core/java/com/android/server/input/KeyboardLedController.java
index 5c404a2ae6e7..a2940d54fe4d 100644
--- a/services/core/java/com/android/server/input/KeyboardLedController.java
+++ b/services/core/java/com/android/server/input/KeyboardLedController.java
@@ -46,11 +46,13 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
private static final String TAG = KeyboardLedController.class.getSimpleName();
private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
private static final int MSG_UPDATE_MIC_MUTE_LED_STATE = 2;
+ private static final int MSG_UPDATE_AUDIO_MUTE_LED_STATE = 3;
private final Context mContext;
private final Handler mHandler;
private final NativeInputManagerService mNative;
private final SparseArray<InputDevice> mKeyboardsWithMicMuteLed = new SparseArray<>();
+ private final SparseArray<InputDevice> mKeyboardsWithVolumeMuteLed = new SparseArray<>();
@NonNull
private InputManager mInputManager;
@NonNull
@@ -66,6 +68,17 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
}
};
+ private BroadcastReceiver mVolumeMuteIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+ if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ Message msg = Message.obtain(mHandler, MSG_UPDATE_AUDIO_MUTE_LED_STATE);
+ mHandler.sendMessage(msg);
+ }
+ }
+ };
+
KeyboardLedController(Context context, Looper looper,
NativeInputManagerService nativeService) {
mContext = context;
@@ -83,6 +96,9 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
case MSG_UPDATE_MIC_MUTE_LED_STATE:
updateMicMuteLedState();
return true;
+ case MSG_UPDATE_AUDIO_MUTE_LED_STATE:
+ updateVolumeMuteLedState();
+ return true;
}
return false;
}
@@ -105,6 +121,21 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
}
}
+ private void updateVolumeMuteLedState() {
+ int color = mAudioManager.isStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE)
+ ? Color.WHITE : Color.TRANSPARENT;
+ for (int i = 0; i < mKeyboardsWithVolumeMuteLed.size(); i++) {
+ InputDevice device = mKeyboardsWithVolumeMuteLed.valueAt(i);
+ if (device != null) {
+ int deviceId = device.getId();
+ Light light = getKeyboardVolumeMuteLight(device);
+ if (light != null) {
+ mNative.setLightColor(deviceId, light.getId(), color);
+ }
+ }
+ }
+ }
+
private Light getKeyboardMicMuteLight(InputDevice device) {
for (Light light : device.getLightsManager().getLights()) {
if (light.getType() == Light.LIGHT_TYPE_KEYBOARD_MIC_MUTE
@@ -115,6 +146,16 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
return null;
}
+ private Light getKeyboardVolumeMuteLight(InputDevice device) {
+ for (Light light : device.getLightsManager().getLights()) {
+ if (light.getType() == Light.LIGHT_TYPE_KEYBOARD_VOLUME_MUTE
+ && light.hasBrightnessControl()) {
+ return light;
+ }
+ }
+ return null;
+ }
+
/** Called when the system is ready for us to start third-party code. */
public void systemRunning() {
mSensorPrivacyManager = Objects.requireNonNull(
@@ -131,6 +172,12 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED),
null,
mHandler);
+ mContext.registerReceiverAsUser(
+ mVolumeMuteIntentReceiver,
+ UserHandle.ALL,
+ new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION),
+ null,
+ mHandler);
}
@Override
@@ -141,6 +188,7 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
@Override
public void onInputDeviceRemoved(int deviceId) {
mKeyboardsWithMicMuteLed.remove(deviceId);
+ mKeyboardsWithVolumeMuteLed.remove(deviceId);
}
@Override
@@ -154,6 +202,11 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
Message msg = Message.obtain(mHandler, MSG_UPDATE_MIC_MUTE_LED_STATE);
mHandler.sendMessage(msg);
}
+ if (getKeyboardVolumeMuteLight(inputDevice) != null) {
+ mKeyboardsWithVolumeMuteLed.put(deviceId, inputDevice);
+ Message msg = Message.obtain(mHandler, MSG_UPDATE_AUDIO_MUTE_LED_STATE);
+ mHandler.sendMessage(msg);
+ }
}
/** Dump the diagnostic information */
@@ -167,5 +220,14 @@ public final class KeyboardLedController implements InputManager.InputDeviceList
+ getKeyboardMicMuteLight(inputDevice).toString());
}
ipw.decreaseIndent();
+ ipw.println(TAG + ": " + mKeyboardsWithVolumeMuteLed.size()
+ + " keyboard volume mute lights");
+ ipw.increaseIndent();
+ for (int i = 0; i < mKeyboardsWithVolumeMuteLed.size(); i++) {
+ InputDevice inputDevice = mKeyboardsWithVolumeMuteLed.valueAt(i);
+ ipw.println(i + " " + inputDevice.getName() + ": "
+ + getKeyboardVolumeMuteLight(inputDevice).toString());
+ }
+ ipw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 283fdea92b63..8903c27b491a 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -143,6 +143,8 @@ interface NativeInputManagerService {
void setTouchpadRightClickZoneEnabled(boolean enabled);
+ void setTouchpadThreeFingerTapShortcutEnabled(boolean enabled);
+
void setShowTouches(boolean enabled);
void setNonInteractiveDisplays(int[] displayIds);
@@ -427,6 +429,9 @@ interface NativeInputManagerService {
public native void setTouchpadRightClickZoneEnabled(boolean enabled);
@Override
+ public native void setTouchpadThreeFingerTapShortcutEnabled(boolean enabled);
+
+ @Override
public native void setShowTouches(boolean enabled);
@Override
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index 146ce1732070..46be1ca0eec2 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -27,6 +27,7 @@ import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import android.view.inputmethod.Flags;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -59,8 +60,14 @@ final class AdditionalSubtypeUtils {
private static final String NODE_SUBTYPE = "subtype";
private static final String NODE_IMI = "imi";
private static final String ATTR_ID = "id";
+ /** The resource ID of the subtype name. */
private static final String ATTR_LABEL = "label";
+ /** The untranslatable name of the subtype. */
private static final String ATTR_NAME_OVERRIDE = "nameOverride";
+ /** The layout label string resource identifier. */
+ private static final String ATTR_LAYOUT_LABEL = "layoutLabel";
+ /** The non-localized layout label. */
+ private static final String ATTR_LAYOUT_LABEL_NON_LOCALIZED = "layoutLabelNonLocalized";
private static final String ATTR_NAME_PK_LANGUAGE_TAG = "pkLanguageTag";
private static final String ATTR_NAME_PK_LAYOUT_TYPE = "pkLayoutType";
private static final String ATTR_ICON = "icon";
@@ -173,6 +180,11 @@ final class AdditionalSubtypeUtils {
out.attributeInt(null, ATTR_ICON, subtype.getIconResId());
out.attributeInt(null, ATTR_LABEL, subtype.getNameResId());
out.attribute(null, ATTR_NAME_OVERRIDE, subtype.getNameOverride().toString());
+ if (Flags.imeSwitcherRevampApi()) {
+ out.attributeInt(null, ATTR_LAYOUT_LABEL, subtype.getLayoutLabelResource());
+ out.attribute(null, ATTR_LAYOUT_LABEL_NON_LOCALIZED,
+ subtype.getLayoutLabelNonLocalized().toString());
+ }
ULocale pkLanguageTag = subtype.getPhysicalKeyboardHintLanguageTag();
if (pkLanguageTag != null) {
out.attribute(null, ATTR_NAME_PK_LANGUAGE_TAG,
@@ -264,6 +276,16 @@ final class AdditionalSubtypeUtils {
final int label = parser.getAttributeInt(null, ATTR_LABEL);
final String untranslatableName = parser.getAttributeValue(null,
ATTR_NAME_OVERRIDE);
+ final int layoutLabelResource;
+ final String layoutLabelNonLocalized;
+ if (Flags.imeSwitcherRevampApi()) {
+ layoutLabelResource = parser.getAttributeInt(null, ATTR_LAYOUT_LABEL);
+ layoutLabelNonLocalized = parser.getAttributeValue(null,
+ ATTR_LAYOUT_LABEL_NON_LOCALIZED);
+ } else {
+ layoutLabelResource = 0;
+ layoutLabelNonLocalized = null;
+ }
final String pkLanguageTag = parser.getAttributeValue(null,
ATTR_NAME_PK_LANGUAGE_TAG);
final String pkLayoutType = parser.getAttributeValue(null,
@@ -283,6 +305,7 @@ final class AdditionalSubtypeUtils {
final InputMethodSubtype.InputMethodSubtypeBuilder
builder = new InputMethodSubtype.InputMethodSubtypeBuilder()
.setSubtypeNameResId(label)
+ .setLayoutLabelResource(layoutLabelResource)
.setPhysicalKeyboardHint(
pkLanguageTag == null ? null : new ULocale(pkLanguageTag),
pkLayoutType == null ? "" : pkLayoutType)
@@ -301,6 +324,9 @@ final class AdditionalSubtypeUtils {
if (untranslatableName != null) {
builder.setSubtypeNameOverride(untranslatableName);
}
+ if (layoutLabelNonLocalized != null) {
+ builder.setLayoutLabelNonLocalized(layoutLabelNonLocalized);
+ }
tempSubtypesArray.add(builder.build());
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 010437337ba1..d8483f721306 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4969,7 +4969,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
final var userData = getUserData(userId);
if (Flags.refactorInsetsController()) {
setImeVisibilityOnFocusedWindowClient(false, userData,
- null /* TODO(b329229469) check statsToken */);
+ null /* TODO(b/353463205) check statsToken */);
} else {
hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
index 6abd5aabfabf..9f94905686fe 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
@@ -238,8 +238,8 @@ final class InputMethodMenuControllerNew {
prevImeId = imeId;
}
- menuItems.add(new SubtypeItem(item.mImeName, item.mSubtypeName, item.mImi,
- item.mSubtypeIndex));
+ menuItems.add(new SubtypeItem(item.mImeName, item.mSubtypeName, item.mLayoutName,
+ item.mImi, item.mSubtypeIndex));
}
return menuItems;
@@ -348,6 +348,13 @@ final class InputMethodMenuControllerNew {
@Nullable
final CharSequence mSubtypeName;
+ /**
+ * The name of the subtype's layout, or {@code null} if this item doesn't have a subtype,
+ * or doesn't specify a layout.
+ */
+ @Nullable
+ private final CharSequence mLayoutName;
+
/** The info of the input method. */
@NonNull
final InputMethodInfo mImi;
@@ -360,10 +367,11 @@ final class InputMethodMenuControllerNew {
final int mSubtypeIndex;
SubtypeItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
- @NonNull InputMethodInfo imi,
+ @Nullable CharSequence layoutName, @NonNull InputMethodInfo imi,
@IntRange(from = NOT_A_SUBTYPE_INDEX) int subtypeIndex) {
mImeName = imeName;
mSubtypeName = subtypeName;
+ mLayoutName = layoutName;
mImi = imi;
mSubtypeIndex = subtypeIndex;
}
@@ -521,6 +529,9 @@ final class InputMethodMenuControllerNew {
/** The name of the item. */
@NonNull
private final TextView mName;
+ /** The layout name. */
+ @NonNull
+ private final TextView mLayout;
/** Indicator for the selected status of the item. */
@NonNull
private final ImageView mCheckmark;
@@ -536,6 +547,7 @@ final class InputMethodMenuControllerNew {
mContainer = itemView;
mName = itemView.requireViewById(com.android.internal.R.id.text);
+ mLayout = itemView.requireViewById(com.android.internal.R.id.text2);
mCheckmark = itemView.requireViewById(com.android.internal.R.id.image);
mContainer.setOnClickListener((v) -> {
@@ -563,6 +575,9 @@ final class InputMethodMenuControllerNew {
// Trigger the ellipsize marquee behaviour by selecting the name.
mName.setSelected(isSelected);
mName.setText(name);
+ mLayout.setText(item.mLayoutName);
+ mLayout.setVisibility(
+ !TextUtils.isEmpty(item.mLayoutName) ? View.VISIBLE : View.GONE);
mCheckmark.setVisibility(isSelected ? View.VISIBLE : View.GONE);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 96b3e084d102..51b85e90c447 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -85,6 +85,12 @@ final class InputMethodSubtypeSwitchingController {
public final CharSequence mImeName;
@Nullable
public final CharSequence mSubtypeName;
+ /**
+ * The subtype's layout name, or {@code null} if this item doesn't have a subtype,
+ * or doesn't specify a layout.
+ */
+ @Nullable
+ public final CharSequence mLayoutName;
@NonNull
public final InputMethodInfo mImi;
/**
@@ -96,10 +102,11 @@ final class InputMethodSubtypeSwitchingController {
public final boolean mIsSystemLanguage;
ImeSubtypeListItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
- @NonNull InputMethodInfo imi, int subtypeIndex, @Nullable String subtypeLocale,
- @NonNull String systemLocale) {
+ @Nullable CharSequence layoutName, @NonNull InputMethodInfo imi, int subtypeIndex,
+ @Nullable String subtypeLocale, @NonNull String systemLocale) {
mImeName = imeName;
mSubtypeName = subtypeName;
+ mLayoutName = layoutName;
mImi = imi;
mSubtypeIndex = subtypeIndex;
if (TextUtils.isEmpty(subtypeLocale)) {
@@ -252,8 +259,11 @@ final class InputMethodSubtypeSwitchingController {
subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
.getDisplayName(userAwareContext, imi.getPackageName(),
imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel,
- subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+ final var layoutName = subtype.overridesImplicitlyEnabledSubtype() ? null
+ : subtype.getLayoutDisplayName(userAwareContext,
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, layoutName,
+ imi, j, subtype.getLocale(), mSystemLocaleStr));
// Removing this subtype from enabledSubtypeSet because we no
// longer need to add an entry of this subtype to imList to avoid
@@ -262,8 +272,8 @@ final class InputMethodSubtypeSwitchingController {
}
}
} else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_INDEX, null,
- mSystemLocaleStr));
+ imList.add(new ImeSubtypeListItem(imeLabel, null /* subtypeName */,
+ null /* layoutName */, imi, NOT_A_SUBTYPE_INDEX, null, mSystemLocaleStr));
}
}
Collections.sort(imList);
@@ -311,13 +321,16 @@ final class InputMethodSubtypeSwitchingController {
subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
.getDisplayName(userAwareContext, imi.getPackageName(),
imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel,
- subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+ final var layoutName = subtype.overridesImplicitlyEnabledSubtype() ? null
+ : subtype.getLayoutDisplayName(userAwareContext,
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, layoutName,
+ imi, j, subtype.getLocale(), mSystemLocaleStr));
}
}
} else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_INDEX, null,
- mSystemLocaleStr));
+ imList.add(new ImeSubtypeListItem(imeLabel, null /* subtypeName */,
+ null /* layoutName */, imi, NOT_A_SUBTYPE_INDEX, null, mSystemLocaleStr));
}
}
return imList;
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index bb4ae96da53b..a132876b72a3 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -46,7 +46,6 @@ import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import com.android.server.integrity.model.RuleMetadata;
import java.io.File;
import java.nio.charset.StandardCharsets;
diff --git a/services/core/java/com/android/server/integrity/model/BitInputStream.java b/services/core/java/com/android/server/integrity/model/BitInputStream.java
deleted file mode 100644
index e7cc81eab26b..000000000000
--- a/services/core/java/com/android/server/integrity/model/BitInputStream.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.model;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/** A wrapper class for reading a stream of bits.
- *
- * <p>Note: this class reads from underlying stream byte-by-byte. It is advised to apply buffering
- * to underlying streams.
- */
-public class BitInputStream {
-
- private long mBitsRead;
-
- private InputStream mInputStream;
-
- private byte mCurrentByte;
-
- public BitInputStream(InputStream inputStream) {
- mInputStream = inputStream;
- }
-
- /**
- * Read the next number of bits from the stream.
- *
- * @param numOfBits The number of bits to read.
- * @return The value read from the stream.
- */
- public int getNext(int numOfBits) throws IOException {
- int component = 0;
- int count = 0;
-
- while (count++ < numOfBits) {
- if (mBitsRead % 8 == 0) {
- mCurrentByte = getNextByte();
- }
- int offset = 7 - (int) (mBitsRead % 8);
-
- component <<= 1;
- component |= (mCurrentByte >>> offset) & 1;
-
- mBitsRead++;
- }
-
- return component;
- }
-
- /** Check if there are bits left in the stream. */
- public boolean hasNext() throws IOException {
- return mInputStream.available() > 0;
- }
-
- private byte getNextByte() throws IOException {
- return (byte) mInputStream.read();
- }
-}
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
deleted file mode 100644
index 14b35fd016eb..000000000000
--- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.model;
-
-import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-/** A wrapper class for writing a stream of bits. */
-public class BitOutputStream {
-
- private static final int BUFFER_SIZE = 4 * 1024;
-
- private int mNextBitIndex;
-
- private final OutputStream mOutputStream;
- private final byte[] mBuffer;
-
- public BitOutputStream(OutputStream outputStream) {
- mBuffer = new byte[BUFFER_SIZE];
- mNextBitIndex = 0;
- mOutputStream = outputStream;
- }
-
- /**
- * Set the next number of bits in the stream to value.
- *
- * @param numOfBits The number of bits used to represent the value.
- * @param value The value to convert to bits.
- */
- public void setNext(int numOfBits, int value) throws IOException {
- if (numOfBits <= 0) {
- return;
- }
-
- // optional: we can do some clever size checking to "OR" an entire segment of bits instead
- // of setting bits one by one, but it is probably not worth it.
- int nextBitMask = 1 << (numOfBits - 1);
- while (numOfBits-- > 0) {
- setNext((value & nextBitMask) != 0);
- nextBitMask >>>= 1;
- }
- }
-
- /**
- * Set the next bit in the stream to value.
- *
- * @param value The value to set the bit to
- */
- public void setNext(boolean value) throws IOException {
- int byteToWrite = mNextBitIndex / BYTE_BITS;
- if (byteToWrite == BUFFER_SIZE) {
- mOutputStream.write(mBuffer);
- reset();
- byteToWrite = 0;
- }
- if (value) {
- mBuffer[byteToWrite] |= 1 << (BYTE_BITS - 1 - (mNextBitIndex % BYTE_BITS));
- }
- mNextBitIndex++;
- }
-
- /** Set the next bit in the stream to true. */
- public void setNext() throws IOException {
- setNext(/* value= */ true);
- }
-
- /**
- * Flush the data written to the underlying {@link java.io.OutputStream}. Any unfinished bytes
- * will be padded with 0.
- */
- public void flush() throws IOException {
- int endByte = mNextBitIndex / BYTE_BITS;
- if (mNextBitIndex % BYTE_BITS != 0) {
- // If next bit is not the first bit of a byte, then mNextBitIndex / BYTE_BITS would be
- // the byte that includes already written bits. We need to increment it so this byte
- // gets written.
- endByte++;
- }
- mOutputStream.write(mBuffer, 0, endByte);
- reset();
- }
-
- /** Reset this output stream to start state. */
- private void reset() {
- mNextBitIndex = 0;
- Arrays.fill(mBuffer, (byte) 0);
- }
-}
diff --git a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
deleted file mode 100644
index ceed054e4a53..000000000000
--- a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 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.integrity.model;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An output stream that tracks the total number written bytes since construction and allows
- * querying this value any time during the execution.
- *
- * <p>This class is used for constructing the rule indexing.
- */
-public class ByteTrackedOutputStream extends OutputStream {
-
- private int mWrittenBytesCount;
- private final OutputStream mOutputStream;
-
- public ByteTrackedOutputStream(OutputStream outputStream) {
- mWrittenBytesCount = 0;
- mOutputStream = outputStream;
- }
-
- @Override
- public void write(int b) throws IOException {
- mWrittenBytesCount++;
- mOutputStream.write(b);
- }
-
- /**
- * Writes the given bytes into the output stream provided in constructor and updates the total
- * number of written bytes.
- */
- @Override
- public void write(byte[] bytes) throws IOException {
- write(bytes, 0, bytes.length);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- mWrittenBytesCount += len;
- mOutputStream.write(b, off, len);
- }
-
- /** Returns the total number of bytes written into the output stream at the requested time. */
- public int getWrittenBytesCount() {
- return mWrittenBytesCount;
- }
-}
diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
deleted file mode 100644
index 94e6708c3038..000000000000
--- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.model;
-
-import android.content.integrity.Rule;
-
-/**
- * A helper class containing information about the binary representation of different {@link Rule}
- * components.
- */
-public final class ComponentBitSize {
- public static final int FORMAT_VERSION_BITS = 8;
-
- public static final int EFFECT_BITS = 3;
- public static final int KEY_BITS = 4;
- public static final int OPERATOR_BITS = 3;
- public static final int CONNECTOR_BITS = 2;
- public static final int SEPARATOR_BITS = 3;
- public static final int VALUE_SIZE_BITS = 8;
- public static final int IS_HASHED_BITS = 1;
-
- public static final int ATOMIC_FORMULA_START = 0;
- public static final int COMPOUND_FORMULA_START = 1;
- public static final int COMPOUND_FORMULA_END = 2;
- public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3;
-
- public static final int DEFAULT_FORMAT_VERSION = 1;
- public static final int SIGNAL_BIT = 1;
-
- public static final int BYTE_BITS = 8;
-}
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
deleted file mode 100644
index b0647fc46473..000000000000
--- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.model;
-
-import android.annotation.Nullable;
-import android.content.integrity.Rule;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A class encapsulating the result from the evaluation engine after evaluating rules against app
- * install metadata.
- *
- * <p>It contains the outcome effect (whether to allow or block the install), and the rule causing
- * that effect.
- */
-public final class IntegrityCheckResult {
-
- public enum Effect {
- ALLOW,
- DENY
- }
-
- private final Effect mEffect;
- private final List<Rule> mRuleList;
-
- private IntegrityCheckResult(Effect effect, @Nullable List<Rule> ruleList) {
- this.mEffect = effect;
- this.mRuleList = ruleList;
- }
-
- public Effect getEffect() {
- return mEffect;
- }
-
- public List<Rule> getMatchedRules() {
- return mRuleList;
- }
-
- /**
- * Create an ALLOW evaluation outcome.
- *
- * @return An evaluation outcome with ALLOW effect and no rule.
- */
- public static IntegrityCheckResult allow() {
- return new IntegrityCheckResult(Effect.ALLOW, Collections.emptyList());
- }
-
- /**
- * Create an ALLOW evaluation outcome.
- *
- * @return An evaluation outcome with ALLOW effect and rule causing that effect.
- */
- public static IntegrityCheckResult allow(List<Rule> ruleList) {
- return new IntegrityCheckResult(Effect.ALLOW, ruleList);
- }
-
- /**
- * Create a DENY evaluation outcome.
- *
- * @param ruleList All valid rules that cause the DENY effect.
- * @return An evaluation outcome with DENY effect and rule causing that effect.
- */
- public static IntegrityCheckResult deny(List<Rule> ruleList) {
- return new IntegrityCheckResult(Effect.DENY, ruleList);
- }
-
- /** Returns true when the {@code mEffect} is caused by an app certificate mismatch. */
- public boolean isCausedByAppCertRule() {
- return mRuleList.stream().anyMatch(rule -> rule.getFormula().isAppCertificateFormula());
- }
-
- /** Returns true when the {@code mEffect} is caused by an installer rule. */
- public boolean isCausedByInstallerRule() {
- return mRuleList.stream().anyMatch(rule -> rule.getFormula().isInstallerFormula());
- }
-
-}
diff --git a/services/core/java/com/android/server/integrity/model/RuleMetadata.java b/services/core/java/com/android/server/integrity/model/RuleMetadata.java
deleted file mode 100644
index 6b582ae7b5f2..000000000000
--- a/services/core/java/com/android/server/integrity/model/RuleMetadata.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.model;
-
-import android.annotation.Nullable;
-
-/** Data class containing relevant metadata associated with a rule set. */
-public class RuleMetadata {
-
- private final String mRuleProvider;
- private final String mVersion;
-
- public RuleMetadata(String ruleProvider, String version) {
- mRuleProvider = ruleProvider;
- mVersion = version;
- }
-
- @Nullable
- public String getRuleProvider() {
- return mRuleProvider;
- }
-
- @Nullable
- public String getVersion() {
- return mVersion;
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
deleted file mode 100644
index e831e40e70d1..000000000000
--- a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.parser;
-
-import android.annotation.Nullable;
-import android.util.Xml;
-
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.server.integrity.model.RuleMetadata;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/** Helper class for parsing rule metadata. */
-public class RuleMetadataParser {
-
- public static final String RULE_PROVIDER_TAG = "P";
- public static final String VERSION_TAG = "V";
-
- /** Parse the rule metadata from an input stream. */
- @Nullable
- public static RuleMetadata parse(InputStream inputStream)
- throws XmlPullParserException, IOException {
-
- String ruleProvider = "";
- String version = "";
-
- TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream);
-
- int eventType;
- while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlPullParser.START_TAG) {
- String tag = xmlPullParser.getName();
- switch (tag) {
- case RULE_PROVIDER_TAG:
- ruleProvider = xmlPullParser.nextText();
- break;
- case VERSION_TAG:
- version = xmlPullParser.nextText();
- break;
- default:
- throw new IllegalStateException("Unknown tag in metadata: " + tag);
- }
- }
- }
-
- return new RuleMetadata(ruleProvider, version);
- }
-}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
deleted file mode 100644
index 8ba5870aef0f..000000000000
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.InstallerAllowedByManifestFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.IntegrityUtils;
-import android.content.integrity.Rule;
-
-import com.android.internal.util.Preconditions;
-import com.android.server.integrity.model.BitOutputStream;
-import com.android.server.integrity.model.ByteTrackedOutputStream;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
-public class RuleBinarySerializer implements RuleSerializer {
- static final int TOTAL_RULE_SIZE_LIMIT = 200000;
- static final int INDEXED_RULE_SIZE_LIMIT = 100000;
- static final int NONINDEXED_RULE_SIZE_LIMIT = 1000;
-
- // Get the byte representation for a list of rules.
- @Override
- public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
- throws RuleSerializeException {
- try {
- ByteArrayOutputStream rulesOutputStream = new ByteArrayOutputStream();
- serialize(rules, formatVersion, rulesOutputStream, new ByteArrayOutputStream());
- return rulesOutputStream.toByteArray();
- } catch (Exception e) {
- throw new RuleSerializeException(e.getMessage(), e);
- }
- }
-
- // Get the byte representation for a list of rules, and write them to an output stream.
- @Override
- public void serialize(
- List<Rule> rules,
- Optional<Integer> formatVersion,
- OutputStream rulesFileOutputStream,
- OutputStream indexingFileOutputStream)
- throws RuleSerializeException {
- try {
- if (rules == null) {
- throw new IllegalArgumentException("Null rules cannot be serialized.");
- }
-
- if (rules.size() > TOTAL_RULE_SIZE_LIMIT) {
- throw new IllegalArgumentException("Too many rules provided: " + rules.size());
- }
-
- // Determine the indexing groups and the order of the rules within each indexed group.
- Map<Integer, Map<String, List<Rule>>> indexedRules =
- RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
-
- // Validate the rule blocks are not larger than expected limits.
- verifySize(indexedRules.get(PACKAGE_NAME_INDEXED), INDEXED_RULE_SIZE_LIMIT);
- verifySize(indexedRules.get(APP_CERTIFICATE_INDEXED), INDEXED_RULE_SIZE_LIMIT);
- verifySize(indexedRules.get(NOT_INDEXED), NONINDEXED_RULE_SIZE_LIMIT);
-
- // Serialize the rules.
- ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
- new ByteTrackedOutputStream(rulesFileOutputStream);
- serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
- LinkedHashMap<String, Integer> packageNameIndexes =
- serializeRuleList(
- indexedRules.get(PACKAGE_NAME_INDEXED),
- ruleFileByteTrackedOutputStream);
- LinkedHashMap<String, Integer> appCertificateIndexes =
- serializeRuleList(
- indexedRules.get(APP_CERTIFICATE_INDEXED),
- ruleFileByteTrackedOutputStream);
- LinkedHashMap<String, Integer> unindexedRulesIndexes =
- serializeRuleList(
- indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream);
-
- // Serialize their indexes.
- BitOutputStream indexingBitOutputStream = new BitOutputStream(indexingFileOutputStream);
- serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */ true);
- serializeIndexGroup(
- appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ true);
- serializeIndexGroup(
- unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ false);
- indexingBitOutputStream.flush();
- } catch (Exception e) {
- throw new RuleSerializeException(e.getMessage(), e);
- }
- }
-
- private void verifySize(Map<String, List<Rule>> ruleListMap, int ruleSizeLimit) {
- int totalRuleCount =
- ruleListMap.values().stream()
- .map(list -> list.size())
- .collect(Collectors.summingInt(Integer::intValue));
- if (totalRuleCount > ruleSizeLimit) {
- throw new IllegalArgumentException(
- "Too many rules provided in the indexing group. Provided "
- + totalRuleCount
- + " limit "
- + ruleSizeLimit);
- }
- }
-
- private void serializeRuleFileMetadata(
- Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream)
- throws IOException {
- int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
-
- BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
- bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
- bitOutputStream.flush();
- }
-
- private LinkedHashMap<String, Integer> serializeRuleList(
- Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream)
- throws IOException {
- Preconditions.checkArgument(
- rulesMap != null, "serializeRuleList should never be called with null rule list.");
-
- BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
- LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap();
- indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
-
- List<String> sortedKeys = rulesMap.keySet().stream().sorted().collect(Collectors.toList());
- int indexTracker = 0;
- for (String key : sortedKeys) {
- if (indexTracker >= INDEXING_BLOCK_SIZE) {
- indexMapping.put(key, outputStream.getWrittenBytesCount());
- indexTracker = 0;
- }
-
- for (Rule rule : rulesMap.get(key)) {
- serializeRule(rule, bitOutputStream);
- bitOutputStream.flush();
- indexTracker++;
- }
- }
- indexMapping.put(END_INDEXING_KEY, outputStream.getWrittenBytesCount());
-
- return indexMapping;
- }
-
- private void serializeRule(Rule rule, BitOutputStream bitOutputStream) throws IOException {
- if (rule == null) {
- throw new IllegalArgumentException("Null rule can not be serialized");
- }
-
- // Start with a '1' bit to mark the start of a rule.
- bitOutputStream.setNext();
-
- serializeFormula(rule.getFormula(), bitOutputStream);
- bitOutputStream.setNext(EFFECT_BITS, rule.getEffect());
-
- // End with a '1' bit to mark the end of a rule.
- bitOutputStream.setNext();
- }
-
- private void serializeFormula(IntegrityFormula formula, BitOutputStream bitOutputStream)
- throws IOException {
- if (formula instanceof AtomicFormula) {
- serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
- } else if (formula instanceof CompoundFormula) {
- serializeCompoundFormula((CompoundFormula) formula, bitOutputStream);
- } else if (formula instanceof InstallerAllowedByManifestFormula) {
- bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START);
- } else {
- throw new IllegalArgumentException(
- String.format("Invalid formula type: %s", formula.getClass()));
- }
- }
-
- private void serializeCompoundFormula(
- CompoundFormula compoundFormula, BitOutputStream bitOutputStream) throws IOException {
- if (compoundFormula == null) {
- throw new IllegalArgumentException("Null compound formula can not be serialized");
- }
-
- bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START);
- bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector());
- for (IntegrityFormula formula : compoundFormula.getFormulas()) {
- serializeFormula(formula, bitOutputStream);
- }
- bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END);
- }
-
- private void serializeAtomicFormula(
- AtomicFormula atomicFormula, BitOutputStream bitOutputStream) throws IOException {
- if (atomicFormula == null) {
- throw new IllegalArgumentException("Null atomic formula can not be serialized");
- }
-
- bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START);
- bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey());
- if (atomicFormula.getTag() == AtomicFormula.STRING_ATOMIC_FORMULA_TAG) {
- AtomicFormula.StringAtomicFormula stringAtomicFormula =
- (AtomicFormula.StringAtomicFormula) atomicFormula;
- bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
- serializeStringValue(
- stringAtomicFormula.getValue(),
- stringAtomicFormula.getIsHashedValue(),
- bitOutputStream);
- } else if (atomicFormula.getTag() == AtomicFormula.LONG_ATOMIC_FORMULA_TAG) {
- AtomicFormula.LongAtomicFormula longAtomicFormula =
- (AtomicFormula.LongAtomicFormula) atomicFormula;
- bitOutputStream.setNext(OPERATOR_BITS, longAtomicFormula.getOperator());
- // TODO(b/147880712): Temporary hack until we support long values in bitOutputStream
- long value = longAtomicFormula.getValue();
- serializeIntValue((int) (value >>> 32), bitOutputStream);
- serializeIntValue((int) value, bitOutputStream);
- } else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) {
- AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
- (AtomicFormula.BooleanAtomicFormula) atomicFormula;
- bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
- serializeBooleanValue(booleanAtomicFormula.getValue(), bitOutputStream);
- } else {
- throw new IllegalArgumentException(
- String.format("Invalid atomic formula type: %s", atomicFormula.getClass()));
- }
- }
-
- private void serializeIndexGroup(
- LinkedHashMap<String, Integer> indexes,
- BitOutputStream bitOutputStream,
- boolean isIndexed)
- throws IOException {
- // Output the starting location of this indexing group.
- serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */ false, bitOutputStream);
- serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
-
- // If the group is indexed, output the locations of the indexes.
- if (isIndexed) {
- for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
- if (!entry.getKey().equals(START_INDEXING_KEY)
- && !entry.getKey().equals(END_INDEXING_KEY)) {
- serializeStringValue(
- entry.getKey(), /* isHashedValue= */ false, bitOutputStream);
- serializeIntValue(entry.getValue(), bitOutputStream);
- }
- }
- }
-
- // Output the end location of this indexing group.
- serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream);
- serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream);
- }
-
- private void serializeStringValue(
- String value, boolean isHashedValue, BitOutputStream bitOutputStream)
- throws IOException {
- if (value == null) {
- throw new IllegalArgumentException("String value can not be null.");
- }
- byte[] valueBytes = getBytesForString(value, isHashedValue);
-
- bitOutputStream.setNext(isHashedValue);
- bitOutputStream.setNext(VALUE_SIZE_BITS, valueBytes.length);
- for (byte valueByte : valueBytes) {
- bitOutputStream.setNext(/* numOfBits= */ 8, valueByte);
- }
- }
-
- private void serializeIntValue(int value, BitOutputStream bitOutputStream) throws IOException {
- bitOutputStream.setNext(/* numOfBits= */ 32, value);
- }
-
- private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream)
- throws IOException {
- bitOutputStream.setNext(value);
- }
-
- // Get the byte array for a value.
- // If the value is not hashed, use its byte array form directly.
- // If the value is hashed, get the raw form decoding of the value. All hashed values are
- // hex-encoded. Serialized values are in raw form.
- private static byte[] getBytesForString(String value, boolean isHashedValue) {
- if (!isHashedValue) {
- return value.getBytes(StandardCharsets.UTF_8);
- }
- return IntegrityUtils.getBytesFromHexDigest(value);
- }
-}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
deleted file mode 100644
index 2cbd4ede5214..000000000000
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Holds the indexing type and indexing key of a given formula. */
-class RuleIndexingDetails {
-
- static final int NOT_INDEXED = 0;
- static final int PACKAGE_NAME_INDEXED = 1;
- static final int APP_CERTIFICATE_INDEXED = 2;
-
- static final String DEFAULT_RULE_KEY = "N/A";
-
- /** Represents which indexed file the rule should be located. */
- @IntDef(
- value = {
- NOT_INDEXED,
- PACKAGE_NAME_INDEXED,
- APP_CERTIFICATE_INDEXED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface IndexType {
- }
-
- private @IndexType int mIndexType;
- private String mRuleKey;
-
- /** Constructor without a ruleKey for {@code NOT_INDEXED}. */
- RuleIndexingDetails(@IndexType int indexType) {
- this.mIndexType = indexType;
- this.mRuleKey = DEFAULT_RULE_KEY;
- }
-
- /** Constructor with a ruleKey for indexed rules. */
- RuleIndexingDetails(@IndexType int indexType, String ruleKey) {
- this.mIndexType = indexType;
- this.mRuleKey = ruleKey;
- }
-
- /** Returns the indexing type for the rule. */
- @IndexType
- public int getIndexType() {
- return mIndexType;
- }
-
- /** Returns the identified rule key. */
- public String getRuleKey() {
- return mRuleKey;
- }
-}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
deleted file mode 100644
index e7235591fb9b..000000000000
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-/** A helper class for identifying the indexing type and key of a given rule. */
-class RuleIndexingDetailsIdentifier {
-
- /**
- * Splits a given rule list into three indexing categories. Each rule category is returned as a
- * TreeMap that is sorted by their indexing keys -- where keys correspond to package name for
- * PACKAGE_NAME_INDEXED rules, app certificate for APP_CERTIFICATE_INDEXED rules and N/A for
- * NOT_INDEXED rules.
- */
- public static Map<Integer, Map<String, List<Rule>>> splitRulesIntoIndexBuckets(
- List<Rule> rules) {
- if (rules == null) {
- throw new IllegalArgumentException(
- "Index buckets cannot be created for null rule list.");
- }
-
- Map<Integer, Map<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
- typeOrganizedRuleMap.put(NOT_INDEXED, new HashMap());
- typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new HashMap<>());
- typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new HashMap<>());
-
- // Split the rules into the appropriate indexed pattern. The Tree Maps help us to keep the
- // entries sorted by their index key.
- for (Rule rule : rules) {
- RuleIndexingDetails indexingDetails;
- try {
- indexingDetails = getIndexingDetails(rule.getFormula());
- } catch (Exception e) {
- throw new IllegalArgumentException(
- String.format("Malformed rule identified. [%s]", rule.toString()));
- }
-
- int ruleIndexType = indexingDetails.getIndexType();
- String ruleKey = indexingDetails.getRuleKey();
-
- if (!typeOrganizedRuleMap.get(ruleIndexType).containsKey(ruleKey)) {
- typeOrganizedRuleMap.get(ruleIndexType).put(ruleKey, new ArrayList());
- }
-
- typeOrganizedRuleMap.get(ruleIndexType).get(ruleKey).add(rule);
- }
-
- return typeOrganizedRuleMap;
- }
-
- private static RuleIndexingDetails getIndexingDetails(IntegrityFormula formula) {
- switch (formula.getTag()) {
- case IntegrityFormula.COMPOUND_FORMULA_TAG:
- return getIndexingDetailsForCompoundFormula((CompoundFormula) formula);
- case IntegrityFormula.STRING_ATOMIC_FORMULA_TAG:
- return getIndexingDetailsForStringAtomicFormula(
- (AtomicFormula.StringAtomicFormula) formula);
- case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG:
- case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG:
- case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG:
- // Package name and app certificate related formulas are string atomic formulas.
- return new RuleIndexingDetails(NOT_INDEXED);
- default:
- throw new IllegalArgumentException(
- String.format("Invalid formula tag type: %s", formula.getTag()));
- }
- }
-
- private static RuleIndexingDetails getIndexingDetailsForCompoundFormula(
- CompoundFormula compoundFormula) {
- int connector = compoundFormula.getConnector();
- List<IntegrityFormula> formulas = compoundFormula.getFormulas();
-
- switch (connector) {
- case CompoundFormula.AND:
- case CompoundFormula.OR:
- // If there is a package name related atomic rule, return package name indexed.
- Optional<RuleIndexingDetails> packageNameRule =
- formulas.stream()
- .map(formula -> getIndexingDetails(formula))
- .filter(ruleIndexingDetails -> ruleIndexingDetails.getIndexType()
- == PACKAGE_NAME_INDEXED)
- .findAny();
- if (packageNameRule.isPresent()) {
- return packageNameRule.get();
- }
-
- // If there is an app certificate related atomic rule but no package name related
- // atomic rule, return app certificate indexed.
- Optional<RuleIndexingDetails> appCertificateRule =
- formulas.stream()
- .map(formula -> getIndexingDetails(formula))
- .filter(ruleIndexingDetails -> ruleIndexingDetails.getIndexType()
- == APP_CERTIFICATE_INDEXED)
- .findAny();
- if (appCertificateRule.isPresent()) {
- return appCertificateRule.get();
- }
-
- // Do not index when there is not package name or app certificate indexing.
- return new RuleIndexingDetails(NOT_INDEXED);
- default:
- // Having a NOT operator in the indexing messes up the indexing; e.g., deny
- // installation if app certificate is NOT X (should not be indexed with app cert
- // X). We will not keep these rules indexed.
- // Also any other type of unknown operators will not be indexed.
- return new RuleIndexingDetails(NOT_INDEXED);
- }
- }
-
- private static RuleIndexingDetails getIndexingDetailsForStringAtomicFormula(
- AtomicFormula.StringAtomicFormula atomicFormula) {
- switch (atomicFormula.getKey()) {
- case AtomicFormula.PACKAGE_NAME:
- return new RuleIndexingDetails(PACKAGE_NAME_INDEXED, atomicFormula.getValue());
- case AtomicFormula.APP_CERTIFICATE:
- return new RuleIndexingDetails(APP_CERTIFICATE_INDEXED, atomicFormula.getValue());
- default:
- return new RuleIndexingDetails(NOT_INDEXED);
- }
- }
-}
-
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
deleted file mode 100644
index 022b4b8cb7eb..000000000000
--- a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG;
-import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG;
-
-import android.util.Xml;
-
-import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.integrity.model.RuleMetadata;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-
-/** Helper class for writing rule metadata. */
-public class RuleMetadataSerializer {
- /** Serialize the rule metadata to an output stream. */
- public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream)
- throws IOException {
- TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream);
-
- serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider());
- serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion());
-
- xmlSerializer.endDocument();
- }
-
- private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag,
- String value) throws IOException {
- xmlSerializer.startTag(/* namespace= */ null, tag);
- xmlSerializer.text(value);
- xmlSerializer.endTag(/* namespace= */ null, tag);
- }
-}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
deleted file mode 100644
index 2941856915a8..000000000000
--- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import android.content.integrity.Rule;
-
-import java.io.OutputStream;
-import java.util.List;
-import java.util.Optional;
-
-/** A helper class to serialize rules from the {@link Rule} model. */
-public interface RuleSerializer {
-
- /** Serialize rules to an output stream */
- void serialize(
- List<Rule> rules,
- Optional<Integer> formatVersion,
- OutputStream ruleFileOutputStream,
- OutputStream indexingFileOutputStream)
- throws RuleSerializeException;
-
- /** Serialize rules to a ByteArray. */
- byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
- throws RuleSerializeException;
-}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index acc8f6634f5c..f611c57dab03 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -35,6 +35,7 @@ import android.hardware.contexthub.MessageDeliveryStatus;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.HubInfo;
import android.hardware.location.IContextHubCallback;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
@@ -57,6 +58,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
import android.util.proto.ProtoOutputStream;
@@ -134,6 +136,9 @@ public class ContextHubService extends IContextHubService.Stub {
private Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
private List<String> mSupportedContextHubPerms;
private List<ContextHubInfo> mContextHubInfoList;
+
+ @Nullable private final HubInfoRegistry mHubInfoRegistry;
+
private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
new RemoteCallbackList<>();
@@ -309,10 +314,21 @@ public class ContextHubService extends IContextHubService.Stub {
mContext = context;
long startTimeNs = SystemClock.elapsedRealtimeNanos();
mContextHubWrapper = contextHubWrapper;
+
if (!initContextHubServiceState(startTimeNs)) {
Log.e(TAG, "Failed to initialize the Context Hub Service");
+ mHubInfoRegistry = null;
return;
}
+
+ if (Flags.offloadApi()) {
+ mHubInfoRegistry = new HubInfoRegistry(mContextHubWrapper);
+ Log.i(TAG, "Enabling generic offload API");
+ } else {
+ mHubInfoRegistry = null;
+ Log.i(TAG, "Disabling generic offload API");
+ }
+
initDefaultClientMap();
initLocationSettingNotifications();
@@ -427,7 +443,7 @@ public class ContextHubService extends IContextHubService.Stub {
Pair<List<ContextHubInfo>, List<String>> hubInfo;
try {
- hubInfo = mContextHubWrapper.getHubs();
+ hubInfo = mContextHubWrapper.getContextHubs();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while getting Context Hub info", e);
hubInfo = new Pair<>(Collections.emptyList(), Collections.emptyList());
@@ -713,6 +729,16 @@ public class ContextHubService extends IContextHubService.Stub {
return mContextHubInfoList;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @Override
+ public List<HubInfo> getHubs() throws RemoteException {
+ super.getHubs_enforcePermission();
+ if (mHubInfoRegistry == null) {
+ return Collections.emptyList();
+ }
+ return mHubInfoRegistry.getHubs();
+ }
+
/**
* Creates an internal load transaction callback to be used for old API clients
*
@@ -1417,6 +1443,8 @@ public class ContextHubService extends IContextHubService.Stub {
}
}
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ pw = ipw;
pw.println("Dumping ContextHub Service");
pw.println("");
@@ -1428,6 +1456,11 @@ public class ContextHubService extends IContextHubService.Stub {
pw.println("Supported permissions: "
+ Arrays.toString(mSupportedContextHubPerms.toArray()));
pw.println("");
+
+ if (mHubInfoRegistry != null) {
+ mHubInfoRegistry.dump(ipw);
+ }
+
pw.println("=================== NANOAPPS ====================");
// Dump nanoAppHash
mNanoAppStateManager.foreachNanoAppInstanceInfo(pw::println);
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
new file mode 100644
index 000000000000..68de9dbda2e1
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.hardware.location.HubInfo;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+class HubInfoRegistry {
+ private static final String TAG = "HubInfoRegistry";
+
+ private final IContextHubWrapper mContextHubWrapper;
+
+ private final List<HubInfo> mHubsInfo;
+
+ HubInfoRegistry(IContextHubWrapper contextHubWrapper) {
+ List<HubInfo> hubInfos;
+ mContextHubWrapper = contextHubWrapper;
+ try {
+ hubInfos = mContextHubWrapper.getHubs();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while getting Hub info", e);
+ hubInfos = Collections.emptyList();
+ }
+ mHubsInfo = hubInfos;
+ }
+
+ /** Retrieve the list of hubs available. */
+ List<HubInfo> getHubs() {
+ return mHubsInfo;
+ }
+
+ void dump(IndentingPrintWriter ipw) {
+ ipw.println(TAG);
+
+ ipw.increaseIndent();
+ for (HubInfo hubInfo : mHubsInfo) {
+ ipw.println(hubInfo);
+ }
+ ipw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 5e9277ac0faf..6656a6fe9eb4 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -30,9 +30,11 @@ import android.hardware.contexthub.V1_2.HubAppInfo;
import android.hardware.contexthub.V1_2.IContexthubCallback;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.HubInfo;
import android.hardware.location.NanoAppBinary;
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
+import android.hardware.location.VendorHubInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -52,13 +54,14 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* @hide
*/
public abstract class IContextHubWrapper {
+ private static final boolean DEBUG = false;
private static final String TAG = "IContextHubWrapper";
/**
@@ -217,10 +220,14 @@ public abstract class IContextHubWrapper {
return proxy == null ? null : new ContextHubWrapperAidl(proxy);
}
- /**
- * Calls the appropriate getHubs function depending on the HAL version.
- */
- public abstract Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException;
+ /** Calls the appropriate getHubs function depending on the HAL version. */
+ public abstract Pair<List<ContextHubInfo>, List<String>> getContextHubs()
+ throws RemoteException;
+
+ /** Calls the appropriate getHubs function depending on the HAL version. */
+ public List<HubInfo> getHubs() throws RemoteException {
+ return Collections.emptyList();
+ }
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
@@ -556,7 +563,7 @@ public abstract class IContextHubWrapper {
mIsTestModeEnabled.set(false);
}
- public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
+ public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException {
android.hardware.contexthub.IContextHub hub = getHub();
if (hub == null) {
return new Pair<List<ContextHubInfo>, List<String>>(new ArrayList<ContextHubInfo>(),
@@ -574,6 +581,47 @@ public abstract class IContextHubWrapper {
return new Pair(hubInfoList, new ArrayList<String>(supportedPermissions));
}
+ public List<HubInfo> getHubs() throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return Collections.emptyList();
+ }
+
+ List<HubInfo> retVal = new ArrayList<>();
+ final List<android.hardware.contexthub.HubInfo> halHubs = hub.getHubs();
+
+ for (android.hardware.contexthub.HubInfo halHub : halHubs) {
+ /* HAL -> API Type conversion */
+ final HubInfo hubInfo;
+ switch (halHub.hubDetails.getTag()) {
+ case android.hardware.contexthub.HubInfo.HubDetails.contextHubInfo:
+ ContextHubInfo contextHubInfo =
+ new ContextHubInfo(halHub.hubDetails.getContextHubInfo());
+ hubInfo = new HubInfo(halHub.hubId, contextHubInfo);
+ break;
+ case android.hardware.contexthub.HubInfo.HubDetails.vendorHubInfo:
+ VendorHubInfo vendorHubInfo =
+ new VendorHubInfo(halHub.hubDetails.getVendorHubInfo());
+ hubInfo = new HubInfo(halHub.hubId, vendorHubInfo);
+ break;
+ default:
+ Log.w(TAG, "getHubs: invalid hub: " + halHub);
+ // Invalid
+ continue;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "getHubs: hubInfo=" + hubInfo);
+ }
+ retVal.add(hubInfo);
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "getHubs: total count=" + retVal.size());
+ }
+ return retVal;
+ }
+
public boolean supportsLocationSettingNotifications() {
return true;
}
@@ -1061,7 +1109,7 @@ public abstract class IContextHubWrapper {
mHub = hub;
}
- public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
+ public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException {
ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>();
for (ContextHub hub : mHub.getHubs()) {
hubInfoList.add(new ContextHubInfo(hub));
@@ -1106,7 +1154,7 @@ public abstract class IContextHubWrapper {
mHub = hub;
}
- public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
+ public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException {
ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>();
for (ContextHub hub : mHub.getHubs()) {
hubInfoList.add(new ContextHubInfo(hub));
@@ -1170,7 +1218,7 @@ public abstract class IContextHubWrapper {
mHubInfo = new Pair(hubInfoList, supportedPermissions);
}
- public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
+ public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException {
mHub.getHubs_1_2(this);
return mHubInfo;
}
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index 6bc40988c097..0f65d1d44789 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -662,8 +662,6 @@ import java.util.Objects;
MediaRoute2Info.TYPE_HDMI_EARC,
/* defaultRouteId= */ "ROUTE_ID_HDMI_EARC",
/* nameResource= */ R.string.default_audio_route_name_external_device));
- // TODO: b/305199571 - Add a proper type constants and human readable names for AUX_LINE,
- // LINE_ANALOG, LINE_DIGITAL, BLE_BROADCAST, BLE_SPEAKER, BLE_HEADSET, and HEARING_AID.
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
AudioDeviceInfo.TYPE_HEARING_AID,
new SystemRouteInfo(
@@ -691,19 +689,22 @@ import java.util.Objects;
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
AudioDeviceInfo.TYPE_LINE_DIGITAL,
new SystemRouteInfo(
- MediaRoute2Info.TYPE_UNKNOWN,
+ com.android.media.flags.Flags.enableNewWiredMediaRoute2InfoTypes()
+ ? MediaRoute2Info.TYPE_LINE_DIGITAL : MediaRoute2Info.TYPE_UNKNOWN,
/* defaultRouteId= */ "ROUTE_ID_LINE_DIGITAL",
/* nameResource= */ R.string.default_audio_route_name_external_device));
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
AudioDeviceInfo.TYPE_LINE_ANALOG,
new SystemRouteInfo(
- MediaRoute2Info.TYPE_UNKNOWN,
+ com.android.media.flags.Flags.enableNewWiredMediaRoute2InfoTypes()
+ ? MediaRoute2Info.TYPE_LINE_ANALOG : MediaRoute2Info.TYPE_UNKNOWN,
/* defaultRouteId= */ "ROUTE_ID_LINE_ANALOG",
/* nameResource= */ R.string.default_audio_route_name_external_device));
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
AudioDeviceInfo.TYPE_AUX_LINE,
new SystemRouteInfo(
- MediaRoute2Info.TYPE_UNKNOWN,
+ com.android.media.flags.Flags.enableNewWiredMediaRoute2InfoTypes()
+ ? MediaRoute2Info.TYPE_AUX_LINE : MediaRoute2Info.TYPE_UNKNOWN,
/* defaultRouteId= */ "ROUTE_ID_AUX_LINE",
/* nameResource= */ R.string.default_audio_route_name_external_device));
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 89555a9f1de4..c8a87994ee16 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -161,6 +161,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl {
}
@Override
+ public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
+ // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
+ }
+
+ @Override
public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
KeyEvent ke, int sequenceId, ResultReceiver cb) {
// TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index d752429e64f7..668ee2adbd9f 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -230,51 +230,49 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
private final Runnable mUserEngagementTimeoutExpirationRunnable =
() -> {
synchronized (mLock) {
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ true,
+ /* isGlobalPrioritySessionActive= */ false);
}
};
@GuardedBy("mLock")
private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
- @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
+ @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARILY_ENGAGED, USER_DISENGAGED})
@Retention(RetentionPolicy.SOURCE)
private @interface UserEngagementState {}
/**
- * Indicates that the session is active and in one of the user engaged states.
+ * Indicates that the session is {@linkplain MediaSession#isActive() active} and in one of the
+ * {@linkplain PlaybackState#isActive() active states}.
*
* @see #updateUserEngagedStateIfNeededLocked(boolean)
*/
private static final int USER_PERMANENTLY_ENGAGED = 0;
/**
- * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
+ * Indicates that the session is {@linkplain MediaSession#isActive() active} and has recently
+ * switched to one of the {@linkplain PlaybackState#isActive() inactive states}.
*
* @see #updateUserEngagedStateIfNeededLocked(boolean)
*/
- private static final int USER_TEMPORARY_ENGAGED = 1;
+ private static final int USER_TEMPORARILY_ENGAGED = 1;
/**
- * Indicates that the session is either not active or in one of the user disengaged states
+ * Indicates that the session is either not {@linkplain MediaSession#isActive() active} or in
+ * one of the {@linkplain PlaybackState#isActive() inactive states}.
*
* @see #updateUserEngagedStateIfNeededLocked(boolean)
*/
private static final int USER_DISENGAGED = 2;
/**
- * Indicates the duration of the temporary engaged states, in milliseconds.
+ * Indicates the duration of the temporary engaged state, in milliseconds.
*
- * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
- * engaged, meaning the corresponding session is only considered in an engaged state for the
- * duration of this timeout, and only if coming from an engaged state.
- *
- * <p>For example, if a session is transitioning from a user-engaged state {@link
- * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link
- * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for
- * the duration of this timeout, starting at the transition instant. However, a temporary
- * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
- * state {@link PlaybackState#STATE_STOPPED}.
+ * <p>When switching to an {@linkplain PlaybackState#isActive() inactive state}, the user is
+ * treated as temporarily engaged, meaning the corresponding session is only considered in an
+ * engaged state for the duration of this timeout, and only if coming from an engaged state.
*/
private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000;
@@ -598,7 +596,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
mSessionCb.mCb.asBinder().unlinkToDeath(this, 0);
mDestroyed = true;
mPlaybackState = null;
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ true, /* isGlobalPrioritySessionActive= */ false);
mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -615,6 +614,24 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
mHandler.post(mUserEngagementTimeoutExpirationRunnable);
}
+ @Override
+ public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
+ mHandler.post(
+ () -> {
+ synchronized (mLock) {
+ if (isGlobalPrioritySessionActive) {
+ mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
+ } else {
+ if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) {
+ mHandler.postDelayed(
+ mUserEngagementTimeoutExpirationRunnable,
+ TEMP_USER_ENGAGED_TIMEOUT_MS);
+ }
+ }
+ }
+ });
+ }
+
/**
* Sends media button.
*
@@ -1063,21 +1080,20 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
@GuardedBy("mLock")
- private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+ private void updateUserEngagedStateIfNeededLocked(
+ boolean isTimeoutExpired, boolean isGlobalPrioritySessionActive) {
if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
return;
}
int oldUserEngagedState = mUserEngagementState;
int newUserEngagedState;
- if (!isActive() || mPlaybackState == null || mDestroyed) {
+ if (!isActive() || mPlaybackState == null) {
newUserEngagedState = USER_DISENGAGED;
- } else if (isActive() && mPlaybackState.isActive()) {
+ } else if (mPlaybackState.isActive()) {
newUserEngagedState = USER_PERMANENTLY_ENGAGED;
- } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) {
- newUserEngagedState =
- oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired
- ? USER_TEMPORARY_ENGAGED
- : USER_DISENGAGED;
+ } else if (oldUserEngagedState == USER_PERMANENTLY_ENGAGED
+ || (oldUserEngagedState == USER_TEMPORARILY_ENGAGED && !isTimeoutExpired)) {
+ newUserEngagedState = USER_TEMPORARILY_ENGAGED;
} else {
newUserEngagedState = USER_DISENGAGED;
}
@@ -1086,7 +1102,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
}
mUserEngagementState = newUserEngagedState;
- if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
+ if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) {
mHandler.postDelayed(
mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
} else {
@@ -1141,9 +1157,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
.logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK,
callingUid, callingPid);
}
+ boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
synchronized (mLock) {
mIsActive = active;
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
}
long token = Binder.clearCallingIdentity();
try {
@@ -1300,9 +1318,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde
boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState)
|| (!TRANSITION_PRIORITY_STATES.contains(oldState)
&& TRANSITION_PRIORITY_STATES.contains(newState));
+ boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive();
synchronized (mLock) {
mPlaybackState = state;
- updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false);
+ updateUserEngagedStateIfNeededLocked(
+ /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive);
}
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 15f90d4fdd0e..6c3b1234935a 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -206,6 +206,10 @@ public abstract class MediaSessionRecordImpl {
*/
public abstract void expireTempEngaged();
+ /** Notifies record that the global priority session active state changed. */
+ public abstract void onGlobalPrioritySessionActiveChanged(
+ boolean isGlobalPrioritySessionActive);
+
@Override
public final boolean equals(Object o) {
if (this == o) return true;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 1ebc856af2d8..2b29fbd9c5b5 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -362,6 +362,7 @@ public class MediaSessionService extends SystemService implements Monitor {
+ record.isActive());
}
user.pushAddressedPlayerChangedLocked();
+ mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
} else {
if (!user.mPriorityStack.contains(record)) {
Log.w(TAG, "Unknown session updated. Ignoring.");
@@ -394,11 +395,16 @@ public class MediaSessionService extends SystemService implements Monitor {
// Currently only media1 can become global priority session.
void setGlobalPrioritySession(MediaSessionRecord record) {
+ boolean globalPrioritySessionActiveChanged = false;
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
if (mGlobalPrioritySession != record) {
Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
+ " to " + record);
+ globalPrioritySessionActiveChanged =
+ (mGlobalPrioritySession == null && record.isActive())
+ || (mGlobalPrioritySession != null
+ && mGlobalPrioritySession.isActive() != record.isActive());
mGlobalPrioritySession = record;
if (user != null && user.mPriorityStack.contains(record)) {
// Handle the global priority session separately.
@@ -409,6 +415,30 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
}
+ if (globalPrioritySessionActiveChanged) {
+ mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
+ }
+ }
+
+ /** Returns whether the global priority session is active. */
+ boolean isGlobalPrioritySessionActive() {
+ synchronized (mLock) {
+ return isGlobalPriorityActiveLocked();
+ }
+ }
+
+ private void notifyGlobalPrioritySessionActiveChanged() {
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
+ synchronized (mLock) {
+ boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
+ for (Set<MediaSessionRecordImpl> records : mUserEngagedSessionsForFgs.values()) {
+ for (MediaSessionRecordImpl record : records) {
+ record.onGlobalPrioritySessionActiveChanged(isGlobalPriorityActive);
+ }
+ }
+ }
}
private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
@@ -646,8 +676,11 @@ public class MediaSessionService extends SystemService implements Monitor {
if (mGlobalPrioritySession == session) {
mGlobalPrioritySession = null;
- if (session.isActive() && user != null) {
- user.pushAddressedPlayerChangedLocked();
+ if (session.isActive()) {
+ if (user != null) {
+ user.pushAddressedPlayerChangedLocked();
+ }
+ mHandler.post(this::notifyGlobalPrioritySessionActiveChanged);
}
} else {
if (user != null) {
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java b/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java
new file mode 100644
index 000000000000..04f6216694a9
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.quality;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.media.quality.MediaQualityContract.BaseParameters;
+
+public class MediaQualityDbHelper extends SQLiteOpenHelper {
+
+ private static final String TAG = "MediaQualityDbHelper";
+
+ static final int DATABASE_VERSION_1 = 1;
+ private static final String DATABASE_NAME = "media_quality.db";
+ public static final String PICTURE_QUALITY_TABLE_NAME = "picture_quality";
+ public static final String SOUND_QUALITY_TABLE_NAME = "sound_quality";
+ public static final String SETTINGS = "settings";
+
+ MediaQualityDbHelper(Context context) {
+ super(context, DATABASE_NAME, null, getDbVersion());
+ }
+
+ private static int getDbVersion() {
+ return DATABASE_VERSION_1;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(getTableCreateStatement(PICTURE_QUALITY_TABLE_NAME));
+ db.execSQL(getTableCreateStatement(SOUND_QUALITY_TABLE_NAME));
+ }
+
+ private String getTableCreateStatement(String tableName) {
+ return
+ "CREATE TABLE " + tableName + "("
+ + BaseParameters.PARAMETER_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + BaseParameters.PARAMETER_TYPE + " INTEGER,"
+ + BaseParameters.PARAMETER_NAME + " STRING,"
+ + BaseParameters.PARAMETER_PACKAGE + " STRING,"
+ + BaseParameters.PARAMETER_INPUT_ID + " STRING,"
+ + SETTINGS + " TEXT)";
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // to do
+ }
+
+}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index d265b6a27853..84413d5710d0 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -16,14 +16,31 @@
package com.android.server.media.quality;
+import android.content.ContentValues;
import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.media.quality.AmbientBacklightSettings;
+import android.media.quality.IAmbientBacklightCallback;
import android.media.quality.IMediaQualityManager;
+import android.media.quality.IPictureProfileCallback;
+import android.media.quality.ISoundProfileCallback;
+import android.media.quality.MediaQualityContract.PictureQuality;
+import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfile;
+import android.os.Bundle;
+import android.util.Log;
import com.android.server.SystemService;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
/**
* This service manage picture profile and sound profile for TV setting. Also communicates with the
@@ -34,10 +51,14 @@ public class MediaQualityService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "MediaQualityService";
private final Context mContext;
+ private final MediaQualityDbHelper mMediaQualityDbHelper;
public MediaQualityService(Context context) {
super(context);
mContext = context;
+ mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
+ mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
+ mMediaQualityDbHelper.setIdleConnectionTimeout(30);
}
@Override
@@ -47,15 +68,83 @@ public class MediaQualityService extends SystemService {
// TODO: Add additional APIs. b/373951081
private final class BinderService extends IMediaQualityManager.Stub {
+
@Override
public PictureProfile createPictureProfile(PictureProfile pp) {
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+
+ ContentValues values = new ContentValues();
+ values.put(PictureQuality.PARAMETER_TYPE, pp.getProfileType());
+ values.put(PictureQuality.PARAMETER_NAME, pp.getName());
+ values.put(PictureQuality.PARAMETER_PACKAGE, pp.getPackageName());
+ values.put(PictureQuality.PARAMETER_INPUT_ID, pp.getInputId());
+ values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
+
+ // id is auto-generated by SQLite upon successful insertion of row
+ long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values);
+ return new PictureProfile.Builder(pp).setProfileId(Long.toString(id)).build();
+ }
+
+ @Override
+ public void updatePictureProfile(String id, PictureProfile pp) {
+ // TODO: implement
+ }
+ @Override
+ public void removePictureProfile(String id) {
// TODO: implement
- return pp;
}
+
@Override
- public PictureProfile getPictureProfileById(long id) {
+ public PictureProfile getPictureProfile(int type, String name) {
return null;
}
+
+ private String bundleToJson(Bundle bundle) {
+ JSONObject jsonObject = new JSONObject();
+ if (bundle == null) {
+ return jsonObject.toString();
+ }
+ for (String key : bundle.keySet()) {
+ try {
+ jsonObject.put(key, bundle.getString(key));
+ } catch (JSONException e) {
+ Log.e(TAG, "Unable to serialize ", e);
+ }
+ }
+ return jsonObject.toString();
+ }
+
+ private Bundle jsonToBundle(String jsonString) {
+ JSONObject jsonObject = null;
+ Bundle bundle = new Bundle();
+
+ try {
+ jsonObject = new JSONObject(jsonString);
+
+ Iterator<String> keys = jsonObject.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ Object value = jsonObject.get(key);
+
+ if (value instanceof String) {
+ bundle.putString(key, (String) value);
+ } else if (value instanceof Integer) {
+ bundle.putInt(key, (Integer) value);
+ } else if (value instanceof Boolean) {
+ bundle.putBoolean(key, (Boolean) value);
+ } else if (value instanceof Double) {
+ bundle.putDouble(key, (Double) value);
+ } else if (value instanceof Long) {
+ bundle.putLong(key, (Long) value);
+ }
+ }
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+
+ return bundle;
+ }
+
@Override
public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
return new ArrayList<>();
@@ -65,8 +154,104 @@ public class MediaQualityService extends SystemService {
return new ArrayList<>();
}
@Override
- public List<PictureProfile> getAvailableAllPictureProfiles() {
+ public List<String> getPictureProfilePackageNames() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public SoundProfile createSoundProfile(SoundProfile pp) {
+ // TODO: implement
+ return pp;
+ }
+ @Override
+ public void updateSoundProfile(String id, SoundProfile pp) {
+ // TODO: implement
+ }
+ @Override
+ public void removeSoundProfile(String id) {
+ // TODO: implement
+ }
+ @Override
+ public SoundProfile getSoundProfileById(String id) {
+ return null;
+ }
+ @Override
+ public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+ return new ArrayList<>();
+ }
+ @Override
+ public List<SoundProfile> getAvailableSoundProfiles() {
return new ArrayList<>();
}
+ @Override
+ public List<String> getSoundProfilePackageNames() {
+ return new ArrayList<>();
+ }
+
+
+ @Override
+ public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
+ }
+ @Override
+ public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
+ }
+
+ @Override
+ public void registerAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+ }
+
+ @Override
+ public void setAmbientBacklightSettings(AmbientBacklightSettings settings) {
+ }
+
+ @Override
+ public void setAmbientBacklightEnabled(boolean enabled) {
+ }
+
+ @Override
+ public List<ParamCapability> getParamCapabilities(List<String> names) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<String> getPictureProfileAllowList() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public void setPictureProfileAllowList(List<String> packages) {
+ }
+
+ @Override
+ public boolean isSupported() {
+ return false;
+ }
+
+ @Override
+ public void setAutoPictureQualityEnabled(boolean enabled) {
+ }
+
+ @Override
+ public boolean isAutoPictureQualityEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setSuperResolutionEnabled(boolean enabled) {
+ }
+
+ @Override
+ public boolean isSuperResolutionEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setAutoSoundQualityEnabled(boolean enabled) {
+ }
+
+ @Override
+ public boolean isAutoSoundQualityEnabled() {
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2a3be1e119bf..7de2815eba6b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -513,12 +513,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private boolean mLoadedRestrictBackground;
/**
- * Whether or not network for apps in proc-states greater than
- * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} is always blocked.
- */
- private boolean mBackgroundNetworkRestricted;
-
- /**
* Whether or not metered firewall chains should be used for uid policy controlling access to
* metered networks.
*/
@@ -1117,14 +1111,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
writePolicyAL();
}
- // The flag is boot-stable.
- mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove();
- if (mBackgroundNetworkRestricted) {
- // Firewall rules and UidBlockedState will get updated in
- // updateRulesForGlobalChangeAL below.
- enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true);
- }
-
+ enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true);
setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service");
updateRulesForGlobalChangeAL(false);
updateNotificationsNL();
@@ -1135,11 +1122,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
| ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_CAPABILITY;
-
- final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN
- : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes,
- cutpoint, "android");
+ PROCESS_STATE_UNKNOWN, "android");
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
@@ -1280,21 +1264,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// different chains may change.
return true;
}
- if (mBackgroundNetworkRestricted) {
- if ((previousProcState >= BACKGROUND_THRESHOLD_STATE)
+ if ((previousProcState >= BACKGROUND_THRESHOLD_STATE)
!= (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
- // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will
- // need to be re-evaluated for the background chain.
- return true;
- }
- if (mUseDifferentDelaysForBackgroundChain
- && newProcState >= BACKGROUND_THRESHOLD_STATE
- && getBackgroundTransitioningDelay(newProcState)
- < getBackgroundTransitioningDelay(previousProcState)) {
- // The old and new proc-state both are in the blocked state but the background
- // transition delay is reduced, so we may have to update the rules sooner.
- return true;
- }
+ // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will
+ // need to be re-evaluated for the background chain.
+ return true;
+ }
+ if (mUseDifferentDelaysForBackgroundChain
+ && newProcState >= BACKGROUND_THRESHOLD_STATE
+ && getBackgroundTransitioningDelay(newProcState)
+ < getBackgroundTransitioningDelay(previousProcState)) {
+ // The old and new proc-state both are in the blocked state but the background
+ // transition delay is reduced, so we may have to update the rules sooner.
+ return true;
}
final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
| PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
@@ -1367,9 +1349,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected
synchronized (mUidRulesFirstLock) {
updatePowerSaveAllowlistUL();
- if (mBackgroundNetworkRestricted) {
- updateRulesForBackgroundChainUL();
- }
+ updateRulesForBackgroundChainUL();
updateRulesForRestrictPowerUL();
updateRulesForAppIdleUL();
}
@@ -4100,8 +4080,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
fout.println();
fout.println("Flags:");
- fout.println(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE + ": "
- + mBackgroundNetworkRestricted);
fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": "
+ mUseMeteredFirewallChains);
fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": "
@@ -4251,35 +4229,33 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
fout.decreaseIndent();
}
- if (mBackgroundNetworkRestricted) {
+ fout.println();
+ if (mUseDifferentDelaysForBackgroundChain) {
+ fout.print("Background restrictions short delay: ");
+ TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout);
fout.println();
- if (mUseDifferentDelaysForBackgroundChain) {
- fout.print("Background restrictions short delay: ");
- TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout);
- fout.println();
- fout.print("Background restrictions long delay: ");
- TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout);
- fout.println();
- }
+ fout.print("Background restrictions long delay: ");
+ TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout);
+ fout.println();
+ }
- size = mBackgroundTransitioningUids.size();
- if (size > 0) {
- final long nowUptime = SystemClock.uptimeMillis();
- fout.println("Uids transitioning to background:");
- fout.increaseIndent();
- for (int i = 0; i < size; i++) {
- fout.print("UID=");
- fout.print(mBackgroundTransitioningUids.keyAt(i));
- fout.print(", ");
- TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i),
- nowUptime, fout);
- fout.println();
- }
- fout.decreaseIndent();
+ size = mBackgroundTransitioningUids.size();
+ if (size > 0) {
+ final long nowUptime = SystemClock.uptimeMillis();
+ fout.println("Uids transitioning to background:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mBackgroundTransitioningUids.keyAt(i));
+ fout.print(", ");
+ TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i),
+ nowUptime, fout);
+ fout.println();
}
- fout.println();
+ fout.decreaseIndent();
}
+ fout.println();
final SparseBooleanArray knownUids = new SparseBooleanArray();
collectKeys(mUidState, knownUids);
@@ -4465,51 +4441,49 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
updatePowerRestrictionRules = true;
}
- if (mBackgroundNetworkRestricted) {
- final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground(
- oldUidState);
- final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState);
- if (!wasAllowed && isAllowed) {
- mBackgroundTransitioningUids.delete(uid);
- updateRuleForBackgroundUL(uid);
- updatePowerRestrictionRules = true;
- } else if (!isAllowed) {
- final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid);
- final long completionTimeMs = SystemClock.uptimeMillis()
- + getBackgroundTransitioningDelay(procState);
- boolean completionTimeUpdated = false;
- if (wasAllowed) {
- // Rules need to transition from allowed to blocked after the respective
- // delay.
- if (transitionIdx < 0) {
- // This is just a defensive check in case the upstream code ever
- // makes multiple calls for the same process state change.
- mBackgroundTransitioningUids.put(uid, completionTimeMs);
- completionTimeUpdated = true;
- }
- } else if (mUseDifferentDelaysForBackgroundChain) {
- // wasAllowed was false, but the transition delay may have reduced.
- // Currently, this can happen when the uid transitions from
- // LAST_ACTIVITY to CACHED_ACTIVITY, for example.
- if (transitionIdx >= 0
- && completionTimeMs < mBackgroundTransitioningUids.valueAt(
- transitionIdx)) {
- mBackgroundTransitioningUids.setValueAt(transitionIdx,
- completionTimeMs);
- completionTimeUpdated = true;
- }
+ final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground(
+ oldUidState);
+ final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState);
+ if (!wasAllowed && isAllowed) {
+ mBackgroundTransitioningUids.delete(uid);
+ updateRuleForBackgroundUL(uid);
+ updatePowerRestrictionRules = true;
+ } else if (!isAllowed) {
+ final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid);
+ final long completionTimeMs = SystemClock.uptimeMillis()
+ + getBackgroundTransitioningDelay(procState);
+ boolean completionTimeUpdated = false;
+ if (wasAllowed) {
+ // Rules need to transition from allowed to blocked after the respective
+ // delay.
+ if (transitionIdx < 0) {
+ // This is just a defensive check in case the upstream code ever
+ // makes multiple calls for the same process state change.
+ mBackgroundTransitioningUids.put(uid, completionTimeMs);
+ completionTimeUpdated = true;
}
- if (completionTimeUpdated
- && completionTimeMs < mNextProcessBackgroundUidsTime) {
- // Many uids may be in this "transitioning" state at the same time,
- // so we always keep one message to process transition completion at
- // the earliest time.
- mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS);
- mHandler.sendEmptyMessageAtTime(
- MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs);
- mNextProcessBackgroundUidsTime = completionTimeMs;
+ } else if (mUseDifferentDelaysForBackgroundChain) {
+ // wasAllowed was false, but the transition delay may have reduced.
+ // Currently, this can happen when the uid transitions from
+ // LAST_ACTIVITY to CACHED_ACTIVITY, for example.
+ if (transitionIdx >= 0
+ && completionTimeMs < mBackgroundTransitioningUids.valueAt(
+ transitionIdx)) {
+ mBackgroundTransitioningUids.setValueAt(transitionIdx,
+ completionTimeMs);
+ completionTimeUpdated = true;
}
}
+ if (completionTimeUpdated
+ && completionTimeMs < mNextProcessBackgroundUidsTime) {
+ // Many uids may be in this "transitioning" state at the same time,
+ // so we always keep one message to process transition completion at
+ // the earliest time.
+ mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS);
+ mHandler.sendEmptyMessageAtTime(
+ MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs);
+ mNextProcessBackgroundUidsTime = completionTimeMs;
+ }
}
if (mLowPowerStandbyActive) {
boolean allowedInLpsChanged =
@@ -4545,12 +4519,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (mRestrictPower) {
updateRuleForRestrictPowerUL(uid);
}
- if (mBackgroundNetworkRestricted) {
- // Uid is no longer running, there is no point in any grace period of network
- // access during transitions to lower importance proc-states.
- mBackgroundTransitioningUids.delete(uid);
- updateRuleForBackgroundUL(uid);
- }
+ // Uid is no longer running, there is no point in any grace period of network
+ // access during transitions to lower importance proc-states.
+ mBackgroundTransitioningUids.delete(uid);
+ updateRuleForBackgroundUL(uid);
updateRulesForPowerRestrictionsUL(uid);
if (mLowPowerStandbyActive) {
updateRuleForLowPowerStandbyUL(uid);
@@ -5021,9 +4993,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
"updateRulesForGlobalChangeAL: " + (restrictedNetworksChanged ? "R" : "-"));
}
try {
- if (mBackgroundNetworkRestricted) {
- updateRulesForBackgroundChainUL();
- }
+ updateRulesForBackgroundChainUL();
updateRulesForAppIdleUL();
updateRulesForRestrictPowerUL();
updateRulesForRestrictBackgroundUL();
@@ -5183,9 +5153,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
updateRuleForDeviceIdleUL(uid);
updateRuleForRestrictPowerUL(uid);
- if (mBackgroundNetworkRestricted) {
- updateRuleForBackgroundUL(uid);
- }
+ updateRuleForBackgroundUL(uid);
// Update internal rules.
updateRulesForPowerRestrictionsUL(uid);
}
@@ -5358,9 +5326,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
updateRuleForDeviceIdleUL(uid);
updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
updateRuleForRestrictPowerUL(uid);
- if (mBackgroundNetworkRestricted) {
- updateRuleForBackgroundUL(uid);
- }
+ updateRuleForBackgroundUL(uid);
// If the uid has the necessary permissions, then it should be added to the restricted mode
// firewall allowlist.
@@ -5611,7 +5577,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0);
newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
- newBlockedReasons |= mBackgroundNetworkRestricted ? BLOCKED_REASON_APP_BACKGROUND : 0;
+ newBlockedReasons |= BLOCKED_REASON_APP_BACKGROUND;
newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
@@ -5624,8 +5590,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
& ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid))
? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0;
- newAllowedReasons |= (mBackgroundNetworkRestricted
- && isUidExemptFromBackgroundRestrictions(uid))
+ newAllowedReasons |= isUidExemptFromBackgroundRestrictions(uid)
? ALLOWED_REASON_NOT_IN_BACKGROUND : 0;
uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index 7f04e665567e..3c0ff6115fcd 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -2,13 +2,6 @@ package: "com.android.server.net"
container: "system"
flag {
- name: "network_blocked_for_top_sleeping_and_above"
- namespace: "backstage_power"
- description: "Block network access for apps in a low importance background state"
- bug: "304347838"
-}
-
-flag {
name: "use_metered_firewall_chains"
namespace: "backstage_power"
description: "Use metered firewall chains to control access to metered networks"
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 6681e36e00ee..5914dbe44b0b 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -25,8 +25,10 @@ import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.Notification.VISIBILITY_PUBLIC;
import static android.service.notification.Flags.notificationForceGrouping;
+import static android.service.notification.Flags.notificationRegroupOnClassification;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -49,6 +51,9 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -83,10 +88,22 @@ public class GroupHelper {
// with less than this value, they will be forced grouped
private static final int MIN_CHILD_COUNT_TO_AVOID_FORCE_GROUPING = 3;
+ // Regrouping needed because the channel was updated, ie. importance changed
+ static final int REGROUP_REASON_CHANNEL_UPDATE = 0;
+ // Regrouping needed because of notification bundling
+ static final int REGROUP_REASON_BUNDLE = 1;
+
+ @IntDef(prefix = { "REGROUP_REASON_" }, value = {
+ REGROUP_REASON_CHANNEL_UPDATE,
+ REGROUP_REASON_BUNDLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RegroupingReason {}
private final Callback mCallback;
private final int mAutoGroupAtCount;
private final int mAutogroupSparseGroupsAtCount;
+ private final int mAutoGroupRegroupingAtCount;
private final Context mContext;
private final PackageManager mPackageManager;
private boolean mIsTestHarnessExempted;
@@ -173,6 +190,11 @@ public class GroupHelper {
mContext = context;
mPackageManager = packageManager;
mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount;
+ if (notificationRegroupOnClassification()) {
+ mAutoGroupRegroupingAtCount = 1;
+ } else {
+ mAutoGroupRegroupingAtCount = mAutoGroupAtCount;
+ }
NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
}
@@ -766,6 +788,20 @@ public class GroupHelper {
return;
}
+ // Check if summary & child notifications are not part of the same section/bundle
+ // Needs a check here if notification was bundled while enqueued
+ if (notificationRegroupOnClassification()
+ && android.service.notification.Flags.notificationClassification()) {
+ if (isGroupChildBundled(record, summaryByGroupKey)) {
+ if (DEBUG) {
+ Slog.v(TAG, "isGroupChildInDifferentBundleThanSummary: " + record);
+ }
+ moveNotificationsToNewSection(record.getUserId(), pkgName,
+ List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey)));
+ return;
+ }
+ }
+
// scenario 3: sparse/singleton groups
if (Flags.notificationForceGroupSingletons()) {
try {
@@ -778,6 +814,27 @@ public class GroupHelper {
}
}
+ private static boolean isGroupChildBundled(final NotificationRecord record,
+ final Map<String, NotificationRecord> summaryByGroupKey) {
+ final StatusBarNotification sbn = record.getSbn();
+ final String groupKey = record.getSbn().getGroupKey();
+
+ if (!sbn.isAppGroup()) {
+ return false;
+ }
+
+ if (record.getNotification().isGroupSummary()) {
+ return false;
+ }
+
+ final NotificationRecord summary = summaryByGroupKey.get(groupKey);
+ if (summary == null) {
+ return false;
+ }
+
+ return NotificationChannel.SYSTEM_RESERVED_IDS.contains(record.getChannel().getId());
+ }
+
/**
* Called when a notification is removed, so that this helper can adjust the aggregate groups:
* - Removes the autogroup summary of the notification's section
@@ -865,7 +922,8 @@ public class GroupHelper {
}
}
- regroupNotifications(userId, pkgName, notificationsToCheck);
+ regroupNotifications(userId, pkgName, notificationsToCheck,
+ REGROUP_REASON_CHANNEL_UPDATE);
}
}
@@ -883,13 +941,14 @@ public class GroupHelper {
ArrayMap<String, NotificationRecord> notificationsToCheck = new ArrayMap<>();
notificationsToCheck.put(record.getKey(), record);
regroupNotifications(record.getUserId(), record.getSbn().getPackageName(),
- notificationsToCheck);
+ notificationsToCheck, REGROUP_REASON_BUNDLE);
}
}
@GuardedBy("mAggregatedNotifications")
private void regroupNotifications(int userId, String pkgName,
- ArrayMap<String, NotificationRecord> notificationsToCheck) {
+ ArrayMap<String, NotificationRecord> notificationsToCheck,
+ @RegroupingReason int regroupingReason) {
// The list of notification operations required after the channel update
final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
@@ -904,12 +963,42 @@ public class GroupHelper {
notificationsToMove.addAll(
getUngroupedNotificationsMoveOps(userId, pkgName, notificationsToCheck));
+ // Handle "grouped correctly" notifications that were re-classified (bundled)
+ if (notificationRegroupOnClassification()) {
+ if (regroupingReason == REGROUP_REASON_BUNDLE) {
+ notificationsToMove.addAll(
+ getReclassifiedNotificationsMoveOps(userId, pkgName, notificationsToCheck));
+ }
+ }
+
// Batch move to new section
if (!notificationsToMove.isEmpty()) {
moveNotificationsToNewSection(userId, pkgName, notificationsToMove);
}
}
+ private List<NotificationMoveOp> getReclassifiedNotificationsMoveOps(int userId,
+ String pkgName, ArrayMap<String, NotificationRecord> notificationsToCheck) {
+ final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
+ for (NotificationRecord record : notificationsToCheck.values()) {
+ if (isChildOfValidAppGroup(record)) {
+ // Check if section changes
+ NotificationSectioner sectioner = getSection(record);
+ if (sectioner != null) {
+ FullyQualifiedGroupKey newFullAggregateGroupKey =
+ new FullyQualifiedGroupKey(userId, pkgName, sectioner);
+ if (DEBUG) {
+ Slog.v(TAG, "Regroup after classification: " + record + " to: "
+ + newFullAggregateGroupKey);
+ }
+ notificationsToMove.add(
+ new NotificationMoveOp(record, null, newFullAggregateGroupKey));
+ }
+ }
+ }
+ return notificationsToMove;
+ }
+
@GuardedBy("mAggregatedNotifications")
private List<NotificationMoveOp> getAutogroupedNotificationsMoveOps(int userId, String pkgName,
ArrayMap<String, NotificationRecord> notificationsToCheck) {
@@ -1010,6 +1099,10 @@ public class GroupHelper {
// Bundled operations to apply to groups affected by the channel update
ArrayMap<FullyQualifiedGroupKey, GroupUpdateOp> groupsToUpdate = new ArrayMap<>();
+ // App-provided (valid) groups of notifications that were classified (bundled).
+ // Summaries will be canceled if all child notifications have been bundled.
+ ArrayMap<String, String> originalGroupsOfBundledNotifications = new ArrayMap<>();
+
for (NotificationMoveOp moveOp: notificationsToMove) {
final NotificationRecord record = moveOp.record;
final FullyQualifiedGroupKey oldFullAggregateGroupKey = moveOp.oldGroup;
@@ -1035,6 +1128,13 @@ public class GroupHelper {
groupsToUpdate.put(oldFullAggregateGroupKey,
new GroupUpdateOp(oldFullAggregateGroupKey, record, true));
}
+ } else {
+ if (notificationRegroupOnClassification()) {
+ // Null "old aggregate group" => this notification was re-classified from
+ // a valid app-provided group => maybe cancel the original summary
+ // if no children are left
+ originalGroupsOfBundledNotifications.put(record.getKey(), record.getGroupKey());
+ }
}
// Add moved notifications to the ungrouped list for new group and do grouping
@@ -1076,7 +1176,7 @@ public class GroupHelper {
NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
//Group needs to be created/updated
- if (ungrouped.size() >= mAutoGroupAtCount
+ if (ungrouped.size() >= mAutoGroupRegroupingAtCount
|| (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
NotificationSectioner sectioner = getSection(triggeringNotification);
if (sectioner == null) {
@@ -1092,6 +1192,18 @@ public class GroupHelper {
}
}
}
+
+ if (notificationRegroupOnClassification()) {
+ // Cancel the summary if it's the last notification of the original app-provided group
+ for (String triggeringKey : originalGroupsOfBundledNotifications.keySet()) {
+ NotificationRecord canceledSummary =
+ mCallback.removeAppProvidedSummaryOnClassification(triggeringKey,
+ originalGroupsOfBundledNotifications.getOrDefault(triggeringKey, null));
+ if (canceledSummary != null) {
+ cacheCanceledSummary(canceledSummary);
+ }
+ }
+ }
}
static String getFullAggregateGroupKey(String pkgName,
@@ -1113,6 +1225,42 @@ public class GroupHelper {
return (record.mOriginalFlags & Notification.FLAG_AUTOGROUP_SUMMARY) != 0;
}
+ private boolean isNotificationAggregatedInSection(NotificationRecord record,
+ NotificationSectioner sectioner) {
+ final FullyQualifiedGroupKey fullAggregateGroupKey = new FullyQualifiedGroupKey(
+ record.getUserId(), record.getSbn().getPackageName(), sectioner);
+ return record.getGroupKey().equals(fullAggregateGroupKey.toString());
+ }
+
+ private boolean isChildOfValidAppGroup(NotificationRecord record) {
+ final StatusBarNotification sbn = record.getSbn();
+ if (!sbn.isAppGroup()) {
+ return false;
+ }
+
+ if (!sbn.getNotification().isGroupChild()) {
+ return false;
+ }
+
+ if (record.isCanceled) {
+ return false;
+ }
+
+ final NotificationSectioner sectioner = getSection(record);
+ if (sectioner == null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Skipping autogrouping for " + record + " no valid section found.");
+ }
+ return false;
+ }
+
+ if (isNotificationAggregatedInSection(record, sectioner)) {
+ return false;
+ }
+
+ return true;
+ }
+
private static int getNumChildrenForGroup(@NonNull final String groupKey,
final List<NotificationRecord> notificationList) {
//TODO (b/349072751): track grouping state in GroupHelper -> do not use notificationList
@@ -1438,12 +1586,54 @@ public class GroupHelper {
}
}
+ protected void dump(PrintWriter pw, String prefix) {
+ synchronized (mAggregatedNotifications) {
+ if (!mUngroupedAbuseNotifications.isEmpty()) {
+ pw.println(prefix + "Ungrouped notifications:");
+ for (FullyQualifiedGroupKey groupKey: mUngroupedAbuseNotifications.keySet()) {
+ if (!mUngroupedAbuseNotifications.getOrDefault(groupKey, new ArrayMap<>())
+ .isEmpty()) {
+ pw.println(prefix + prefix + groupKey.toString());
+ for (String notifKey : mUngroupedAbuseNotifications.get(groupKey)
+ .keySet()) {
+ pw.println(prefix + prefix + prefix + notifKey);
+ }
+ }
+ }
+ pw.println("");
+ }
+
+ if (!mAggregatedNotifications.isEmpty()) {
+ pw.println(prefix + "Autogrouped notifications:");
+ for (FullyQualifiedGroupKey groupKey: mAggregatedNotifications.keySet()) {
+ if (!mAggregatedNotifications.getOrDefault(groupKey, new ArrayMap<>())
+ .isEmpty()) {
+ pw.println(prefix + prefix + groupKey.toString());
+ for (String notifKey : mAggregatedNotifications.get(groupKey).keySet()) {
+ pw.println(prefix + prefix + prefix + notifKey);
+ }
+ }
+ }
+ pw.println("");
+ }
+
+ if (!mCanceledSummaries.isEmpty()) {
+ pw.println(prefix + "Cached canceled summaries:");
+ for (CachedSummary summary: mCanceledSummaries.values()) {
+ pw.println(prefix + prefix + prefix + summary.key + " -> "
+ + summary.originalGroupKey);
+ }
+ pw.println("");
+ }
+ }
+ }
+
protected static class NotificationSectioner {
final String mName;
final int mSummaryId;
private final Predicate<NotificationRecord> mSectionChecker;
- public NotificationSectioner(String name, int summaryId,
+ private NotificationSectioner(String name, int summaryId,
Predicate<NotificationRecord> sectionChecker) {
mName = name;
mSummaryId = summaryId;
@@ -1551,5 +1741,16 @@ public class GroupHelper {
void removeNotificationFromCanceledGroup(int userId, String pkg, String groupKey,
int cancelReason);
+
+ /**
+ * Cancels the group summary of a notification that was regrouped because of classification
+ * (bundling). Only cancels if the summary is the last notification of the original group.
+ * @param triggeringKey the triggering child notification key
+ * @param groupKey the original group key
+ * @return the canceled group summary or null if the summary was not canceled
+ */
+ @Nullable
+ NotificationRecord removeAppProvidedSummaryOnClassification(String triggeringKey,
+ @Nullable String groupKey);
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 122836e19d58..b0ef80793cd7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,9 +21,6 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-import static android.content.pm.PackageManager.MATCH_INSTANT;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
@@ -78,9 +75,7 @@ import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerService.DumpFilter;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import org.xmlpull.v1.XmlPullParser;
@@ -109,8 +104,7 @@ abstract public class ManagedServices {
protected final String TAG = getClass().getSimpleName().replace('$', '.');
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
- protected static final int ON_BINDING_DIED_REBIND_MSG = 1234;
+ private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
private static final String DB_VERSION_1 = "1";
private static final String DB_VERSION_2 = "2";
@@ -140,7 +134,6 @@ abstract public class ManagedServices {
private final UserProfiles mUserProfiles;
protected final IPackageManager mPm;
protected final UserManager mUm;
- private final UserManagerInternal mUserManagerInternal;
private final Config mConfig;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -202,7 +195,6 @@ abstract public class ManagedServices {
mConfig = getConfig();
mApprovalLevel = APPROVAL_BY_COMPONENT;
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
}
abstract protected Config getConfig();
@@ -879,21 +871,7 @@ abstract public class ManagedServices {
String approvedItem = getApprovedValue(pkgOrComponent);
if (approvedItem != null) {
- final ComponentName component = ComponentName.unflattenFromString(approvedItem);
if (enabled) {
- if (Flags.notificationNlsRebind()) {
- if (component != null && !isValidService(component, userId)) {
- // Only fail if package is available
- // If not, it will be validated again in onPackagesChanged
- final PackageManager pm = mContext.getPackageManager();
- if (pm.isPackageAvailable(component.getPackageName())) {
- Slog.w(TAG, "Skip allowing " + mConfig.caption
- + " " + pkgOrComponent + " (userSet: " + userSet
- + ") for invalid service");
- return;
- }
- }
- }
approved.add(approvedItem);
} else {
approved.remove(approvedItem);
@@ -991,7 +969,7 @@ abstract public class ManagedServices {
|| isPackageOrComponentAllowed(component.getPackageName(), userId))) {
return false;
}
- return isValidService(component, userId);
+ return componentHasBindPermission(component, userId);
}
private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1238,21 +1216,12 @@ abstract public class ManagedServices {
if (!TextUtils.isEmpty(packageName)) {
queryIntent.setPackage(packageName);
}
-
- if (Flags.notificationNlsRebind()) {
- // Expand the package query
- extraFlags |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- extraFlags |= MATCH_INSTANT;
- }
-
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
queryIntent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA | extraFlags,
userId);
- if (DEBUG) {
- Slog.v(TAG, mConfig.serviceInterface + " pkg: " + packageName + " services: "
- + installedServices);
- }
+ if (DEBUG)
+ Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
if (installedServices != null) {
for (int i = 0, count = installedServices.size(); i < count; i++) {
ResolveInfo resolveInfo = installedServices.get(i);
@@ -1352,12 +1321,11 @@ abstract public class ManagedServices {
if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
final ComponentName component = ComponentName.unflattenFromString(
approvedPackageOrComponent);
- if (component != null && !isValidService(component, userId)) {
+ if (component != null && !componentHasBindPermission(component, userId)) {
approved.removeAt(j);
if (DEBUG) {
Slog.v(TAG, "Removing " + approvedPackageOrComponent
- + " from approved list; no bind permission or "
- + "service interface filter found "
+ + " from approved list; no bind permission found "
+ mConfig.bindPermission);
}
}
@@ -1376,15 +1344,6 @@ abstract public class ManagedServices {
}
}
- protected boolean isValidService(ComponentName component, int userId) {
- if (Flags.notificationNlsRebind()) {
- return componentHasBindPermission(component, userId) && queryPackageForServices(
- component.getPackageName(), userId).contains(component);
- } else {
- return componentHasBindPermission(component, userId);
- }
- }
-
protected boolean isValidEntry(String packageOrComponent, int userId) {
return hasMatchingServices(packageOrComponent, userId);
}
@@ -1426,14 +1385,9 @@ abstract public class ManagedServices {
@GuardedBy("mMutex")
protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
final IntArray activeUsers,
- SparseArray<ArraySet<ComponentName>> approvedComponentsByUser,
- boolean isVisibleBackgroundUser) {
- // When it is a visible background user in Automotive MUMD environment,
- // don't clear mEnabledServicesForCurrentProfile and mEnabledServicesPackageNames.
- if (!isVisibleBackgroundUser) {
- mEnabledServicesForCurrentProfiles.clear();
- mEnabledServicesPackageNames.clear();
- }
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
+ mEnabledServicesForCurrentProfiles.clear();
+ mEnabledServicesPackageNames.clear();
final int nUserIds = activeUsers.size();
for (int i = 0; i < nUserIds; ++i) {
@@ -1454,12 +1408,7 @@ abstract public class ManagedServices {
}
componentsToBind.put(userId, add);
- // When it is a visible background user in Automotive MUMD environment,
- // skip adding items to mEnabledServicesForCurrentProfile
- // and mEnabledServicesPackageNames.
- if (isVisibleBackgroundUser) {
- continue;
- }
+
mEnabledServicesForCurrentProfiles.addAll(userComponents);
for (int j = 0; j < userComponents.size(); j++) {
@@ -1507,10 +1456,7 @@ abstract public class ManagedServices {
IntArray userIds = mUserProfiles.getCurrentProfileIds();
boolean rebindAllCurrentUsers = mUserProfiles.isProfileUser(userToRebind, mContext)
&& allowRebindForParentUser();
- boolean isVisibleBackgroundUser = false;
if (userToRebind != USER_ALL && !rebindAllCurrentUsers) {
- isVisibleBackgroundUser =
- mUserManagerInternal.isVisibleBackgroundFullUser(userToRebind);
userIds = new IntArray(1);
userIds.add(userToRebind);
}
@@ -1525,8 +1471,7 @@ abstract public class ManagedServices {
// Filter approvedComponentsByUser to collect all of the components that are allowed
// for the currently active user(s).
- populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser,
- isVisibleBackgroundUser);
+ populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
// For every current non-system connection, disconnect services that are no longer
// approved, or ALL services if we are force rebinding
@@ -1542,27 +1487,23 @@ abstract public class ManagedServices {
* Called when user switched to unbind all services from other users.
*/
@VisibleForTesting
- void unbindOtherUserServices(int switchedToUser) {
+ void unbindOtherUserServices(int currentUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("ManagedServices.unbindOtherUserServices_current" + switchedToUser);
- unbindServicesImpl(switchedToUser, true /* allExceptUser */);
+ t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
+ unbindServicesImpl(currentUser, true /* allExceptUser */);
t.traceEnd();
}
- void unbindUserServices(int removedUser) {
+ void unbindUserServices(int user) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("ManagedServices.unbindUserServices" + removedUser);
- unbindServicesImpl(removedUser, false /* allExceptUser */);
+ t.traceBegin("ManagedServices.unbindUserServices" + user);
+ unbindServicesImpl(user, false /* allExceptUser */);
t.traceEnd();
}
void unbindServicesImpl(int user, boolean allExceptUser) {
final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
synchronized (mMutex) {
- if (Flags.notificationNlsRebind()) {
- // Remove enqueued rebinds to avoid rebinding services for a switched user
- mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
- }
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
if ((allExceptUser && (info.userid != user))
@@ -1757,7 +1698,6 @@ abstract public class ManagedServices {
mServicesRebinding.add(servicesBindingTag);
mHandler.postDelayed(() ->
reregisterService(name, userid),
- ON_BINDING_DIED_REBIND_MSG,
ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
Slog.v(TAG, getCaption() + " not rebinding in user " + userid
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 73fc02c6d867..207764b4e555 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -211,6 +211,7 @@ import android.app.RemoteServiceException.BadForegroundServiceNotificationExcept
import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException;
import android.app.StatsManager;
import android.app.UriGrantsManager;
+import android.app.ZenBypassingApp;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger;
@@ -238,7 +239,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.ModuleInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -474,6 +474,10 @@ public class NotificationManagerService extends SystemService {
Adjustment.KEY_TYPE
};
+ static final Integer[] DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES = new Integer[] {
+ TYPE_PROMOTION
+ };
+
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
RoleManager.ROLE_EMERGENCY
@@ -1929,6 +1933,12 @@ public class NotificationManagerService extends SystemService {
hasSensitiveContent, lifespanMs);
}
+ protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
+ int classification, int lifespanMs) {
+ FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_CLASSIFICATION,
+ hasPosted, isAlerting, classification, lifespanMs);
+ }
+
protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2998,6 +3008,16 @@ public class NotificationManagerService extends SystemService {
groupKey, REASON_APP_CANCEL, SystemClock.elapsedRealtime());
}
}
+
+ @Override
+ @Nullable
+ public NotificationRecord removeAppProvidedSummaryOnClassification(String triggeringKey,
+ @Nullable String oldGroupKey) {
+ synchronized (mNotificationLock) {
+ return removeAppProvidedSummaryOnClassificationLocked(triggeringKey,
+ oldGroupKey);
+ }
+ }
});
}
@@ -4023,7 +4043,7 @@ public class NotificationManagerService extends SystemService {
"canNotifyAsPackage for uid " + uid);
}
- return areNotificationsEnabledForPackageInt(pkg, uid);
+ return areNotificationsEnabledForPackageInt(uid);
}
/**
@@ -4189,6 +4209,22 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public @NonNull int[] getAllowedAdjustmentKeyTypes() {
+ checkCallerIsSystemOrSystemUiOrShell();
+ return mAssistants.getAllowedAdjustmentKeyTypes();
+ }
+
+ @Override
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void setAssistantAdjustmentKeyTypeState(int type, boolean enabled) {
+ checkCallerIsSystemOrSystemUiOrShell();
+ mAssistants.setAssistantAdjustmentKeyTypeState(type, enabled);
+
+ handleSavePolicyFile();
+ }
+
+ @Override
@FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
public boolean appCanBePromoted(String pkg, int uid) {
checkCallerIsSystemOrSystemUiOrShell();
@@ -4828,30 +4864,20 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public List<String> getPackagesBypassingDnd(int userId,
- boolean includeConversationChannels) {
+ public ParceledListSlice<ZenBypassingApp> getPackagesBypassingDnd(int userId)
+ throws RemoteException {
checkCallerIsSystem();
- final ArraySet<String> packageNames = new ArraySet<>();
-
- List<PackageInfo> pkgs = mPackageManagerClient.getInstalledPackagesAsUser(0, userId);
- for (PackageInfo pi : pkgs) {
- String pkg = pi.packageName;
- // If any NotificationChannel for this package is bypassing, the
- // package is considered bypassing.
- for (NotificationChannel channel : getNotificationChannelsBypassingDnd(pkg,
- pi.applicationInfo.uid).getList()) {
- // Skips non-demoted conversation channels.
- if (!includeConversationChannels
- && !TextUtils.isEmpty(channel.getConversationId())
- && !channel.isDemoted()) {
- continue;
- }
- packageNames.add(pkg);
+ UserHandle user = UserHandle.of(userId);
+ ArrayList<ZenBypassingApp> bypassing =
+ mPreferencesHelper.getPackagesBypassingDnd(userId);
+ for (int i = bypassing.size() - 1; i >= 0; i--) {
+ String pkg = bypassing.get(i).getPkg();
+ if (!areNotificationsEnabledForPackage(pkg, getUidForPackageAndUser(pkg, user))) {
+ bypassing.remove(i);
}
}
-
- return new ArrayList<String>(packageNames);
+ return new ParceledListSlice<>(bypassing);
}
@Override
@@ -6977,19 +7003,30 @@ public class NotificationManagerService extends SystemService {
if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
toRemove.add(potentialKey);
}
+ if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
+ if (!mAssistants.isAdjustmentKeyTypeAllowed(adjustments.getInt(KEY_TYPE))) {
+ toRemove.add(potentialKey);
+ }
+ }
}
for (String removeKey : toRemove) {
adjustments.remove(removeKey);
}
- if (android.service.notification.Flags.notificationClassification()
- && adjustments.containsKey(KEY_TYPE)) {
+ if (notificationClassification() && adjustments.containsKey(KEY_TYPE)) {
final NotificationChannel newChannel = getClassificationChannelLocked(r,
adjustments);
if (newChannel == null || newChannel.getId().equals(r.getChannel().getId())) {
adjustments.remove(KEY_TYPE);
} else {
+ // Save the app-provided type for logging.
+ int classification = adjustments.getInt(KEY_TYPE);
// swap app provided type with the real thing
adjustments.putParcelable(KEY_TYPE, newChannel);
+ // Note that this value of isAlerting does not fully indicate whether a notif
+ // would make a sound or HUN on device; it is an approximation for metrics.
+ boolean isAlerting = r.getChannel().getImportance() >= IMPORTANCE_DEFAULT;
+ logClassificationChannelAdjustmentReceived(isPosted, isAlerting, classification,
+ r.getLifespanMs(System.currentTimeMillis()));
}
}
r.addAdjustment(adjustment);
@@ -7114,6 +7151,50 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mNotificationLock")
+ @Nullable
+ NotificationRecord removeAppProvidedSummaryOnClassificationLocked(String triggeringKey,
+ @Nullable String oldGroupKey) {
+ NotificationRecord canceledSummary = null;
+ NotificationRecord r = mNotificationsByKey.get(triggeringKey);
+ if (r == null || oldGroupKey == null) {
+ return null;
+ }
+
+ if (r.getSbn().isAppGroup() && r.getNotification().isGroupChild()) {
+ NotificationRecord groupSummary = mSummaryByGroupKey.get(oldGroupKey);
+ // We only care about app-provided valid groups
+ if (groupSummary != null && !GroupHelper.isAggregatedGroup(groupSummary)) {
+ List<NotificationRecord> notificationsInGroup =
+ findGroupNotificationsLocked(r.getSbn().getPackageName(),
+ oldGroupKey, r.getUserId());
+ // Remove the app-provided summary if only the summary is left in the
+ // original group, or summary + triggering notification that will be
+ // regrouped
+ boolean isOnlySummaryLeft =
+ (notificationsInGroup.size() <= 1)
+ || (notificationsInGroup.size() == 2
+ && notificationsInGroup.contains(r)
+ && notificationsInGroup.contains(groupSummary));
+ if (isOnlySummaryLeft) {
+ if (DBG) {
+ Slog.i(TAG, "Removing app summary (all children bundled): "
+ + groupSummary);
+ }
+ canceledSummary = groupSummary;
+ mSummaryByGroupKey.remove(oldGroupKey);
+ cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(),
+ groupSummary.getSbn().getPackageName(),
+ groupSummary.getSbn().getTag(),
+ groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(),
+ NotificationListenerService.REASON_GROUP_OPTIMIZATION, null);
+ }
+ }
+ }
+
+ return canceledSummary;
+ }
+
+ @GuardedBy("mNotificationLock")
private boolean hasAutoGroupSummaryLocked(NotificationRecord record) {
final String autbundledGroupKey;
if (notificationForceGrouping()) {
@@ -7493,6 +7574,11 @@ public class NotificationManagerService extends SystemService {
mTtlHelper.dump(pw, " ");
}
}
+
+ if (notificationForceGrouping()) {
+ pw.println("\n GroupHelper:");
+ mGroupHelper.dump(pw, " ");
+ }
}
}
@@ -7667,7 +7753,7 @@ public class NotificationManagerService extends SystemService {
@Override
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
- return areNotificationsEnabledForPackageInt(pkg, uid);
+ return areNotificationsEnabledForPackageInt(uid);
}
@Override
@@ -7770,10 +7856,11 @@ public class NotificationManagerService extends SystemService {
// Make Notification silent
r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
- // Repost
+ // Repost as the original app (even if it was posted by a delegate originally
+ // because the delegate may now be revoked)
enqueueNotificationInternal(r.getSbn().getPackageName(),
- r.getSbn().getOpPkg(), r.getSbn().getUid(),
- r.getSbn().getInitialPid(), r.getSbn().getTag(),
+ r.getSbn().getPackageName(), r.getSbn().getUid(),
+ MY_PID, r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(),
r.getSbn().getUserId(), /* postSilently= */ true,
/* byForegroundService= */ false,
@@ -8012,7 +8099,6 @@ public class NotificationManagerService extends SystemService {
r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
r.setImportanceFixed(isImportanceFixed);
-
if (notification.isFgsOrUij()) {
if (((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
|| !channel.isUserVisibleTaskShown())
@@ -8646,7 +8732,7 @@ public class NotificationManagerService extends SystemService {
}
// blocked apps
- boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
+ boolean isBlocked = !areNotificationsEnabledForPackageInt(uid);
synchronized (mNotificationLock) {
isBlocked |= isRecordBlockedLocked(r);
}
@@ -8696,7 +8782,7 @@ public class NotificationManagerService extends SystemService {
}
}
- private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) {
+ private boolean areNotificationsEnabledForPackageInt(int uid) {
return mPermissionHelper.hasPermission(uid);
}
@@ -9232,7 +9318,7 @@ public class NotificationManagerService extends SystemService {
* notifying all listeners to a background thread; false otherwise.
*/
private boolean postNotification() {
- boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid);
+ boolean appBanned = !areNotificationsEnabledForPackageInt(uid);
boolean isCallNotification = isCallNotification(pkg, uid);
boolean posted = false;
synchronized (NotificationManagerService.this.mNotificationLock) {
@@ -11552,11 +11638,15 @@ public class NotificationManagerService extends SystemService {
private static final String ATT_TYPES = "types";
private static final String ATT_DENIED = "denied_adjustments";
+ private static final String ATT_ENABLED_TYPES = "enabled_key_types";
private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments";
private final Object mLock = new Object();
@GuardedBy("mLock")
+ private Set<Integer> mAllowedAdjustmentKeyTypes = new ArraySet<>();
+
+ @GuardedBy("mLock")
private Set<String> mAllowedAdjustments = new ArraySet<>();
@GuardedBy("mLock")
@@ -11639,6 +11729,8 @@ public class NotificationManagerService extends SystemService {
for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) {
mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]);
}
+ } else {
+ mAllowedAdjustmentKeyTypes.addAll(List.of(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES));
}
}
@@ -11726,6 +11818,42 @@ public class NotificationManagerService extends SystemService {
}
}
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ protected @NonNull boolean isAdjustmentKeyTypeAllowed(@Adjustment.Types int type) {
+ synchronized (mLock) {
+ if (notificationClassification()) {
+ return mAllowedAdjustmentKeyTypes.contains(type);
+ }
+ }
+ return false;
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ protected @NonNull int[] getAllowedAdjustmentKeyTypes() {
+ synchronized (mLock) {
+ if (notificationClassification()) {
+ return mAllowedAdjustmentKeyTypes.stream()
+ .mapToInt(Integer::intValue).toArray();
+ }
+ }
+ return new int[]{};
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void setAssistantAdjustmentKeyTypeState(@Adjustment.Types int type,
+ boolean enabled) {
+ if (!android.service.notification.Flags.notificationClassification()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (enabled) {
+ mAllowedAdjustmentKeyTypes.add(type);
+ } else {
+ mAllowedAdjustmentKeyTypes.remove(type);
+ }
+ }
+ }
+
protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
ArrayList<String> keys = new ArrayList<>(records.size());
@@ -12165,27 +12293,46 @@ public class NotificationManagerService extends SystemService {
@Override
protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
- if (!android.service.notification.Flags.notificationClassification()) {
+ if (!notificationClassification()) {
return;
}
synchronized (mLock) {
out.startTag(null, ATT_DENIED);
out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments));
out.endTag(null, ATT_DENIED);
+ out.startTag(null, ATT_ENABLED_TYPES);
+ out.attribute(null, ATT_TYPES,
+ TextUtils.join(",", mAllowedAdjustmentKeyTypes));
+ out.endTag(null, ATT_ENABLED_TYPES);
}
}
@Override
protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
- if (!android.service.notification.Flags.notificationClassification()) {
+ if (!notificationClassification()) {
return;
}
if (ATT_DENIED.equals(tag)) {
- final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+ final String keys = XmlUtils.readStringAttribute(parser, ATT_TYPES);
synchronized (mLock) {
mDeniedAdjustments.clear();
+ if (!TextUtils.isEmpty(keys)) {
+ mDeniedAdjustments.addAll(Arrays.asList(keys.split(",")));
+ }
+ }
+ } else if (ATT_ENABLED_TYPES.equals(tag)) {
+ final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+ synchronized (mLock) {
+ mAllowedAdjustmentKeyTypes.clear();
if (!TextUtils.isEmpty(types)) {
- mDeniedAdjustments.addAll(Arrays.asList(types.split(",")));
+ List<String> typeList = Arrays.asList(types.split(","));
+ for (String type : typeList) {
+ try {
+ mAllowedAdjustmentKeyTypes.add(Integer.parseInt(type));
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Bad type specified", e);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 9f0b4b0b6299..e6f784c71ef3 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -57,6 +57,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.ZenBypassingApp;
import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -1950,6 +1951,35 @@ public class PreferencesHelper implements RankingConfig {
}
/**
+ * Gets all apps that can bypass DND, and a boolean indicating whether all (true) or some
+ * (false) of its notification channels can currently bypass.
+ */
+ public @NonNull ArrayList<ZenBypassingApp> getPackagesBypassingDnd(@UserIdInt int userId) {
+ ArrayList<ZenBypassingApp> bypassing = new ArrayList<>();
+ synchronized (mLock) {
+ for (PackagePreferences p : mPackagePreferences.values()) {
+ if (p.userId != userId) {
+ continue;
+ }
+ int totalChannelCount = p.channels.size();
+ int bypassingCount = 0;
+ if (totalChannelCount == 0) {
+ continue;
+ }
+ for (NotificationChannel channel : p.channels.values()) {
+ if (channelIsLiveLocked(p, channel) && channel.canBypassDnd()) {
+ bypassingCount++;
+ }
+ }
+ if (bypassingCount > 0) {
+ bypassing.add(new ZenBypassingApp(p.pkg, totalChannelCount == bypassingCount));
+ }
+ }
+ }
+ return bypassing;
+ }
+
+ /**
* True for pre-O apps that only have the default channel, or pre O apps that have no
* channels yet. This method will create the default channel for pre-O apps that don't have it.
* Should never be true for O+ targeting apps, but that's enforced on boot/when an app
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 31c9ea4809c7..ca4f83fd46f6 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -2431,7 +2431,7 @@ public class ZenModeHelper {
|| (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
// call restrictions
final boolean muteCalls = zenAlarmsOnly
- || (zenPriorityOnly && !(allowCalls || allowRepeatCallers))
+ || (zenPriorityOnly && (!allowCalls || !allowRepeatCallers))
|| (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
// alarm restrictions
final boolean muteAlarms = zenPriorityOnly && !allowAlarms;
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index c479acfd6228..f79d9ef174ea 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -194,13 +194,3 @@ flag {
description: "Enables sound uri with vibration source in notification channel"
bug: "351975435"
}
-
-flag {
- name: "notification_nls_rebind"
- namespace: "systemui"
- description: "Check for NLS service intent filter when rebinding services"
- bug: "347674739"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 015b7fd74211..38f39393a025 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -19,6 +19,7 @@ package com.android.server.om;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
+import android.content.res.Flags;
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
@@ -162,11 +163,15 @@ public class OverlayActorEnforcer {
return ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE;
}
- if (targetOverlayable == null) {
+ // Framework doesn't have <overlayable> declaration by design, and we still want to be able
+ // to enable its overlays from the packages with the permission.
+ if (targetOverlayable == null
+ && !(Flags.rroControlForAndroidNoOverlayable() && targetPackageName.equals(
+ "android"))) {
return ActorState.MISSING_OVERLAYABLE;
}
- String actor = targetOverlayable.actor;
+ final String actor = targetOverlayable == null ? null : targetOverlayable.actor;
if (TextUtils.isEmpty(actor)) {
// If there's no actor defined, fallback to the legacy permission check
try {
diff --git a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
new file mode 100644
index 000000000000..8ec716077f46
--- /dev/null
+++ b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os.instrumentation;
+
+import static android.Manifest.permission.DYNAMIC_INSTRUMENTATION;
+import static android.content.Context.DYNAMIC_INSTRUMENTATION_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.PermissionManuallyEnforced;
+import android.content.Context;
+import android.os.instrumentation.ExecutableMethodFileOffsets;
+import android.os.instrumentation.IDynamicInstrumentationManager;
+import android.os.instrumentation.MethodDescriptor;
+import android.os.instrumentation.TargetProcess;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import dalvik.system.VMDebug;
+
+import java.lang.reflect.Method;
+
+/**
+ * System private implementation of the {@link IDynamicInstrumentationManager interface}.
+ */
+public class DynamicInstrumentationManagerService extends SystemService {
+ public DynamicInstrumentationManagerService(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(DYNAMIC_INSTRUMENTATION_SERVICE, new BinderService());
+ }
+
+ private final class BinderService extends IDynamicInstrumentationManager.Stub {
+ @Override
+ @PermissionManuallyEnforced
+ public @Nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets(
+ @NonNull TargetProcess targetProcess, @NonNull MethodDescriptor methodDescriptor) {
+ if (!com.android.art.flags.Flags.executableMethodFileOffsets()) {
+ throw new UnsupportedOperationException();
+ }
+ getContext().enforceCallingOrSelfPermission(
+ DYNAMIC_INSTRUMENTATION, "Caller must have DYNAMIC_INSTRUMENTATION permission");
+
+ if (targetProcess.processName == null
+ || !targetProcess.processName.equals("system_server")) {
+ throw new UnsupportedOperationException(
+ "system_server is the only supported target process");
+ }
+
+ Method method = parseMethodDescriptor(
+ getClass().getClassLoader(), methodDescriptor);
+ VMDebug.ExecutableMethodFileOffsets location =
+ VMDebug.getExecutableMethodFileOffsets(method);
+
+ if (location == null) {
+ return null;
+ }
+
+ ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets();
+ ret.containerPath = location.getContainerPath();
+ ret.containerOffset = location.getContainerOffset();
+ ret.methodOffset = location.getMethodOffset();
+ return ret;
+ }
+ }
+
+ @VisibleForTesting
+ static Method parseMethodDescriptor(ClassLoader classLoader,
+ @NonNull MethodDescriptor descriptor) {
+ try {
+ Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName);
+ Class<?>[] parameters = new Class[descriptor.fullyQualifiedParameters.length];
+ for (int i = 0; i < descriptor.fullyQualifiedParameters.length; i++) {
+ String typeName = descriptor.fullyQualifiedParameters[i];
+ boolean isArrayType = typeName.endsWith("[]");
+ if (isArrayType) {
+ typeName = typeName.substring(0, typeName.length() - 2);
+ }
+ switch (typeName) {
+ case "boolean":
+ parameters[i] = isArrayType ? boolean.class.arrayType() : boolean.class;
+ break;
+ case "byte":
+ parameters[i] = isArrayType ? byte.class.arrayType() : byte.class;
+ break;
+ case "char":
+ parameters[i] = isArrayType ? char.class.arrayType() : char.class;
+ break;
+ case "short":
+ parameters[i] = isArrayType ? short.class.arrayType() : short.class;
+ break;
+ case "int":
+ parameters[i] = isArrayType ? int.class.arrayType() : int.class;
+ break;
+ case "long":
+ parameters[i] = isArrayType ? long.class.arrayType() : long.class;
+ break;
+ case "float":
+ parameters[i] = isArrayType ? float.class.arrayType() : float.class;
+ break;
+ case "double":
+ parameters[i] = isArrayType ? double.class.arrayType() : double.class;
+ break;
+ default:
+ parameters[i] = isArrayType ? classLoader.loadClass(typeName).arrayType()
+ : classLoader.loadClass(typeName);
+ }
+ }
+
+ return javaClass.getDeclaredMethod(descriptor.methodName, parameters);
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ throw new IllegalArgumentException(
+ "The specified method cannot be found. Is this descriptor valid? "
+ + descriptor, e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 20cca969dade..d538bb876b64 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -265,6 +265,7 @@ public class BackgroundInstallControlService extends SystemService {
@Override
public void handleMessage(Message msg) {
+ Slog.d(TAG, "Package event received: " + msg.what);
switch (msg.what) {
case MSG_USAGE_EVENT_RECEIVED:
mService.handleUsageEvent(
@@ -326,6 +327,8 @@ public class BackgroundInstallControlService extends SystemService {
return;
}
+ Slog.d(TAG, "handlePackageAdd: adding " + packageName + " from "
+ + userId + " and notifying callbacks");
initBackgroundInstalledPackages();
mBackgroundInstalledPackages.add(userId, packageName);
mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_INSTALL);
@@ -364,7 +367,11 @@ public class BackgroundInstallControlService extends SystemService {
// ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
// addressed with b/265203007
private boolean installedByAdb(String initiatingPackageName) {
- return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName);
+ if(PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName)) {
+ Slog.d(TAG, "handlePackageAdd: is installed by ADB, skipping");
+ return true;
+ }
+ return false;
}
private boolean wasForegroundInstallation(
@@ -404,9 +411,12 @@ public class BackgroundInstallControlService extends SystemService {
void handlePackageRemove(String packageName, int userId) {
initBackgroundInstalledPackages();
+ if (mBackgroundInstalledPackages.contains(userId, packageName)) {
+ mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_UNINSTALL);
+ }
+ Slog.d(TAG, "handlePackageRemove: removing " + packageName + " from " + userId);
mBackgroundInstalledPackages.remove(userId, packageName);
writeBackgroundInstalledPackagesToDisk();
- mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_UNINSTALL);
}
void handleUsageEvent(UsageEvents.Event event, int userId) {
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
new file mode 100644
index 000000000000..745665bab5b3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.PackageLite;
+import android.os.OutcomeReceiver;
+
+import java.util.List;
+
+/**
+ * Helper class to interact with SDK Dependency Installer service.
+ */
+public class InstallDependencyHelper {
+ private final SharedLibrariesImpl mSharedLibraries;
+
+ InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) {
+ mSharedLibraries = sharedLibraries;
+ }
+
+ void resolveLibraryDependenciesIfNeeded(PackageLite pkg,
+ OutcomeReceiver<Void, PackageManagerException> callback) {
+ final List<SharedLibraryInfo> missing;
+ try {
+ missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
+ } catch (PackageManagerException e) {
+ callback.onError(e);
+ return;
+ }
+
+ if (missing.isEmpty()) {
+ // No need for dependency resolution. Move to installation directly.
+ callback.onResult(null);
+ return;
+ }
+
+ try {
+ bindToDependencyInstaller();
+ } catch (Exception e) {
+ PackageManagerException pe = new PackageManagerException(
+ INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage());
+ callback.onError(pe);
+ }
+ }
+
+ private void bindToDependencyInstaller() {
+ throw new IllegalStateException("Failed to bind to Dependency Installer");
+ }
+
+
+}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 355184e1c758..d9e76966892c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -85,6 +85,7 @@ import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
@@ -133,9 +134,11 @@ import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Message;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
+import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -174,7 +177,6 @@ import com.android.internal.util.CollectionUtils;
import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
import com.android.server.criticalevents.CriticalEventLog;
-import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -210,6 +212,10 @@ import java.util.concurrent.ExecutorService;
final class InstallPackageHelper {
+ // One minute over PM WATCHDOG_TIMEOUT
+ private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
+ private static final String INSTALLER_WAKE_LOCK_TAG = "installer:packages";
+
private final PackageManagerService mPm;
private final AppDataHelper mAppDataHelper;
private final BroadcastHelper mBroadcastHelper;
@@ -218,14 +224,16 @@ final class InstallPackageHelper {
private final IncrementalManager mIncrementalManager;
private final ApexManager mApexManager;
private final DexManager mDexManager;
- private final ArtManagerService mArtManagerService;
private final Context mContext;
- private final PackageDexOptimizer mPackageDexOptimizer;
private final PackageAbiHelper mPackageAbiHelper;
private final SharedLibrariesImpl mSharedLibraries;
private final PackageManagerServiceInjector mInjector;
private final UpdateOwnershipHelper mUpdateOwnershipHelper;
+ private final Object mInternalLock = new Object();
+ @GuardedBy("mInternalLock")
+ private PowerManager.WakeLock mInstallingWakeLock;
+
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm,
AppDataHelper appDataHelper,
@@ -241,9 +249,7 @@ final class InstallPackageHelper {
mIncrementalManager = pm.mInjector.getIncrementalManager();
mApexManager = pm.mInjector.getApexManager();
mDexManager = pm.mInjector.getDexManager();
- mArtManagerService = pm.mInjector.getArtManagerService();
mContext = pm.mInjector.getContext();
- mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
mPackageAbiHelper = pm.mInjector.getAbiHelper();
mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper();
@@ -1013,6 +1019,7 @@ final class InstallPackageHelper {
boolean success = false;
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
+ final long acquireTime = acquireWakeLock(requests.size());
try {
CriticalEventLog.getInstance().logInstallPackagesStarted();
if (prepareInstallPackages(requests)
@@ -1033,6 +1040,46 @@ final class InstallPackageHelper {
} finally {
completeInstallProcess(requests, createdAppId, success);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ releaseWakeLock(acquireTime, requests.size());
+ }
+ }
+
+ private long acquireWakeLock(int count) {
+ if (!mPm.isSystemReady()) {
+ return -1;
+ }
+ synchronized (mInternalLock) {
+ if (mInstallingWakeLock == null) {
+ PowerManager pwm = mContext.getSystemService(PowerManager.class);
+ if (pwm != null) {
+ mInstallingWakeLock = pwm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ INSTALLER_WAKE_LOCK_TAG);
+ } else {
+ Slog.w(TAG, "Unable to obtain power manager while obtaining wake lock");
+ return -1;
+ }
+ }
+
+ mInstallingWakeLock.acquire(WAKELOCK_TIMEOUT_MS * count);
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ private void releaseWakeLock(final long acquireTime, int count) {
+ if (acquireTime < 0) {
+ return;
+ }
+ synchronized (mInternalLock) {
+ try {
+ if (mInstallingWakeLock == null) {
+ return;
+ }
+ if (mInstallingWakeLock.isHeld()) {
+ mInstallingWakeLock.release();
+ }
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Error while releasing installer lock", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5653da07779b..2c0942337b1f 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -88,6 +88,7 @@ import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
+import android.database.ContentObserver;
import android.graphics.Rect;
import android.multiuser.Flags;
import android.net.Uri;
@@ -95,6 +96,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IInterface;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -249,6 +251,7 @@ public class LauncherAppsService extends SystemService {
private PackageInstallerService mPackageInstallerService;
final LauncherAppsServiceInternal mInternal;
+ private SecureSettingsObserver mSecureSettingsObserver;
@NonNull
private final RemoteCallbackList<IDumpCallback> mDumpCallbacks =
@@ -278,6 +281,7 @@ public class LauncherAppsService extends SystemService {
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
mInternal = new LocalService();
+ registerSettingsObserver();
}
@VisibleForTesting
@@ -2312,6 +2316,13 @@ public class LauncherAppsService extends SystemService {
}
}
+ void registerSettingsObserver() {
+ if (Flags.addLauncherUserConfig()) {
+ mSecureSettingsObserver = new SecureSettingsObserver();
+ mSecureSettingsObserver.register();
+ }
+ }
+
public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
private final UserManagerInternal mUserManagerInternal;
@@ -2837,5 +2848,84 @@ public class LauncherAppsService extends SystemService {
shortcutId, sourceBounds, startActivityOptions, targetUserId);
}
}
+
+ class SecureSettingsObserver extends ContentObserver {
+
+ SecureSettingsObserver() {
+ super(new Handler(Looper.getMainLooper()));
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (uri.equals(
+ Settings.Secure.getUriFor(Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT))) {
+
+ // This setting key only apply to private profile at the moment
+ UserHandle privateProfile = getPrivateProfile();
+ if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
+ return;
+ }
+
+ final int n = mListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ final IOnAppsChangedListener listener =
+ mListeners.getBroadcastItem(i);
+ final BroadcastCookie cookie =
+ (BroadcastCookie) mListeners.getBroadcastCookie(
+ i);
+ if (!isEnabledProfileOf(cookie, privateProfile,
+ "onSecureSettingsChange")) {
+ Log.d(TAG, "onSecureSettingsChange: Skipping - profile not enabled"
+ + " or not accessible for package=" + cookie.packageName
+ + ", packageUid=" + cookie.callingUid);
+ } else {
+ try {
+ Log.d(TAG,
+ "onUserConfigChanged: triggering onUserConfigChanged");
+ listener.onUserConfigChanged(
+ mUserManagerInternal.getLauncherUserInfo(
+ privateProfile.getIdentifier()));
+ } catch (RemoteException re) {
+ Slog.d(TAG, "onUserConfigChanged: Callback failed ", re);
+ }
+ }
+ }
+ } finally {
+ mListeners.finishBroadcast();
+ }
+ }
+ }
+
+ public void register() {
+ UserHandle privateProfile = getPrivateProfile();
+ int parentUserId;
+ if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
+ // No private space available, register the observer for the current user
+ parentUserId = mContext.getUserId();
+ } else {
+ parentUserId = mUserManagerInternal.getProfileParentId(
+ privateProfile.getIdentifier());
+ }
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT),
+ true, this, parentUserId);
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ private UserHandle getPrivateProfile() {
+ UserInfo[] userInfos = mUserManagerInternal.getUserInfos();
+ for (UserInfo u : userInfos) {
+ if (u.isPrivateProfile()) {
+ return UserHandle.of(u.id);
+ }
+ }
+ return UserHandle.of(UserHandle.USER_NULL);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index ef0997696cd7..eb70748918b6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -220,6 +220,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
private AppOpsManager mAppOps;
private final VerifierController mVerifierController;
+ private final InstallDependencyHelper mInstallDependencyHelper;
private final HandlerThread mInstallThread;
private final Handler mInstallHandler;
@@ -346,6 +347,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
synchronized (mVerificationPolicyPerUser) {
mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY);
}
+ mInstallDependencyHelper = new InstallDependencyHelper(
+ mPm.mInjector.getSharedLibrariesImpl());
LocalServices.getService(SystemServiceManager.class).startService(
new Lifecycle(context, this));
@@ -543,7 +546,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
session = PackageInstallerSession.readFromXml(in, mInternalCallback,
mContext, mPm, mInstallThread.getLooper(), mStagingManager,
mSessionsDir, this, mSilentUpdatePolicy,
- mVerifierController);
+ mVerifierController, mInstallDependencyHelper);
} catch (Exception e) {
Slog.e(TAG, "Could not read session", e);
continue;
@@ -1065,7 +1068,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, PackageManager.INSTALL_UNKNOWN, "", null,
- mVerifierController, verificationPolicy, verificationPolicy);
+ mVerifierController, verificationPolicy, verificationPolicy,
+ mInstallDependencyHelper);
synchronized (mSessions) {
mSessions.put(sessionId, session);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2a92de57446d..e156b31c19e1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -145,6 +145,7 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.PersistableBundle;
@@ -433,6 +434,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private final StagingManager mStagingManager;
@NonNull private final VerifierController mVerifierController;
+ private final InstallDependencyHelper mInstallDependencyHelper;
+
final int sessionId;
final int userId;
final SessionParams params;
@@ -1188,7 +1191,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
String sessionErrorMessage, DomainSet preVerifiedDomains,
@NonNull VerifierController verifierController,
@PackageInstaller.VerificationPolicy int initialVerificationPolicy,
- @PackageInstaller.VerificationPolicy int currentVerificationPolicy) {
+ @PackageInstaller.VerificationPolicy int currentVerificationPolicy,
+ InstallDependencyHelper installDependencyHelper) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -1200,6 +1204,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mVerifierController = verifierController;
mInitialVerificationPolicy = initialVerificationPolicy;
mCurrentVerificationPolicy = new AtomicInteger(currentVerificationPolicy);
+ mInstallDependencyHelper = installDependencyHelper;
this.sessionId = sessionId;
this.userId = userId;
@@ -1424,6 +1429,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
info.packageSource = params.packageSource;
info.applicationEnabledSettingPersistent = params.applicationEnabledSettingPersistent;
info.pendingUserActionReason = userActionRequirementToReason(mUserActionRequirement);
+ info.isAutoInstallingDependenciesEnabled = params.isAutoInstallDependenciesEnabled;
}
return info;
}
@@ -2611,6 +2617,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
maybeFinishChildSessions(error, msg);
}
+ private void onSessionDependencyResolveFailure(int error, String msg) {
+ Slog.e(TAG, "Failed to resolve dependency for session " + sessionId);
+ // Dispatch message to remove session from PackageInstallerService.
+ dispatchSessionFinished(error, msg, null);
+ maybeFinishChildSessions(error, msg);
+ }
+
private void onSystemDataLoaderUnrecoverable() {
final String packageName = getPackageName();
if (TextUtils.isEmpty(packageName)) {
@@ -3402,7 +3415,36 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/* extras= */ null, /* forPreapproval= */ false);
return;
}
- install();
+
+ if (Flags.sdkDependencyInstaller()
+ && params.isAutoInstallDependenciesEnabled
+ && !isMultiPackage()) {
+ resolveLibraryDependenciesIfNeeded();
+ } else {
+ install();
+ }
+ }
+
+
+ private void resolveLibraryDependenciesIfNeeded() {
+ synchronized (mLock) {
+ // TODO(b/372862145): Callback should be called on a handler passed as parameter
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite,
+ new OutcomeReceiver<>() {
+
+ @Override
+ public void onResult(Void result) {
+ install();
+ }
+
+ @Override
+ public void onError(@NonNull PackageManagerException e) {
+ final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+ setSessionFailed(e.error, completeMsg);
+ onSessionDependencyResolveFailure(e.error, completeMsg);
+ }
+ });
+ }
}
/**
@@ -6048,7 +6090,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@NonNull StagingManager stagingManager, @NonNull File sessionsDir,
@NonNull PackageSessionProvider sessionProvider,
@NonNull SilentUpdatePolicy silentUpdatePolicy,
- @NonNull VerifierController verifierController)
+ @NonNull VerifierController verifierController,
+ @NonNull InstallDependencyHelper installDependencyHelper)
throws IOException, XmlPullParserException {
final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID);
final int userId = in.getAttributeInt(null, ATTR_USER_ID);
@@ -6257,6 +6300,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController,
- initialVerificationPolicy, currentVerificationPolicy);
+ initialVerificationPolicy, currentVerificationPolicy, installDependencyHelper);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index a28e3c142220..52e8c52fe6af 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -38,6 +38,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
@@ -45,7 +46,8 @@ import java.util.function.BiFunction;
/** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly
* used by PackageMonitor to improve the broadcast latency. */
-class PackageMonitorCallbackHelper {
+@VisibleForTesting
+public class PackageMonitorCallbackHelper {
private static final boolean DEBUG = false;
private static final String TAG = "PackageMonitorCallbackHelper";
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 929fccce5265..fc54f6864db0 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -33,6 +33,7 @@ import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.PackageLite;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -83,6 +84,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
private static final boolean DEBUG_SHARED_LIBRARIES = false;
private static final String LIBRARY_TYPE_SDK = "sdk";
+ private static final String LIBRARY_TYPE_STATIC = "static shared";
/**
* Apps targeting Android S and above need to declare dependencies to the public native
@@ -926,18 +928,19 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
if (!pkg.getUsesLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, null,
pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
- availablePackages, newLibraries);
+ availablePackages, newLibraries, null);
}
if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
- null, pkg.getPackageName(), "static shared", true,
- pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries);
+ null, pkg.getPackageName(), LIBRARY_TYPE_STATIC, true,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries,
+ null);
}
if (!pkg.getUsesOptionalLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
null, pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, newLibraries);
+ usesLibraryInfos, availablePackages, newLibraries, null);
}
if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
pkg.getPackageName(), pkg.getTargetSdkVersion())) {
@@ -945,13 +948,13 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
null, null, pkg.getPackageName(), "native shared", true,
pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
- newLibraries);
+ newLibraries, null);
}
if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
null, null, null, pkg.getPackageName(), "native shared", false,
pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
- newLibraries);
+ newLibraries, null);
}
}
if (!pkg.getUsesSdkLibraries().isEmpty()) {
@@ -961,11 +964,34 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
pkg.getUsesSdkLibrariesOptional(),
pkg.getPackageName(), LIBRARY_TYPE_SDK, required, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, newLibraries);
+ usesLibraryInfos, availablePackages, newLibraries, null);
}
return usesLibraryInfos;
}
+ List<SharedLibraryInfo> collectMissingSharedLibraryInfos(PackageLite pkgLite)
+ throws PackageManagerException {
+ ArrayList<SharedLibraryInfo> missingSharedLibrary = new ArrayList<>();
+ synchronized (mPm.mLock) {
+ collectSharedLibraryInfos(pkgLite.getUsesSdkLibraries(),
+ pkgLite.getUsesSdkLibrariesVersionsMajor(),
+ pkgLite.getUsesSdkLibrariesCertDigests(),
+ /*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_SDK,
+ /*required=*/ true, pkgLite.getTargetSdk(),
+ /*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null,
+ missingSharedLibrary);
+
+ collectSharedLibraryInfos(pkgLite.getUsesStaticLibraries(),
+ pkgLite.getUsesStaticLibrariesVersions(),
+ pkgLite.getUsesStaticLibrariesCertDigests(),
+ /*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_STATIC,
+ /*required=*/ true, pkgLite.getTargetSdk(),
+ /*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null,
+ missingSharedLibrary);
+ }
+ return missingSharedLibrary;
+ }
+
private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@@ -973,7 +999,8 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
@NonNull String packageName, @NonNull String libraryType, boolean required,
int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
- @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
+ @Nullable final List<SharedLibraryInfo> outMissingSharedLibraryInfos)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
@@ -986,16 +1013,33 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
libName, libVersion, mSharedLibraries, newLibraries);
}
if (libraryInfo == null) {
- // Only allow app be installed if the app specifies the sdk-library dependency is
- // optional
- if (required || (LIBRARY_TYPE_SDK.equals(libraryType) && (libsOptional != null
- && !libsOptional[i]))) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library " + libName + "; failing!");
- } else if (DEBUG_SHARED_LIBRARIES) {
- Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
- + " library " + libName + "; ignoring!");
+ if (required) {
+ boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK)
+ || libraryType.equals(LIBRARY_TYPE_STATIC);
+ if (isSdkOrStatic && outMissingSharedLibraryInfos != null) {
+ // TODO(b/372862145): Pass the CertDigest too
+ // If Dependency Installation is supported, try that instead of failing.
+ SharedLibraryInfo missingLibrary = new SharedLibraryInfo(
+ libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE
+ );
+ outMissingSharedLibraryInfos.add(missingLibrary);
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + "; failing!");
+ }
+ } else {
+ // Only allow app be installed if the app specifies the sdk-library
+ // dependency is optional
+ boolean isOptional = libsOptional != null && libsOptional[i];
+ if (LIBRARY_TYPE_SDK.equals(libraryType) && !isOptional) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + "; failing!");
+ } else if (DEBUG_SHARED_LIBRARIES) {
+ Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
+ + " library " + libName + "; ignoring!");
+ }
}
} else {
if (requiredVersions != null && requiredCertDigests != null) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7ecfe7f64ffe..06e29c2c1408 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_SCREEN_ON;
import static android.content.Intent.EXTRA_USER_ID;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.LauncherUserInfo.PRIVATE_SPACE_ENTRYPOINT_HIDDEN;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -32,6 +33,7 @@ import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
import static android.os.UserManager.USER_OPERATION_ERROR_USER_RESTRICTED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
import static com.android.internal.app.SetScreenLockDialogActivity.EXTRA_ORIGIN_USER_ID;
import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_DISABLE_QUIET_MODE;
@@ -341,6 +343,10 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TRON_USER_CREATED = "users_user_created";
private static final String TRON_DEMO_CREATED = "users_demo_created";
+ // The boot user strategy for HSUM.
+ private static final int BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER = 0;
+ private static final int BOOT_TO_HSU_FOR_PROVISIONED_DEVICE = 1;
+
private final Context mContext;
private final PackageManagerService mPm;
@@ -1391,37 +1397,77 @@ public class UserManagerService extends IUserManager.Stub {
}
if (isHeadlessSystemUserMode()) {
- if (mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser)) {
- return UserHandle.USER_SYSTEM;
- }
- // Return the previous foreground user, if there is one.
- final int previousUser = getPreviousFullUserToEnterForeground();
- if (previousUser != UserHandle.USER_NULL) {
- Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
- return previousUser;
+ final int bootStrategy = mContext.getResources()
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
+ switch (bootStrategy) {
+ case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
+ return getPreviousOrFirstSwitchableUser();
+ case BOOT_TO_HSU_FOR_PROVISIONED_DEVICE:
+ return getBootUserBasedOnProvisioning();
+ default:
+ Slogf.w(LOG_TAG, "Unknown HSUM boot strategy: %d", bootStrategy);
+ return getPreviousOrFirstSwitchableUser();
}
- // No previous user. Return the first switchable user if there is one.
- synchronized (mUsersLock) {
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- final UserData userData = mUsers.valueAt(i);
- if (userData.info.supportsSwitchToByUser()) {
- int firstSwitchable = userData.info.id;
- Slogf.i(LOG_TAG,
- "Boot user is first switchable user %d", firstSwitchable);
- return firstSwitchable;
- }
- }
- }
- // No switchable users found. Uh oh!
- throw new UserManager.CheckedUserOperationException(
- "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
}
// Not HSUM, return system user.
return UserHandle.USER_SYSTEM;
}
+ private @UserIdInt int getBootUserBasedOnProvisioning()
+ throws UserManager.CheckedUserOperationException {
+ final boolean provisioned = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ if (provisioned) {
+ return UserHandle.USER_SYSTEM;
+ } else {
+ final int firstSwitchableFullUser = getFirstSwitchableUser(true);
+ if (firstSwitchableFullUser != UserHandle.USER_NULL) {
+ Slogf.i(LOG_TAG,
+ "Boot user is first switchable full user %d",
+ firstSwitchableFullUser);
+ return firstSwitchableFullUser;
+ }
+ // No switchable full user found. Uh oh!
+ throw new UserManager.CheckedUserOperationException(
+ "No switchable full user found", USER_OPERATION_ERROR_UNKNOWN);
+ }
+ }
+
+ private @UserIdInt int getPreviousOrFirstSwitchableUser()
+ throws UserManager.CheckedUserOperationException {
+ // Return the previous foreground user, if there is one.
+ final int previousUser = getPreviousFullUserToEnterForeground();
+ if (previousUser != UserHandle.USER_NULL) {
+ Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
+ return previousUser;
+ }
+ // No previous user. Return the first switchable user if there is one.
+ final int firstSwitchableUser = getFirstSwitchableUser(false);
+ if (firstSwitchableUser != UserHandle.USER_NULL) {
+ Slogf.i(LOG_TAG,
+ "Boot user is first switchable user %d", firstSwitchableUser);
+ return firstSwitchableUser;
+ }
+ // No switchable users found. Uh oh!
+ throw new UserManager.CheckedUserOperationException(
+ "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
+ }
+
+ private @UserIdInt int getFirstSwitchableUser(boolean fullUserOnly) {
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ final UserData userData = mUsers.valueAt(i);
+ if (userData.info.supportsSwitchToByUser() &&
+ (!fullUserOnly || userData.info.isFull())) {
+ int firstSwitchable = userData.info.id;
+ return firstSwitchable;
+ }
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
@Override
public int getPreviousFullUserToEnterForeground() {
@@ -7902,11 +7948,25 @@ public class UserManagerService extends IUserManager.Stub {
}
if (userInfo != null) {
final UserTypeDetails userDetails = getUserTypeDetails(userInfo);
- final LauncherUserInfo uiInfo = new LauncherUserInfo.Builder(
- userDetails.getName(),
- userInfo.serialNumber)
- .build();
- return uiInfo;
+
+ if (Flags.addLauncherUserConfig()) {
+ Bundle config = new Bundle();
+ if (userInfo.isPrivateProfile()) {
+ try {
+ int parentId = getProfileParentIdUnchecked(userId);
+ config.putBoolean(PRIVATE_SPACE_ENTRYPOINT_HIDDEN,
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ HIDE_PRIVATESPACE_ENTRY_POINT, parentId) == 1);
+ } catch (Settings.SettingNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new LauncherUserInfo.Builder(userDetails.getName(),
+ userInfo.serialNumber, config).build();
+ }
+
+ return new LauncherUserInfo.Builder(userDetails.getName(),
+ userInfo.serialNumber).build();
} else {
return null;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 07fd1cb544f6..09feb18d07bf 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -36,6 +36,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
+import android.health.connect.HealthPermissions;
import android.media.RingtoneManager;
import android.media.midi.MidiManager;
import android.net.Uri;
@@ -48,6 +49,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
+import android.permission.flags.Flags;
import android.print.PrintManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -64,6 +66,7 @@ import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
@@ -213,8 +216,13 @@ final class DefaultPermissionGrantPolicy {
private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
static {
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ if (Flags.replaceBodySensorPermissionEnabled()) {
+ SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEART_RATE);
+ SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
+ } else {
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ }
}
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
@@ -235,6 +243,7 @@ final class DefaultPermissionGrantPolicy {
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.UWB_RANGING);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
+ NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.RANGING);
}
private static final Set<String> NOTIFICATION_PERMISSIONS = new ArraySet<>();
@@ -1631,6 +1640,14 @@ final class DefaultPermissionGrantPolicy {
continue;
}
+ // If the trunkstable feature flag is disabled for this
+ // exception, skip the tag.
+ if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(
+ /* pkg= */ null, parser, /* allowNoNamespace= */ true)) {
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
final boolean fixed =
parser.getAttributeBoolean(null, ATTR_FIXED, false);
final boolean whitelisted =
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5fc3e332b95c..05bc69a9f1f0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1015,7 +1015,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
permission, attributionSource, message, forDataDelivery, startDataDelivery,
fromDatasource, attributedOp);
// Finish any started op if some step in the attribution chain failed.
- if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
+ if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED
+ && result != PermissionChecker.PERMISSION_SOFT_DENIED) {
if (attributedOp == AppOpsManager.OP_NONE) {
finishDataDelivery(AppOpsManager.permissionToOpCode(permission),
attributionSource.asState(), fromDatasource);
@@ -1244,6 +1245,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final boolean hasChain = attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
AttributionSource current = attributionSource;
AttributionSource next = null;
+ AttributionSource prev = null;
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
@@ -1310,6 +1312,22 @@ public class PermissionManagerService extends IPermissionManager.Stub {
selfAccess, singleReceiverFromDatasource, attributedOp,
proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
+ if (startDataDelivery && opMode != AppOpsManager.MODE_ALLOWED) {
+ // Current failed the perm check, so if we are part-way through an attr chain,
+ // we need to clean up the already started proxy op higher up the chain. Note,
+ // proxy ops are verified two by two, which means we have to clear the 2nd next
+ // from the previous iteration (since it is actually curr.next which failed
+ // to pass the perm check).
+ if (prev != null) {
+ final var cutAttrSourceState = prev.asState();
+ if (cutAttrSourceState.next.length > 0) {
+ cutAttrSourceState.next[0].next = new AttributionSourceState[0];
+ }
+ finishDataDelivery(context, attributedOp,
+ cutAttrSourceState, fromDatasource);
+ }
+ }
+
switch (opMode) {
case AppOpsManager.MODE_ERRORED: {
if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
@@ -1335,6 +1353,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionChecker.PERMISSION_GRANTED;
}
+ // an attribution we have already possibly started an op for
+ prev = current;
current = next;
}
}
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index a1236e533beb..4f67318faddb 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
import android.hardware.input.AppLaunchData;
+import android.hardware.input.InputGestureData;
import android.hardware.input.KeyGestureEvent;
import android.os.Handler;
import android.os.RemoteException;
@@ -769,6 +770,30 @@ public class ModifierShortcutManager {
shortcuts);
}
+ /**
+ * @return a {@link KeyboardShortcutGroup} containing the application launch keyboard
+ * shortcuts based on provided list of shortcut data.
+ */
+ public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId,
+ List<InputGestureData> shortcutData) {
+ List<KeyboardShortcutInfo> shortcuts = new ArrayList<>();
+ KeyCharacterMap kcm = KeyCharacterMap.load(deviceId);
+ for (InputGestureData data : shortcutData) {
+ if (data.getTrigger() instanceof InputGestureData.KeyTrigger trigger) {
+ KeyboardShortcutInfo info = shortcutInfoFromIntent(
+ kcm.getDisplayLabel(trigger.getKeycode()),
+ getIntentFromAppLaunchData(data.getAction().appLaunchData()),
+ (trigger.getModifierState() & KeyEvent.META_SHIFT_ON) != 0);
+ if (info != null) {
+ shortcuts.add(info);
+ }
+ }
+ }
+ return new KeyboardShortcutGroup(
+ mContext.getString(R.string.keyboard_shortcut_group_applications),
+ shortcuts);
+ }
+
private Intent getIntentFromAppLaunchData(@NonNull AppLaunchData data) {
Context context = mContext.createContextAsUser(mCurrentUser, 0);
synchronized (mAppIntentCache) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2893430572d8..dda5bcf24d07 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -83,11 +83,11 @@ import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
-import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
+import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
+import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.modifierShortcutDump;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
-import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
@@ -183,6 +183,7 @@ import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.Secure;
+import android.service.SensorPrivacyToggleSourceProto;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
@@ -2494,7 +2495,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void initKeyCombinationRules() {
mKeyCombinationManager = new KeyCombinationManager(mHandler);
- if (useKeyGestureEventHandler() && useKeyGestureEventHandlerMultiPressGestures()) {
+ if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
return;
}
final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
@@ -3414,6 +3415,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
+ if (useKeyGestureEventHandler()) {
+ return mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(deviceId,
+ mInputManager.getAppLaunchBookmarks());
+ }
return mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(deviceId);
}
@@ -3436,7 +3441,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ keyguardOn() + " canceled=" + event.isCanceled());
}
- if (!useKeyGestureEventHandler()) {
+ if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) {
if (mKeyCombinationManager.isKeyConsumed(event)) {
return keyConsumed;
}
@@ -3608,7 +3613,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
case KeyEvent.KEYCODE_T:
- if (keyboardA11yShortcutControl()) {
+ if (enableTalkbackAndMagnifierKeyGestures()) {
if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
mTalkbackShortcutController.toggleTalkback(mCurrentUserId,
TalkbackShortcutController.ShortcutSource.KEYBOARD);
@@ -3987,10 +3992,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
case KeyEvent.KEYCODE_SCREENSHOT:
- if (emojiAndScreenshotKeycodesAvailable() && down && repeatCount == 0) {
+ if (firstDown) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
}
return true;
+ case KeyEvent.KEYCODE_DO_NOT_DISTURB:
+ case KeyEvent.KEYCODE_LOCK:
+ case KeyEvent.KEYCODE_FULLSCREEN:
+ return true;
}
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
@@ -4004,14 +4013,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private boolean interceptSystemKeysAndShortcutsNew(IBinder focusedToken, KeyEvent event) {
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
- final boolean keyguardOn = keyguardOn();
- if (isUserSetupComplete() && !keyguardOn) {
- if (mModifierShortcutManager.interceptKey(event)) {
- dismissKeyboardShortcutsMenu();
- return true;
- }
- }
switch (keyCode) {
case KeyEvent.KEYCODE_HOME:
return handleHomeShortcuts(focusedToken, event);
@@ -4115,7 +4117,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController
.isAccessibilityShortcutAvailable(false);
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
- return keyboardA11yShortcutControl();
+ return enableTalkbackAndMagnifierKeyGestures();
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
&& keyboardA11yShortcutControl();
@@ -4348,7 +4350,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
- if (keyboardA11yShortcutControl()) {
+ if (enableTalkbackAndMagnifierKeyGestures()) {
if (complete) {
mTalkbackShortcutController.toggleTalkback(mCurrentUserId,
TalkbackShortcutController.ShortcutSource.KEYBOARD);
@@ -4538,8 +4540,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
SensorPrivacyManager.Sensors.MICROPHONE);
- mSensorPrivacyManager.setSensorPrivacy(SensorPrivacyManager.Sensors.MICROPHONE,
- !isEnabled);
+ mSensorPrivacyManager.setSensorPrivacy(SensorPrivacyToggleSourceProto.OTHER,
+ SensorPrivacyManager.Sensors.MICROPHONE, !isEnabled, mCurrentUserId);
int toastTextResId;
if (isEnabled) {
@@ -4753,6 +4755,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
throws RemoteException {
synchronized (mLock) {
+ if (useKeyGestureEventHandler()) {
+ mInputManagerInternal.registerShortcutKey(shortcutCode, shortcutService);
+ return;
+ }
mModifierShortcutManager.registerShortcutKey(shortcutCode, shortcutService);
}
}
@@ -5663,9 +5669,23 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_MACRO_4:
result &= ~ACTION_PASS_TO_USER;
break;
- case KeyEvent.KEYCODE_EMOJI_PICKER:
- if (!emojiAndScreenshotKeycodesAvailable()) {
- // Don't allow EMOJI_PICKER key to be dispatched until flag is released.
+ case KeyEvent.KEYCODE_DICTATE:
+ case KeyEvent.KEYCODE_NEW:
+ case KeyEvent.KEYCODE_CLOSE:
+ case KeyEvent.KEYCODE_PRINT:
+ case KeyEvent.KEYCODE_F13:
+ case KeyEvent.KEYCODE_F14:
+ case KeyEvent.KEYCODE_F15:
+ case KeyEvent.KEYCODE_F16:
+ case KeyEvent.KEYCODE_F17:
+ case KeyEvent.KEYCODE_F18:
+ case KeyEvent.KEYCODE_F19:
+ case KeyEvent.KEYCODE_F20:
+ case KeyEvent.KEYCODE_F21:
+ case KeyEvent.KEYCODE_F22:
+ case KeyEvent.KEYCODE_F23:
+ case KeyEvent.KEYCODE_F24:
+ if (!enableNew25q2Keycodes()) {
result &= ~ACTION_PASS_TO_USER;
}
break;
@@ -5699,7 +5719,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) {
- if (mKeyCombinationManager.interceptKey(event, interactive)) {
+ if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()
+ && mKeyCombinationManager.interceptKey(event, interactive)) {
// handled by combo keys manager.
mSingleKeyGestureDetector.reset();
return;
@@ -5838,8 +5859,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
long whenNanos, int policyFlags) {
if ((policyFlags & FLAG_WAKE) != 0) {
- if (mWindowWakeUpPolicy.wakeUpFromMotion(
- whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) {
+ if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
+ action == MotionEvent.ACTION_DOWN)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -5853,8 +5874,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// there will be no dream to intercept the touch and wake into ambient. The device should
// wake up in this case.
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
- if (mWindowWakeUpPolicy.wakeUpFromMotion(
- whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) {
+ if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
+ action == MotionEvent.ACTION_DOWN)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -6202,7 +6223,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown) {
- if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode, isDown)) {
+ if (mWindowWakeUpPolicy.wakeUpFromKey(DEFAULT_DISPLAY, eventTime, keyCode, isDown)) {
final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
if (shouldWakeUpWithHomeIntent() && keyCanLaunchHome) {
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
index af1ad13f1d15..04dbd1fea5d6 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -25,6 +25,7 @@ import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
import static android.view.KeyEvent.KEYCODE_POWER;
import static com.android.server.policy.Flags.supportInputWakeupDelegate;
+import static com.android.server.power.feature.flags.Flags.perDisplayWakeByTouch;
import android.annotation.Nullable;
import android.content.Context;
@@ -107,13 +108,14 @@ class WindowWakeUpPolicy {
/**
* Wakes up from a key event.
*
+ * @param displayId the id of the display to wake.
* @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
* @param keyCode the {@link android.view.KeyEvent} key code of the key event.
* @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}.
* @return {@code true} if the policy allows the requested wake up and the request has been
* executed; {@code false} otherwise.
*/
- boolean wakeUpFromKey(long eventTime, int keyCode, boolean isDown) {
+ boolean wakeUpFromKey(int displayId, long eventTime, int keyCode, boolean isDown) {
final boolean wakeAllowedDuringTheaterMode =
keyCode == KEYCODE_POWER
? mAllowTheaterModeWakeFromPowerKey
@@ -126,22 +128,31 @@ class WindowWakeUpPolicy {
&& mInputWakeUpDelegate.wakeUpFromKey(eventTime, keyCode, isDown)) {
return true;
}
- wakeUp(
- eventTime,
- keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
- keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ if (perDisplayWakeByTouch()) {
+ wakeUp(
+ displayId,
+ eventTime,
+ keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
+ keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ } else {
+ wakeUp(
+ eventTime,
+ keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
+ keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ }
return true;
}
/**
* Wakes up from a motion event.
*
+ * @param displayId the id of the display to wake.
* @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
* @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
* @return {@code true} if the policy allows the requested wake up and the request has been
* executed; {@code false} otherwise.
*/
- boolean wakeUpFromMotion(long eventTime, int source, boolean isDown) {
+ boolean wakeUpFromMotion(int displayId, long eventTime, int source, boolean isDown) {
if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
if (DEBUG) Slog.d(TAG, "Unable to wake up from motion.");
return false;
@@ -150,7 +161,11 @@ class WindowWakeUpPolicy {
&& mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) {
return true;
}
- wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
+ if (perDisplayWakeByTouch()) {
+ wakeUp(displayId, eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
+ } else {
+ wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
+ }
return true;
}
@@ -237,4 +252,12 @@ class WindowWakeUpPolicy {
private void wakeUp(long wakeTime, @WakeReason int reason, String details) {
mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
}
+
+ /** Wakes up given display. */
+ private void wakeUp(int displayId, long wakeTime, @WakeReason int reason, String details) {
+ // If we're given an invalid display id to wake, fall back to waking default display
+ final int displayIdToWake =
+ displayId == Display.INVALID_DISPLAY ? Display.DEFAULT_DISPLAY : displayId;
+ mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details, displayIdToWake);
+ }
}
diff --git a/services/core/java/com/android/server/power/FrameworkStatsLogger.java b/services/core/java/com/android/server/power/FrameworkStatsLogger.java
new file mode 100644
index 000000000000..78ad30548075
--- /dev/null
+++ b/services/core/java/com/android/server/power/FrameworkStatsLogger.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.os.WorkSource.WorkChain;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+public class FrameworkStatsLogger {
+ public enum WakelockEventType {
+ ACQUIRE,
+ RELEASE
+ }
+
+ /** Log WakelockStateChanged push atom without a WorkChain. */
+ public void wakelockStateChanged(
+ int ownerUid, String tag, int powerManagerWakeLockLevel, WakelockEventType eventType) {
+ int event =
+ (eventType == WakelockEventType.ACQUIRE)
+ ? FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE
+ : FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE;
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
+ ownerUid,
+ null,
+ powerManagerWakeLockLevel,
+ tag,
+ event,
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED__PROCESS_STATE__PROCESS_STATE_UNKNOWN);
+ }
+
+ /** Log WakelockStateChanged push atom with a WorkChain. */
+ public void wakelockStateChanged(
+ String tag, WorkChain wc, int powerManagerWakeLockLevel, WakelockEventType eventType) {
+ int event =
+ (eventType == WakelockEventType.ACQUIRE)
+ ? FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE
+ : FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE;
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED,
+ wc.getUids(),
+ wc.getTags(),
+ powerManagerWakeLockLevel,
+ tag,
+ event,
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED__PROCESS_STATE__PROCESS_STATE_UNKNOWN);
+ }
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8ba56c5320f2..0c3c46c75eee 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -33,6 +33,7 @@ import android.media.RingtoneManager;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.IWakeLockCallback;
@@ -48,6 +49,7 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.WorkSource;
+import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.EventLog;
@@ -66,10 +68,12 @@ import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.FrameworkStatsLogger.WakelockEventType;
import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -195,6 +199,9 @@ public class Notifier {
private final PowerManagerFlags mFlags;
+ private final BatteryStatsInternal mBatteryStatsInternal;
+ private final FrameworkStatsLogger mFrameworkStatsLogger;
+
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
@@ -241,6 +248,14 @@ public class Notifier {
} catch (RemoteException ex) { }
FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
+
+ if (mFlags.isMoveWscLoggingToNotifierEnabled()) {
+ mBatteryStatsInternal = mInjector.getBatteryStatsInternal();
+ mFrameworkStatsLogger = mInjector.getFrameworkStatsLogger();
+ } else {
+ mBatteryStatsInternal = null;
+ mFrameworkStatsLogger = null;
+ }
}
/**
@@ -277,6 +292,7 @@ public class Notifier {
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
+ logWakelockStateChanged(flags, tag, ownerUid, workSource, WakelockEventType.ACQUIRE);
notifyWakeLockListener(callback, tag, true, ownerUid, ownerPid, flags, workSource,
packageName, historyTag);
if (!mFlags.improveWakelockLatency()) {
@@ -380,6 +396,10 @@ public class Notifier {
+ ", workSource=" + newWorkSource);
}
+ logWakelockStateChanged(flags, tag, ownerUid, workSource, WakelockEventType.RELEASE);
+ logWakelockStateChanged(
+ newFlags, newTag, newOwnerUid, newWorkSource, WakelockEventType.ACQUIRE);
+
final boolean unimportantForLogging = newOwnerUid == Process.SYSTEM_UID
&& (newFlags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
try {
@@ -425,6 +445,7 @@ public class Notifier {
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
+ logWakelockStateChanged(flags, tag, ownerUid, workSource, WakelockEventType.RELEASE);
notifyWakeLockListener(callback, tag, false, ownerUid, ownerPid, flags, workSource,
packageName, historyTag);
if (!mFlags.improveWakelockLatency()) {
@@ -1258,6 +1279,44 @@ public class Notifier {
}
}
+ private void logWakelockStateChanged(
+ int flags,
+ String tag,
+ int ownerUid,
+ WorkSource workSource,
+ WakelockEventType eventType) {
+ if (mBatteryStatsInternal == null) {
+ return;
+ }
+ final int type = flags & PowerManager.WAKE_LOCK_LEVEL_MASK;
+ if (workSource == null || workSource.isEmpty()) {
+ final int mappedUid = mBatteryStatsInternal.getOwnerUid(ownerUid);
+ mFrameworkStatsLogger.wakelockStateChanged(mappedUid, tag, type, eventType);
+ } else {
+ for (int i = 0; i < workSource.size(); ++i) {
+ final int mappedUid = mBatteryStatsInternal.getOwnerUid(workSource.getUid(i));
+ mFrameworkStatsLogger.wakelockStateChanged(mappedUid, tag, type, eventType);
+ }
+
+ List<WorkChain> workChains = workSource.getWorkChains();
+ if (workChains != null) {
+ for (WorkChain workChain : workChains) {
+ WorkChain mappedWorkChain = new WorkChain();
+ // Cache getUids() and getTags() because they make an arraycopy.
+ int[] uids = workChain.getUids();
+ String[] tags = workChain.getTags();
+
+ for (int i = 0; i < workChain.getSize(); ++i) {
+ final int mappedUid = mBatteryStatsInternal.getOwnerUid(uids[i]);
+ mappedWorkChain.addNode(mappedUid, tags[i]);
+ }
+ mFrameworkStatsLogger.wakelockStateChanged(
+ tag, mappedWorkChain, type, eventType);
+ }
+ }
+ }
+ }
+
public interface Injector {
/**
* Gets the current time in millis
@@ -1273,9 +1332,15 @@ public class Notifier {
* Gets the AppOpsManager system service
*/
AppOpsManager getAppOpsManager(Context context);
+
+ /** Gets the BatteryStatsInternal object */
+ BatteryStatsInternal getBatteryStatsInternal();
+
+ /** Get the FrameworkStatsLogger object */
+ FrameworkStatsLogger getFrameworkStatsLogger();
}
- static class RealInjector implements Injector {
+ class RealInjector implements Injector {
@Override
public long currentTimeMillis() {
return System.currentTimeMillis();
@@ -1290,5 +1355,15 @@ public class Notifier {
public AppOpsManager getAppOpsManager(Context context) {
return context.getSystemService(AppOpsManager.class);
}
+
+ @Override
+ public BatteryStatsInternal getBatteryStatsInternal() {
+ return LocalServices.getService(BatteryStatsInternal.class);
+ }
+
+ @Override
+ public FrameworkStatsLogger getFrameworkStatsLogger() {
+ return new FrameworkStatsLogger();
+ }
}
}
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index a928814c7909..01a2045df426 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -42,6 +42,8 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
+import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.power.feature.PowerManagerFlags;
/**
@@ -56,6 +58,11 @@ public class PowerGroup {
private static final String TAG = PowerGroup.class.getSimpleName();
private static final boolean DEBUG = false;
+ /**
+ * Indicates that the default dim/sleep timeouts should be used.
+ */
+ private static final long INVALID_TIMEOUT = -1;
+
@VisibleForTesting
final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest();
private final PowerGroupListener mWakefulnessListener;
@@ -91,6 +98,9 @@ public class PowerGroup {
private @PowerManager.GoToSleepReason int mLastSleepReason =
PowerManager.GO_TO_SLEEP_REASON_UNKNOWN;
+ private final long mDimDuration;
+ private final long mScreenOffTimeout;
+
PowerGroup(int groupId, PowerGroupListener wakefulnessListener, Notifier notifier,
DisplayManagerInternal displayManagerInternal, int wakefulness, boolean ready,
boolean supportsSandman, long eventTime, PowerManagerFlags featureFlags) {
@@ -104,6 +114,30 @@ public class PowerGroup {
mLastWakeTime = eventTime;
mLastSleepTime = eventTime;
mFeatureFlags = featureFlags;
+
+ long dimDuration = INVALID_TIMEOUT;
+ long screenOffTimeout = INVALID_TIMEOUT;
+ if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()
+ && mGroupId != Display.DEFAULT_DISPLAY_GROUP) {
+ VirtualDeviceManagerInternal vdm =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
+ if (vdm != null) {
+ int[] displayIds = mDisplayManagerInternal.getDisplayIdsForGroup(mGroupId);
+ if (displayIds != null && displayIds.length > 0) {
+ int deviceId = vdm.getDeviceIdForDisplayId(displayIds[0]);
+ if (vdm.isValidVirtualDeviceId(deviceId)) {
+ dimDuration = vdm.getDimDurationMillisForDeviceId(deviceId);
+ screenOffTimeout = vdm.getScreenOffTimeoutMillisForDeviceId(deviceId);
+ if (dimDuration > 0 && dimDuration > screenOffTimeout) {
+ // If the dim duration is set, cap it to the screen off timeout.
+ dimDuration = screenOffTimeout;
+ }
+ }
+ }
+ }
+ }
+ mDimDuration = dimDuration;
+ mScreenOffTimeout = screenOffTimeout;
}
PowerGroup(int wakefulness, PowerGroupListener wakefulnessListener, Notifier notifier,
@@ -119,6 +153,16 @@ public class PowerGroup {
mLastWakeTime = eventTime;
mLastSleepTime = eventTime;
mFeatureFlags = featureFlags;
+ mDimDuration = INVALID_TIMEOUT;
+ mScreenOffTimeout = INVALID_TIMEOUT;
+ }
+
+ long getScreenOffTimeoutOverrideLocked(long defaultScreenOffTimeout) {
+ return mScreenOffTimeout == INVALID_TIMEOUT ? defaultScreenOffTimeout : mScreenOffTimeout;
+ }
+
+ long getScreenDimDurationOverrideLocked(long defaultScreenDimDuration) {
+ return mDimDuration == INVALID_TIMEOUT ? defaultScreenDimDuration : mDimDuration;
}
long getLastWakeTimeLocked() {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3a5afacd0977..0acfe92f578d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2971,8 +2971,8 @@ public final class PowerManagerService extends SystemService
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
final long attentiveTimeout = getAttentiveTimeoutLocked();
- final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
- final long defaultScreenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
+ final long defaultSleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
+ final long defaultScreenOffTimeout = getScreenOffTimeoutLocked(defaultSleepTimeout,
attentiveTimeout);
final long defaultScreenDimDuration = getScreenDimDurationLocked(defaultScreenOffTimeout);
@@ -2985,13 +2985,25 @@ public final class PowerManagerService extends SystemService
final PowerGroup powerGroup = mPowerGroups.valueAt(idx);
final int wakefulness = powerGroup.getWakefulnessLocked();
- // The default display screen timeout could be overridden by policy.
+ // The timeouts could be overridden by the power group policy.
long screenOffTimeout = defaultScreenOffTimeout;
long screenDimDuration = defaultScreenDimDuration;
+ long sleepTimeout = defaultSleepTimeout;
+ // TODO(b/376211497): Consolidate the timeout logic for all power groups.
if (powerGroup.getGroupId() == Display.DEFAULT_DISPLAY_GROUP) {
screenOffTimeout =
- getScreenOffTimeoutOverrideLocked(screenOffTimeout, screenDimDuration);
+ getDefaultGroupScreenOffTimeoutOverrideLocked(screenOffTimeout,
+ screenDimDuration);
screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ } else {
+ screenOffTimeout = powerGroup.getScreenOffTimeoutOverrideLocked(screenOffTimeout);
+ screenDimDuration =
+ powerGroup.getScreenDimDurationOverrideLocked(screenDimDuration);
+ if (sleepTimeout > 0 && screenOffTimeout > 0) {
+ // If both sleep and screen off timeouts are set, make sure that the sleep
+ // timeout is not smaller than the screen off one.
+ sleepTimeout = Math.max(sleepTimeout, screenOffTimeout);
+ }
}
if (wakefulness != WAKEFULNESS_ASLEEP) {
@@ -3273,7 +3285,8 @@ public final class PowerManagerService extends SystemService
@VisibleForTesting
@GuardedBy("mLock")
- long getScreenOffTimeoutOverrideLocked(long screenOffTimeout, long screenDimDuration) {
+ long getDefaultGroupScreenOffTimeoutOverrideLocked(long screenOffTimeout,
+ long screenDimDuration) {
long shortestScreenOffTimeout = screenOffTimeout;
if (mScreenTimeoutOverridePolicy != null) {
shortestScreenOffTimeout =
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 78bc06c27130..42dbb7974fe2 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -43,6 +43,7 @@ import android.os.Handler;
import android.os.HwBinder;
import android.os.IBinder;
import android.os.IThermalEventListener;
+import android.os.IThermalHeadroomListener;
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
@@ -59,6 +60,7 @@ import android.os.Trace;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.StatsEvent;
import com.android.internal.annotations.GuardedBy;
@@ -96,6 +98,15 @@ public class ThermalManagerService extends SystemService {
/** Input range limits for getThermalHeadroom API */
public static final int MIN_FORECAST_SEC = 0;
public static final int MAX_FORECAST_SEC = 60;
+ public static final int DEFAULT_FORECAST_SECONDS = 10;
+ public static final int HEADROOM_CALLBACK_MIN_INTERVAL_MILLIS = 5000;
+ // headroom to temperature conversion: 3C every 0.1 headroom difference
+ // if no throttling event, the temperature difference should be at least 0.9C (or 0.03 headroom)
+ // to make a callback
+ public static final float HEADROOM_CALLBACK_MIN_DIFFERENCE = 0.03f;
+ // if no throttling event, the threshold headroom difference should be at least 0.01 (or 0.3C)
+ // to make a callback
+ public static final float HEADROOM_THRESHOLD_CALLBACK_MIN_DIFFERENCE = 0.01f;
/** Lock to protect listen list. */
private final Object mLock = new Object();
@@ -113,6 +124,15 @@ public class ThermalManagerService extends SystemService {
private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
new RemoteCallbackList<>();
+ /** Registered observers of the thermal headroom. */
+ @GuardedBy("mLock")
+ private final RemoteCallbackList<IThermalHeadroomListener> mThermalHeadroomListeners =
+ new RemoteCallbackList<>();
+ @GuardedBy("mLock")
+ private long mLastHeadroomCallbackTimeMillis;
+ @GuardedBy("mLock")
+ private HeadroomCallbackData mLastHeadroomCallbackData = null;
+
/** Current thermal status */
@GuardedBy("mLock")
private int mStatus;
@@ -133,7 +153,7 @@ public class ThermalManagerService extends SystemService {
/** Watches temperatures to forecast when throttling will occur */
@VisibleForTesting
- final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher();
+ final TemperatureWatcher mTemperatureWatcher;
private final ThermalHalWrapper.WrapperThermalChangedCallback mWrapperCallback =
new ThermalHalWrapper.WrapperThermalChangedCallback() {
@@ -151,8 +171,14 @@ public class ThermalManagerService extends SystemService {
public void onThresholdChanged(TemperatureThreshold threshold) {
final long token = Binder.clearCallingIdentity();
try {
+ final HeadroomCallbackData data;
synchronized (mTemperatureWatcher.mSamples) {
+ Slog.d(TAG, "Updating skin threshold: " + threshold);
mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true);
+ data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
+ }
+ synchronized (mLock) {
+ checkAndNotifyHeadroomListenersLocked(data);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -175,6 +201,7 @@ public class ThermalManagerService extends SystemService {
halWrapper.setCallback(mWrapperCallback);
}
mStatus = Temperature.THROTTLING_NONE;
+ mTemperatureWatcher = new TemperatureWatcher();
}
@Override
@@ -231,32 +258,79 @@ public class ThermalManagerService extends SystemService {
}
}
- private void postStatusListener(IThermalStatusListener listener) {
+ @GuardedBy("mLock")
+ private void postStatusListenerLocked(IThermalStatusListener listener) {
final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
try {
listener.onStatusChange(mStatus);
} catch (RemoteException | RuntimeException e) {
- Slog.e(TAG, "Thermal callback failed to call", e);
+ Slog.e(TAG, "Thermal status callback failed to call", e);
}
});
if (!thermalCallbackQueued) {
- Slog.e(TAG, "Thermal callback failed to queue");
+ Slog.e(TAG, "Thermal status callback failed to queue");
}
}
+ @GuardedBy("mLock")
private void notifyStatusListenersLocked() {
final int length = mThermalStatusListeners.beginBroadcast();
try {
for (int i = 0; i < length; i++) {
final IThermalStatusListener listener =
mThermalStatusListeners.getBroadcastItem(i);
- postStatusListener(listener);
+ postStatusListenerLocked(listener);
}
} finally {
mThermalStatusListeners.finishBroadcast();
}
}
+ @GuardedBy("mLock")
+ private void postHeadroomListenerLocked(IThermalHeadroomListener listener,
+ HeadroomCallbackData data) {
+ if (!mHalReady.get()) {
+ return;
+ }
+ final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
+ try {
+ if (Float.isNaN(data.mHeadroom)) {
+ return;
+ }
+ listener.onHeadroomChange(data.mHeadroom, data.mForecastHeadroom,
+ data.mForecastSeconds, data.mHeadroomThresholds);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Thermal headroom callback failed to call", e);
+ }
+ });
+ if (!thermalCallbackQueued) {
+ Slog.e(TAG, "Thermal headroom callback failed to queue");
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void checkAndNotifyHeadroomListenersLocked(HeadroomCallbackData data) {
+ if (!data.isSignificantDifferentFrom(mLastHeadroomCallbackData)
+ && System.currentTimeMillis()
+ < mLastHeadroomCallbackTimeMillis + HEADROOM_CALLBACK_MIN_INTERVAL_MILLIS) {
+ // skip notifying the client with similar data within a short period
+ return;
+ }
+ mLastHeadroomCallbackTimeMillis = System.currentTimeMillis();
+ mLastHeadroomCallbackData = data;
+ final int length = mThermalHeadroomListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ final IThermalHeadroomListener listener =
+ mThermalHeadroomListeners.getBroadcastItem(i);
+ postHeadroomListenerLocked(listener, data);
+ }
+ } finally {
+ mThermalHeadroomListeners.finishBroadcast();
+ }
+ }
+
+ @GuardedBy("mLock")
private void onTemperatureMapChangedLocked() {
int newStatus = Temperature.THROTTLING_NONE;
final int count = mTemperatureMap.size();
@@ -272,6 +346,7 @@ public class ThermalManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
private void setStatusLocked(int newStatus) {
if (newStatus != mStatus) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ThermalManagerService.status", newStatus);
@@ -280,18 +355,18 @@ public class ThermalManagerService extends SystemService {
}
}
- private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
+ @GuardedBy("mLock")
+ private void postEventListenerCurrentTemperaturesLocked(IThermalEventListener listener,
@Nullable Integer type) {
- synchronized (mLock) {
- final int count = mTemperatureMap.size();
- for (int i = 0; i < count; i++) {
- postEventListener(mTemperatureMap.valueAt(i), listener,
- type);
- }
+ final int count = mTemperatureMap.size();
+ for (int i = 0; i < count; i++) {
+ postEventListenerLocked(mTemperatureMap.valueAt(i), listener,
+ type);
}
}
- private void postEventListener(Temperature temperature,
+ @GuardedBy("mLock")
+ private void postEventListenerLocked(Temperature temperature,
IThermalEventListener listener,
@Nullable Integer type) {
// Skip if listener registered with a different type
@@ -302,14 +377,15 @@ public class ThermalManagerService extends SystemService {
try {
listener.notifyThrottling(temperature);
} catch (RemoteException | RuntimeException e) {
- Slog.e(TAG, "Thermal callback failed to call", e);
+ Slog.e(TAG, "Thermal event callback failed to call", e);
}
});
if (!thermalCallbackQueued) {
- Slog.e(TAG, "Thermal callback failed to queue");
+ Slog.e(TAG, "Thermal event callback failed to queue");
}
}
+ @GuardedBy("mLock")
private void notifyEventListenersLocked(Temperature temperature) {
final int length = mThermalEventListeners.beginBroadcast();
try {
@@ -318,7 +394,7 @@ public class ThermalManagerService extends SystemService {
mThermalEventListeners.getBroadcastItem(i);
final Integer type =
(Integer) mThermalEventListeners.getBroadcastCookie(i);
- postEventListener(temperature, listener, type);
+ postEventListenerLocked(temperature, listener, type);
}
} finally {
mThermalEventListeners.finishBroadcast();
@@ -348,17 +424,31 @@ public class ThermalManagerService extends SystemService {
}
}
- private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
+ private void onTemperatureChanged(Temperature temperature, boolean sendCallback) {
shutdownIfNeeded(temperature);
synchronized (mLock) {
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
if (old == null || old.getStatus() != temperature.getStatus()) {
notifyEventListenersLocked(temperature);
}
- if (sendStatus) {
+ if (sendCallback) {
onTemperatureMapChangedLocked();
}
}
+ if (sendCallback && Flags.allowThermalThresholdsCallback()
+ && temperature.getType() == Temperature.TYPE_SKIN) {
+ final HeadroomCallbackData data;
+ synchronized (mTemperatureWatcher.mSamples) {
+ Slog.d(TAG, "Updating new temperature: " + temperature);
+ mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(),
+ temperature);
+ mTemperatureWatcher.mCachedHeadrooms.clear();
+ data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
+ }
+ synchronized (mLock) {
+ checkAndNotifyHeadroomListenersLocked(data);
+ }
+ }
}
private void registerStatsCallbacks() {
@@ -399,7 +489,7 @@ public class ThermalManagerService extends SystemService {
return false;
}
// Notify its callback after new client registered.
- postEventListenerCurrentTemperatures(listener, null);
+ postEventListenerCurrentTemperaturesLocked(listener, null);
return true;
} finally {
Binder.restoreCallingIdentity(token);
@@ -415,11 +505,11 @@ public class ThermalManagerService extends SystemService {
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
- if (!mThermalEventListeners.register(listener, new Integer(type))) {
+ if (!mThermalEventListeners.register(listener, type)) {
return false;
}
// Notify its callback after new client registered.
- postEventListenerCurrentTemperatures(listener, new Integer(type));
+ postEventListenerCurrentTemperaturesLocked(listener, type);
return true;
} finally {
Binder.restoreCallingIdentity(token);
@@ -484,7 +574,7 @@ public class ThermalManagerService extends SystemService {
return false;
}
// Notify its callback after new client registered.
- postStatusListener(listener);
+ postStatusListenerLocked(listener);
return true;
} finally {
Binder.restoreCallingIdentity(token);
@@ -557,11 +647,50 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ public boolean registerThermalHeadroomListener(IThermalHeadroomListener listener) {
+ if (!mHalReady.get()) {
+ return false;
+ }
+ synchronized (mLock) {
+ // Notify its callback after new client registered.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalHeadroomListeners.register(listener)) {
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ final HeadroomCallbackData data;
+ synchronized (mTemperatureWatcher.mSamples) {
+ data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
+ }
+ // Notify its callback after new client registered.
+ synchronized (mLock) {
+ postHeadroomListenerLocked(listener, data);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean unregisterThermalHeadroomListener(IThermalHeadroomListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mThermalHeadroomListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
public float getThermalHeadroom(int forecastSeconds) {
if (!mHalReady.get()) {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
- FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__HAL_NOT_READY,
- Float.NaN, forecastSeconds);
+ FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__HAL_NOT_READY,
+ Float.NaN, forecastSeconds);
return Float.NaN;
}
@@ -570,8 +699,8 @@ public class ThermalManagerService extends SystemService {
Slog.d(TAG, "Invalid forecastSeconds: " + forecastSeconds);
}
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
- FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__INVALID_ARGUMENT,
- Float.NaN, forecastSeconds);
+ FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__INVALID_ARGUMENT,
+ Float.NaN, forecastSeconds);
return Float.NaN;
}
@@ -592,13 +721,10 @@ public class ThermalManagerService extends SystemService {
THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__FEATURE_NOT_SUPPORTED);
throw new UnsupportedOperationException("Thermal headroom thresholds not enabled");
}
- synchronized (mTemperatureWatcher.mSamples) {
- FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED,
- Binder.getCallingUid(),
- THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__SUCCESS);
- return Arrays.copyOf(mTemperatureWatcher.mHeadroomThresholds,
- mTemperatureWatcher.mHeadroomThresholds.length);
- }
+ FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED,
+ Binder.getCallingUid(),
+ THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__SUCCESS);
+ return mTemperatureWatcher.getHeadroomThresholds();
}
@Override
@@ -711,7 +837,7 @@ public class ThermalManagerService extends SystemService {
class ThermalShellCommand extends ShellCommand {
@Override
public int onCommand(String cmd) {
- switch(cmd != null ? cmd : "") {
+ switch (cmd != null ? cmd : "") {
case "inject-temperature":
return runInjectTemperature();
case "override-status":
@@ -1112,7 +1238,8 @@ public class ThermalManagerService extends SystemService {
}
@Override
- @NonNull protected List<TemperatureThreshold> getTemperatureThresholds(
+ @NonNull
+ protected List<TemperatureThreshold> getTemperatureThresholds(
boolean shouldFilter, int type) {
synchronized (mHalLock) {
final List<TemperatureThreshold> ret = new ArrayList<>();
@@ -1631,14 +1758,68 @@ public class ThermalManagerService extends SystemService {
}
}
+ private static final class HeadroomCallbackData {
+ float mHeadroom;
+ float mForecastHeadroom;
+ int mForecastSeconds;
+ float[] mHeadroomThresholds;
+
+ HeadroomCallbackData(float headroom, float forecastHeadroom, int forecastSeconds,
+ @NonNull float[] headroomThresholds) {
+ mHeadroom = headroom;
+ mForecastHeadroom = forecastHeadroom;
+ mForecastSeconds = forecastSeconds;
+ mHeadroomThresholds = headroomThresholds;
+ }
+
+ private boolean isSignificantDifferentFrom(HeadroomCallbackData other) {
+ if (other == null) return true;
+ // currently this is always the same as DEFAULT_FORECAST_SECONDS, when it's retried
+ // from thermal HAL, we may want to adjust this.
+ if (this.mForecastSeconds != other.mForecastSeconds) return true;
+ if (Math.abs(this.mHeadroom - other.mHeadroom)
+ >= HEADROOM_CALLBACK_MIN_DIFFERENCE) return true;
+ if (Math.abs(this.mForecastHeadroom - other.mForecastHeadroom)
+ >= HEADROOM_CALLBACK_MIN_DIFFERENCE) return true;
+ for (int i = 0; i < this.mHeadroomThresholds.length; i++) {
+ if (Float.isNaN(this.mHeadroomThresholds[i]) != Float.isNaN(
+ other.mHeadroomThresholds[i])) {
+ return true;
+ }
+ if (Math.abs(this.mHeadroomThresholds[i] - other.mHeadroomThresholds[i])
+ >= HEADROOM_THRESHOLD_CALLBACK_MIN_DIFFERENCE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "HeadroomCallbackData[mHeadroom=" + mHeadroom + ", mForecastHeadroom="
+ + mForecastHeadroom + ", mForecastSeconds=" + mForecastSeconds
+ + ", mHeadroomThresholds=" + Arrays.toString(mHeadroomThresholds) + "]";
+ }
+ }
+
@VisibleForTesting
class TemperatureWatcher {
+ private static final int RING_BUFFER_SIZE = 30;
+ private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
+ @VisibleForTesting
+ long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;
+
private final Handler mHandler = BackgroundThread.getHandler();
- /** Map of skin temperature sensor name to a corresponding list of samples */
+ /**
+ * Map of skin temperature sensor name to a corresponding list of samples
+ * Updates to the samples should also clear the headroom cache.
+ */
@GuardedBy("mSamples")
@VisibleForTesting
final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>();
+ @GuardedBy("mSamples")
+ private final SparseArray<Float> mCachedHeadrooms = new SparseArray<>(2);
/** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */
@GuardedBy("mSamples")
@@ -1650,13 +1831,9 @@ public class ThermalManagerService extends SystemService {
@GuardedBy("mSamples")
private long mLastForecastCallTimeMillis = 0;
- private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
- @VisibleForTesting
- long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;
-
void getAndUpdateThresholds() {
List<TemperatureThreshold> thresholds =
- mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
+ mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
synchronized (mSamples) {
if (Flags.allowThermalHeadroomThresholds()) {
Arrays.fill(mHeadroomThresholds, Float.NaN);
@@ -1684,6 +1861,8 @@ public class ThermalManagerService extends SystemService {
return;
}
if (override) {
+ Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold);
+ mCachedHeadrooms.clear();
Arrays.fill(mHeadroomThresholds, Float.NaN);
}
for (int severity = ThrottlingSeverity.LIGHT;
@@ -1693,62 +1872,61 @@ public class ThermalManagerService extends SystemService {
if (Float.isNaN(t)) {
continue;
}
- synchronized (mSamples) {
- if (severity == ThrottlingSeverity.SEVERE) {
- mHeadroomThresholds[severity] = 1.0f;
- continue;
- }
- float headroom = normalizeTemperature(t, severeThreshold);
- if (Float.isNaN(mHeadroomThresholds[severity])) {
- mHeadroomThresholds[severity] = headroom;
- } else {
- float lastHeadroom = mHeadroomThresholds[severity];
- mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom);
- }
+ if (severity == ThrottlingSeverity.SEVERE) {
+ mHeadroomThresholds[severity] = 1.0f;
+ continue;
+ }
+ float headroom = normalizeTemperature(t, severeThreshold);
+ if (Float.isNaN(mHeadroomThresholds[severity])) {
+ mHeadroomThresholds[severity] = headroom;
+ } else {
+ float lastHeadroom = mHeadroomThresholds[severity];
+ mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom);
}
}
}
}
- private static final int RING_BUFFER_SIZE = 30;
-
- private void updateTemperature() {
+ private void getAndUpdateTemperatureSamples() {
synchronized (mSamples) {
if (SystemClock.elapsedRealtime() - mLastForecastCallTimeMillis
< mInactivityThresholdMillis) {
// Trigger this again after a second as long as forecast has been called more
// recently than the inactivity timeout
- mHandler.postDelayed(this::updateTemperature, 1000);
+ mHandler.postDelayed(this::getAndUpdateTemperatureSamples, 1000);
} else {
// Otherwise, we've been idle for at least 10 seconds, so we should
// shut down
mSamples.clear();
+ mCachedHeadrooms.clear();
return;
}
long now = SystemClock.elapsedRealtime();
- List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
+ final List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
Temperature.TYPE_SKIN);
-
- for (int t = 0; t < temperatures.size(); ++t) {
- Temperature temperature = temperatures.get(t);
-
- // Filter out invalid temperatures. If this results in no values being stored at
- // all, the mSamples.empty() check in getForecast() will catch it.
- if (Float.isNaN(temperature.getValue())) {
- continue;
- }
-
- ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
- k -> new ArrayList<>(RING_BUFFER_SIZE));
- if (samples.size() == RING_BUFFER_SIZE) {
- samples.removeFirst();
- }
- samples.add(new Sample(now, temperature.getValue()));
+ for (Temperature temperature : temperatures) {
+ updateTemperatureSampleLocked(now, temperature);
}
+ mCachedHeadrooms.clear();
}
}
+ @GuardedBy("mSamples")
+ private void updateTemperatureSampleLocked(long timeNow, Temperature temperature) {
+ // Filter out invalid temperatures. If this results in no values being stored at
+ // all, the mSamples.empty() check in getForecast() will catch it.
+ if (Float.isNaN(temperature.getValue())) {
+ return;
+ }
+ ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
+ k -> new ArrayList<>(RING_BUFFER_SIZE));
+ if (samples.size() == RING_BUFFER_SIZE) {
+ samples.removeFirst();
+ }
+ samples.add(new Sample(timeNow, temperature.getValue()));
+ }
+
/**
* Calculates the trend using a linear regression. As the samples are degrees Celsius with
* associated timestamps in milliseconds, the slope is in degrees Celsius per millisecond.
@@ -1801,7 +1979,7 @@ public class ThermalManagerService extends SystemService {
synchronized (mSamples) {
mLastForecastCallTimeMillis = SystemClock.elapsedRealtime();
if (mSamples.isEmpty()) {
- updateTemperature();
+ getAndUpdateTemperatureSamples();
}
// If somehow things take much longer than expected or there are no temperatures
@@ -1826,6 +2004,14 @@ public class ThermalManagerService extends SystemService {
return Float.NaN;
}
+ if (mCachedHeadrooms.contains(forecastSeconds)) {
+ // TODO(b/360486877): replace with metrics
+ Slog.d(TAG,
+ "Headroom forecast in " + forecastSeconds + "s served from cache: "
+ + mCachedHeadrooms.get(forecastSeconds));
+ return mCachedHeadrooms.get(forecastSeconds);
+ }
+
float maxNormalized = Float.NaN;
int noThresholdSampleCount = 0;
for (Map.Entry<String, ArrayList<Sample>> entry : mSamples.entrySet()) {
@@ -1842,6 +2028,12 @@ public class ThermalManagerService extends SystemService {
float currentTemperature = samples.getLast().temperature;
if (samples.size() < MINIMUM_SAMPLE_COUNT) {
+ if (mSamples.size() == 1 && mCachedHeadrooms.contains(0)) {
+ // if only one sensor name exists, then try reading the cache
+ // TODO(b/360486877): replace with metrics
+ Slog.d(TAG, "Headroom forecast cached: " + mCachedHeadrooms.get(0));
+ return mCachedHeadrooms.get(0);
+ }
// Don't try to forecast, just use the latest one we have
float normalized = normalizeTemperature(currentTemperature, threshold);
if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
@@ -1849,8 +2041,10 @@ public class ThermalManagerService extends SystemService {
}
continue;
}
-
- float slope = getSlopeOf(samples);
+ float slope = 0.0f;
+ if (forecastSeconds > 0) {
+ slope = getSlopeOf(samples);
+ }
float normalized = normalizeTemperature(
currentTemperature + slope * forecastSeconds * 1000, threshold);
if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
@@ -1868,10 +2062,28 @@ public class ThermalManagerService extends SystemService {
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
maxNormalized, forecastSeconds);
}
+ mCachedHeadrooms.put(forecastSeconds, maxNormalized);
return maxNormalized;
}
}
+ float[] getHeadroomThresholds() {
+ synchronized (mSamples) {
+ return Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length);
+ }
+ }
+
+ @GuardedBy("mSamples")
+ HeadroomCallbackData getHeadroomCallbackDataLocked() {
+ final HeadroomCallbackData data = new HeadroomCallbackData(
+ getForecast(0),
+ getForecast(DEFAULT_FORECAST_SECONDS),
+ DEFAULT_FORECAST_SECONDS,
+ Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length));
+ Slog.d(TAG, "New headroom callback data: " + data);
+ return data;
+ }
+
@VisibleForTesting
// Since Sample is inside an inner class, we can't make it static
// This allows test code to create Sample objects via ThermalManagerService
@@ -1880,7 +2092,7 @@ public class ThermalManagerService extends SystemService {
}
@VisibleForTesting
- class Sample {
+ static class Sample {
public long time;
public float temperature;
@@ -1888,6 +2100,11 @@ public class ThermalManagerService extends SystemService {
this.time = time;
this.temperature = temperature;
}
+
+ @Override
+ public String toString() {
+ return "Sample[temperature=" + temperature + ", time=" + time + "]";
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index 4ddf0c02c730..5cd7dee35e5f 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -55,6 +55,9 @@ public class PowerManagerFlags {
Flags::policyReasonInDisplayPowerRequest
);
+ private final FlagState mMoveWscLoggingToNotifier =
+ new FlagState(Flags.FLAG_MOVE_WSC_LOGGING_TO_NOTIFIER, Flags::moveWscLoggingToNotifier);
+
/** Returns whether early-screen-timeout-detector is enabled on not. */
public boolean isEarlyScreenTimeoutDetectorEnabled() {
return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -89,6 +92,14 @@ public class PowerManagerFlags {
}
/**
+ * @return Whether we move WakelockStateChanged atom logging to Notifier (enabled) or leave it
+ * in BatteryStatsImpl (disabled).
+ */
+ public boolean isMoveWscLoggingToNotifierEnabled() {
+ return mMoveWscLoggingToNotifier.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -98,6 +109,7 @@ public class PowerManagerFlags {
pw.println(" " + mImproveWakelockLatency);
pw.println(" " + mPerDisplayWakeByTouch);
pw.println(" " + mFrameworkWakelockInfo);
+ pw.println(" " + mMoveWscLoggingToNotifier);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index e27f8bb6fee6..a6948fcbae49 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -41,3 +41,10 @@ flag {
bug: "364349703"
is_fixed_read_only: true
}
+
+flag {
+ name: "move_wsc_logging_to_notifier"
+ namespace: "power"
+ description: "Feature flag to move logging of WakelockStateChanged atoms from BatteryStatsImpl to Notifier."
+ bug: "352602149"
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 48174a6bad11..028ac57fc5a3 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -303,6 +303,8 @@ public class BatteryStatsImpl extends BatteryStats {
private final GnssPowerStatsCollector mGnssPowerStatsCollector;
private final CustomEnergyConsumerPowerStatsCollector mCustomEnergyConsumerPowerStatsCollector;
private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
+ private boolean mMoveWscLoggingToNotifierEnabled = false;
+
private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever =
new ScreenPowerStatsCollector.ScreenUsageTimeRetriever() {
@@ -809,17 +811,16 @@ public class BatteryStatsImpl extends BatteryStats {
mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, elapsedRealtimeMs);
if (u.mChildUids != null) {
- LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
- getCpuTimeInFreqContainer();
+ long[] delta = getCpuTimeInFreqContainer();
int childUidCount = u.mChildUids.size();
for (int j = childUidCount - 1; j >= 0; --j) {
LongArrayMultiStateCounter cpuTimeInFreqCounter =
u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
if (cpuTimeInFreqCounter != null) {
mKernelSingleUidTimeReader.addDelta(u.mChildUids.keyAt(j),
- cpuTimeInFreqCounter, elapsedRealtimeMs, deltaContainer);
- onBatteryCounter.addCounts(deltaContainer);
- onBatteryScreenOffCounter.addCounts(deltaContainer);
+ cpuTimeInFreqCounter, elapsedRealtimeMs, delta);
+ onBatteryCounter.addCounts(delta);
+ onBatteryScreenOffCounter.addCounts(delta);
}
}
}
@@ -890,8 +891,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (childUid != null) {
final LongArrayMultiStateCounter counter = childUid.cpuTimeInFreqCounter;
if (counter != null) {
- final LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
- getCpuTimeInFreqContainer();
+ final long[] deltaContainer = getCpuTimeInFreqContainer();
mKernelSingleUidTimeReader.addDelta(uid, counter, elapsedRealtimeMs,
deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
@@ -1741,7 +1741,7 @@ public class BatteryStatsImpl extends BatteryStats {
private long mBatteryTimeToFullSeconds = -1;
- private LongArrayMultiStateCounter.LongArrayContainer mTmpCpuTimeInFreq;
+ private long[] mTmpCpuTimeInFreq;
/**
* Times spent by the system server threads handling incoming binder requests.
@@ -5157,10 +5157,11 @@ public class BatteryStatsImpl extends BatteryStats {
Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs);
uidStats.noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
-
- mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
- uidStats.mProcessState, true /* acquired */,
- getPowerManagerWakeLockLevel(type));
+ if (!mMoveWscLoggingToNotifierEnabled) {
+ mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
+ uidStats.mProcessState, true /* acquired */,
+ getPowerManagerWakeLockLevel(type));
+ }
if (mPowerManagerFlags.isFrameworkWakelockInfoEnabled()) {
mFrameworkEvents.noteStartWakeLock(
mapIsolatedUid(uid), name, getPowerManagerWakeLockLevel(type), uptimeMs);
@@ -5207,9 +5208,11 @@ public class BatteryStatsImpl extends BatteryStats {
Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs);
uidStats.noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
- mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
- uidStats.mProcessState, false/* acquired */,
- getPowerManagerWakeLockLevel(type));
+ if (!mMoveWscLoggingToNotifierEnabled) {
+ mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
+ uidStats.mProcessState, false/* acquired */,
+ getPowerManagerWakeLockLevel(type));
+ }
if (mPowerManagerFlags.isFrameworkWakelockInfoEnabled()) {
mFrameworkEvents.noteStopWakeLock(
mapIsolatedUid(uid), name, getPowerManagerWakeLockLevel(type), uptimeMs);
@@ -10956,9 +10959,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Set initial values to all 0. This is a child UID and we want to include
// the entirety of its CPU time-in-freq stats into the parent's stats.
- cpuTimeInFreqCounter.updateValues(
- new LongArrayMultiStateCounter.LongArrayContainer(cpuFreqCount),
- timestampMs);
+ cpuTimeInFreqCounter.updateValues(new long[cpuFreqCount], timestampMs);
} else {
cpuTimeInFreqCounter = null;
}
@@ -11361,11 +11362,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- private LongArrayMultiStateCounter.LongArrayContainer getCpuTimeInFreqContainer() {
+ private long[] getCpuTimeInFreqContainer() {
if (mTmpCpuTimeInFreq == null) {
- mTmpCpuTimeInFreq =
- new LongArrayMultiStateCounter.LongArrayContainer(
- mCpuScalingPolicies.getScalingStepCount());
+ mTmpCpuTimeInFreq = new long[mCpuScalingPolicies.getScalingStepCount()];
}
return mTmpCpuTimeInFreq;
}
@@ -15978,6 +15977,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ /**
+ * Controls where the logging of the WakelockStateChanged atom occurs:
+ * true = Notifier, false = BatteryStatsImpl.
+ */
+ public void setMoveWscLoggingToNotifierEnabled(boolean enabled) {
+ synchronized (this) {
+ mMoveWscLoggingToNotifierEnabled = enabled;
+ }
+ }
@GuardedBy("this")
public void systemServicesReady(Context context) {
mConstants.startObserving(context.getContentResolver());
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 600fe59215b6..606bd1dd0f3f 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -51,6 +51,7 @@ public class BatteryUsageStatsProvider {
private final CpuScalingPolicies mCpuScalingPolicies;
private final int mAccumulatedBatteryUsageStatsSpanSize;
private final Clock mClock;
+ private final MonotonicClock mMonotonicClock;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
private UserPowerCalculator mUserPowerCalculator;
@@ -67,7 +68,7 @@ public class BatteryUsageStatsProvider {
@NonNull PowerAttributor powerAttributor,
@NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies,
@NonNull PowerStatsStore powerStatsStore, int accumulatedBatteryUsageStatsSpanSize,
- @NonNull Clock clock) {
+ @NonNull Clock clock, @NonNull MonotonicClock monotonicClock) {
mContext = context;
mPowerAttributor = powerAttributor;
mPowerStatsStore = powerStatsStore;
@@ -75,6 +76,7 @@ public class BatteryUsageStatsProvider {
mCpuScalingPolicies = cpuScalingPolicies;
mAccumulatedBatteryUsageStatsSpanSize = accumulatedBatteryUsageStatsSpanSize;
mClock = clock;
+ mMonotonicClock = monotonicClock;
mUserPowerCalculator = new UserPowerCalculator();
mPowerStatsStore.addSectionReader(new BatteryUsageStatsSection.Reader());
@@ -213,7 +215,7 @@ public class BatteryUsageStatsProvider {
powerStatsSpan.addTimeFrame(accumulatedStats.startMonotonicTime,
accumulatedStats.startWallClockTime,
accumulatedStats.endMonotonicTime - accumulatedStats.startMonotonicTime);
- stats.commitMonotonicClock();
+ mMonotonicClock.write();
mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
accumulatedStats.builder::discard);
}
@@ -308,23 +310,29 @@ public class BatteryUsageStatsProvider {
private void updateAccumulatedBatteryUsageStats(AccumulatedBatteryUsageStats accumulatedStats,
BatteryStatsImpl stats, BatteryUsageStatsQuery query) {
- // TODO(b/366493365): add the current batteryusagestats directly into
- // `accumulatedStats.builder` to avoid allocating a second CursorWindow
- BatteryUsageStats.Builder remainingBatteryUsageStats = computeBatteryUsageStats(stats,
- query, accumulatedStats.endMonotonicTime, query.getMonotonicEndTime(),
- mClock.currentTimeMillis());
+ long startMonotonicTime = accumulatedStats.endMonotonicTime;
+ if (startMonotonicTime == MonotonicClock.UNDEFINED) {
+ startMonotonicTime = stats.getMonotonicStartTime();
+ }
+ long endWallClockTime = mClock.currentTimeMillis();
+ long endMonotonicTime = mMonotonicClock.monotonicTime();
if (accumulatedStats.builder == null) {
- accumulatedStats.builder = remainingBatteryUsageStats;
+ accumulatedStats.builder = new BatteryUsageStats.Builder(
+ stats.getCustomEnergyConsumerNames(), false, true, true, true, 0);
accumulatedStats.startWallClockTime = stats.getStartClockTime();
- accumulatedStats.startMonotonicTime = stats.getMonotonicStartTime();
- accumulatedStats.endMonotonicTime = accumulatedStats.startMonotonicTime
- + accumulatedStats.builder.getStatsDuration();
- } else {
- accumulatedStats.builder.add(remainingBatteryUsageStats.build());
- accumulatedStats.endMonotonicTime += remainingBatteryUsageStats.getStatsDuration();
- remainingBatteryUsageStats.discard();
+ accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime);
}
+
+ accumulatedStats.endMonotonicTime = endMonotonicTime;
+
+ accumulatedStats.builder.setStatsEndTimestamp(endWallClockTime);
+ accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime);
+
+ mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, stats.getHistory(),
+ startMonotonicTime, MonotonicClock.UNDEFINED);
+
+ populateGeneralInfo(accumulatedStats.builder, stats);
}
private BatteryUsageStats.Builder computeBatteryUsageStats(BatteryStatsImpl stats,
diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java
index 9398c7a854d4..b129fdc1b6e3 100644
--- a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
+++ b/services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.adaptiveauth;
+package com.android.server.security.adaptiveauthentication;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
@@ -55,8 +55,8 @@ import java.util.Objects;
/**
* @hide
*/
-public class AdaptiveAuthService extends SystemService {
- private static final String TAG = "AdaptiveAuthService";
+public class AdaptiveAuthenticationService extends SystemService {
+ private static final String TAG = "AdaptiveAuthenticationService";
private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
@@ -78,12 +78,12 @@ public class AdaptiveAuthService extends SystemService {
final SparseIntArray mFailedAttemptsForUser = new SparseIntArray();
private final SparseLongArray mLastLockedTimestamp = new SparseLongArray();
- public AdaptiveAuthService(Context context) {
+ public AdaptiveAuthenticationService(Context context) {
this(context, new LockPatternUtils(context));
}
@VisibleForTesting
- public AdaptiveAuthService(Context context, LockPatternUtils lockPatternUtils) {
+ public AdaptiveAuthenticationService(Context context, LockPatternUtils lockPatternUtils) {
super(context);
mLockPatternUtils = lockPatternUtils;
mLockSettings = Objects.requireNonNull(
diff --git a/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS b/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
new file mode 100644
index 000000000000..29affcdb81aa
--- /dev/null
+++ b/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
@@ -0,0 +1,3 @@
+hainingc@google.com
+jbolinger@google.com
+graciecheng@google.com
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
index e798bc487031..3f7fcee5bb9a 100644
--- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
@@ -19,6 +19,7 @@ package com.android.server.stats.pull.netstats;
import android.annotation.NonNull;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
+import android.util.Log;
import java.util.Objects;
@@ -33,6 +34,7 @@ import java.util.Objects;
*/
public class NetworkStatsAccumulator {
+ private static final String TAG = "NetworkStatsAccumulator";
private final NetworkTemplate mTemplate;
private final boolean mWithTags;
private final long mBucketDurationMillis;
@@ -57,8 +59,9 @@ public class NetworkStatsAccumulator {
@NonNull
public NetworkStats queryStats(long currentTimeMillis,
@NonNull StatsQueryFunction queryFunction) {
- maybeExpandSnapshot(currentTimeMillis, queryFunction);
- return snapshotPlusFollowingStats(currentTimeMillis, queryFunction);
+ NetworkStats completeStats = snapshotPlusFollowingStats(currentTimeMillis, queryFunction);
+ maybeExpandSnapshot(currentTimeMillis, completeStats, queryFunction);
+ return completeStats;
}
/**
@@ -72,15 +75,28 @@ public class NetworkStatsAccumulator {
* Expands the internal cumulative stats snapshot, if possible, by querying NetworkStats.
*/
private void maybeExpandSnapshot(long currentTimeMillis,
+ NetworkStats completeStatsUntilCurrentTime,
@NonNull StatsQueryFunction queryFunction) {
// Update snapshot only if it is possible to expand it by at least one full bucket, and only
// if the new snapshot's end is not in the active bucket.
long newEndTimeMillis = currentTimeMillis - mBucketDurationMillis;
if (newEndTimeMillis - mSnapshotEndTimeMillis > mBucketDurationMillis) {
- NetworkStats extraStats = queryFunction.queryNetworkStats(mTemplate, mWithTags,
- mSnapshotEndTimeMillis, newEndTimeMillis);
+ Log.v(TAG,
+ "Expanding snapshot (mTemplate=" + mTemplate + ", mWithTags=" + mWithTags
+ + ") from " + mSnapshotEndTimeMillis + " to " + newEndTimeMillis
+ + " at " + currentTimeMillis);
+ NetworkStats extraStats = queryFunction.queryNetworkStats(
+ mTemplate, mWithTags, mSnapshotEndTimeMillis, newEndTimeMillis);
mSnapshot = mSnapshot.add(extraStats);
mSnapshotEndTimeMillis = newEndTimeMillis;
+
+ // NetworkStats queries interpolate historical data using integers maths, which makes
+ // queries non-transitive: Query(t0, t1) + Query(t1, t2) <= Query(t0, t2).
+ // Compute interpolation data loss from moving the snapshot's end-point, and add it to
+ // the snapshot to avoid under-counting.
+ NetworkStats newStats = snapshotPlusFollowingStats(currentTimeMillis, queryFunction);
+ NetworkStats interpolationLoss = completeStatsUntilCurrentTime.subtract(newStats);
+ mSnapshot = mSnapshot.add(interpolationLoss);
}
}
diff --git a/services/core/java/com/android/server/stats/pull/psi/OWNERS b/services/core/java/com/android/server/stats/pull/psi/OWNERS
new file mode 100644
index 000000000000..f72fd7c18925
--- /dev/null
+++ b/services/core/java/com/android/server/stats/pull/psi/OWNERS
@@ -0,0 +1,9 @@
+jackrichardson@google.com
+dbrotikovskaya@google.com
+ivokay@google.com
+gagapov@google.com
+yigitfiliz@google.com
+rswang@google.com
+evaleriano@google.com
+igorstepanov@google.com
+iyou@google.com
diff --git a/services/core/java/com/android/server/stats/pull/psi/PsiData.java b/services/core/java/com/android/server/stats/pull/psi/PsiData.java
new file mode 100644
index 000000000000..d1cbf7468d19
--- /dev/null
+++ b/services/core/java/com/android/server/stats/pull/psi/PsiData.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.stats.pull.psi;
+
+/**
+ * Wraps PSI (Pressure Stall Information) corresponding to a system resource. See more details about
+ * PSI, see https://docs.kernel.org/accounting/psi.html#psi-pressure-stall-information.
+ */
+public class PsiData {
+ public enum ResourceType {
+ CPU,
+ MEMORY,
+ IO
+ }
+
+ static class AppsStallInfo {
+
+ /** Past 10s average % of wasted CPU cycles when apps tasks are stalled on mResourceType.*/
+ private final float mAvg10SecPercentage;
+
+ /** Past 60s average % of wasted CPU cycles when apps tasks are stalled on mResourceType.*/
+ private final float mAvg60SecPercentage;
+
+ /** Past 300s average % of wasted CPU cycles when apps tasks are stalled on mResourceType.*/
+ private final float mAvg300SecPercentage;
+
+ /** Total number of microseconds that apps tasks are stalled on mResourceType.*/
+ private final long mTotalUsec;
+
+ AppsStallInfo(
+ float avg10SecPercentage, float avg60SecPercentage,
+ float avg300SecPercentage, long totalUsec) {
+ mAvg10SecPercentage = avg10SecPercentage;
+ mAvg60SecPercentage = avg60SecPercentage;
+ mAvg300SecPercentage = avg300SecPercentage;
+ mTotalUsec = totalUsec;
+ }
+ }
+
+ /** The system resource type of this {@code PsiData}. */
+ private final ResourceType mResourceType;
+
+ /** Info on some tasks are stalled on mResourceType. */
+ private final AppsStallInfo mSomeAppsStallInfo;
+
+ /**
+ * Info on all non-idle tasks are stalled on mResourceType. For the CPU ResourceType,
+ * all fields will always be 0 as it's undefined.
+ */
+ private final AppsStallInfo mFullAppsStallInfo;
+
+ PsiData(
+ ResourceType resourceType,
+ AppsStallInfo someAppsStallInfo,
+ AppsStallInfo fullAppsStallInfo) {
+ mResourceType = resourceType;
+ mSomeAppsStallInfo = someAppsStallInfo;
+ mFullAppsStallInfo = fullAppsStallInfo;
+ }
+
+ public ResourceType getResourceType() {
+ return mResourceType;
+ }
+
+ public float getSomeAvg10SecPercentage() {
+ return mSomeAppsStallInfo.mAvg10SecPercentage; }
+
+ public float getSomeAvg60SecPercentage() {
+ return mSomeAppsStallInfo.mAvg60SecPercentage; }
+
+ public float getSomeAvg300SecPercentage() {
+ return mSomeAppsStallInfo.mAvg300SecPercentage; }
+
+ public long getSomeTotalUsec() {
+ return mSomeAppsStallInfo.mTotalUsec;
+ }
+
+ public float getFullAvg10SecPercentage() {
+ return mFullAppsStallInfo.mAvg10SecPercentage;
+ }
+
+ public float getFullAvg60SecPercentage() {
+ return mFullAppsStallInfo.mAvg60SecPercentage;
+ }
+
+ public float getFullAvg300SecPercentage() {
+ return mFullAppsStallInfo.mAvg300SecPercentage; }
+
+ public long getFullTotalUsec() {
+ return mFullAppsStallInfo.mTotalUsec;
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java b/services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java
new file mode 100644
index 000000000000..5d0d7e161990
--- /dev/null
+++ b/services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.stats.pull.psi;
+
+import static java.util.stream.Collectors.joining;
+
+import android.annotation.Nullable;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PsiExtractor {
+ private static final String TAG = "PsiExtractor";
+
+ // Paths for PSI files are guarded by SELinux policy. PCS needs to be explicitly
+ // allowlisted to access these files.
+ private static final String PSI_MEMORY_PATH = "/proc/pressure/memory";
+ private static final String PSI_IO_PATH = "/proc/pressure/io";
+ private static final String PSI_CPU_PATH = "/proc/pressure/cpu";
+
+ // The patterns matching a line of PSI output such as
+ // "some avg10=0.12 avg60=0.34 avg300=0.56 total=123456" or
+ // "full avg10=0.12 avg60=0.34 avg300=0.56 total=123456" to extract the stalling percentage
+ // values for "some" and "full" line of PSI output respectively.
+ private static final String PSI_PATTERN_TEMPLATE =
+ ".*{0} avg10=(\\d+.\\d+) avg60=(\\d+.\\d+) avg300=(\\d+.\\d+) total=(\\d+).*";
+ private static final String SOME = "some";
+ private static final String FULL = "full";
+ private final PsiReader mPsiReader;
+
+ public PsiExtractor() {
+ mPsiReader = new PsiReader();
+ }
+ public PsiExtractor(PsiReader psiReader) {
+ mPsiReader = psiReader;
+ }
+
+ /**
+ * Parses /pressure/proc/{resourceType} kernel file to extract the Pressure Stall Information
+ * (PSI), more information: can be found at https://docs.kernel.org/accounting/psi.html.
+ *
+ * @param resourceType (Memory/CPU/IO) to get the PSI for.
+ */
+ @Nullable
+ public PsiData getPsiData(PsiData.ResourceType resourceType) {
+ String psiFileData;
+ if (resourceType == PsiData.ResourceType.MEMORY) {
+ psiFileData = mPsiReader.read(PSI_MEMORY_PATH);
+ } else if (resourceType == PsiData.ResourceType.IO) {
+ psiFileData = mPsiReader.read(PSI_IO_PATH);
+ } else if (resourceType == PsiData.ResourceType.CPU) {
+ psiFileData = mPsiReader.read(PSI_CPU_PATH);
+ } else {
+ Log.w(TAG, "PsiExtractor failure: cannot read kernel source file, returning null");
+ return null;
+ }
+ return parsePsiData(psiFileData, resourceType);
+ }
+
+ @Nullable
+ private static PsiData.AppsStallInfo parsePsiString(
+ String psiFileData, String appType, PsiData.ResourceType resourceType) {
+ // There is an extra case of file content: the CPU full is undefined and isn't reported for
+ // earlier versions. It should be always propagated as 0, but for the current logic purposes
+ // we will report atom only if at least one value (some/full) is presented. Thus, hardcoding
+ // the "full" line as 0 only when the "some" line is presented.
+ if (appType == FULL && resourceType == PsiData.ResourceType.CPU) {
+ if (psiFileData.contains(SOME) && !psiFileData.contains(FULL)) {
+ return new PsiData.AppsStallInfo((float) 0.0, (float) 0.0, (float) 0.0, 0);
+ }
+ }
+
+ Pattern psiStringPattern = Pattern.compile(
+ MessageFormat.format(PSI_PATTERN_TEMPLATE, appType));
+ Matcher psiLineMatcher = psiStringPattern.matcher(psiFileData);
+
+ // Parsing the line starts with "some" in the expected output.
+ // The line for "some" should always be present in PSI output. The output must be somehow
+ // malformed if the line cannot be matched.
+ if (!psiLineMatcher.find()) {
+ Log.w(TAG,
+ "Returning null: the line \"" + appType + "\" is not in expected pattern.");
+ return null;
+ }
+ try {
+ return new PsiData.AppsStallInfo(
+ Float.parseFloat(psiLineMatcher.group(1)),
+ Float.parseFloat(psiLineMatcher.group(2)),
+ Float.parseFloat(psiLineMatcher.group(3)),
+ Long.parseLong(psiLineMatcher.group(4)));
+ } catch (NumberFormatException e) {
+ Log.w(TAG,
+ "Returning null: some value in line \"" + appType
+ + "\" cannot be parsed as numeric.");
+ return null;
+ }
+ }
+
+ @Nullable
+ private static PsiData parsePsiData(
+ String psiFileData, PsiData.ResourceType resourceType) {
+ PsiData.AppsStallInfo someAppsStallInfo = parsePsiString(psiFileData, SOME, resourceType);
+ PsiData.AppsStallInfo fullAppsStallInfo = parsePsiString(psiFileData, FULL, resourceType);
+
+ if (someAppsStallInfo == null && fullAppsStallInfo == null) {
+ Log.w(TAG, "Returning empty PSI: some or full line are failed to parse");
+ return null;
+ } else if (someAppsStallInfo == null) {
+ Log.d(TAG, "Replacing some info with empty PSI record for the resource type "
+ + resourceType);
+ someAppsStallInfo = new PsiData.AppsStallInfo(
+ (float) -1.0, (float) -1.0, (float) -1.0, -1);
+ } else if (fullAppsStallInfo == null) {
+ Log.d(TAG, "Replacing full info with empty PSI record for the resource type "
+ + resourceType);
+ fullAppsStallInfo = new PsiData.AppsStallInfo(
+ (float) -1.0, (float) -1.0, (float) -1.0, -1);
+ }
+ return new PsiData(resourceType, someAppsStallInfo, fullAppsStallInfo);
+ }
+
+ /** Dependency class */
+ public static class PsiReader {
+ /**
+ * Reads file from provided path and returns its content if the file found, null otherwise.
+ *
+ * @param filePath file path to read.
+ */
+ @Nullable
+ public String read(String filePath) {
+ try (BufferedReader br =
+ new BufferedReader(new InputStreamReader(
+ new FileInputStream(filePath)))) {
+ return br.lines().collect(joining(System.lineSeparator()));
+ } catch (IOException e) {
+ Log.w(TAG, "Cannot read file " + filePath);
+ return null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/utils/LazyJniRegistrar.java b/services/core/java/com/android/server/utils/LazyJniRegistrar.java
new file mode 100644
index 000000000000..ac4a92e12909
--- /dev/null
+++ b/services/core/java/com/android/server/utils/LazyJniRegistrar.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
+import com.android.tools.r8.keepanno.annotations.UsedByNative;
+
+/**
+ * Utility class for lazily registering native methods for a given class.
+ *
+ * <p><strong>Note: </strong>Most native methods are registered eagerly via the
+ * native {@code JNI_OnLoad} hook when system server loads its primary native
+ * lib. However, some classes within system server may be stripped if unused.
+ * This class offers a way to selectively register their native methods. Such
+ * register calls should typically be done from that class's {@code static {}}
+ * init block.
+ */
+@UsedByNative(
+ description = "Referenced from JNI in jni/com_android_server_utils_LazyJniRegistrar.cpp",
+ kind = KeepItemKind.CLASS_AND_MEMBERS,
+ methodAccess = {MethodAccessFlags.NATIVE})
+public final class LazyJniRegistrar {
+
+ // Note: {@link SystemServer#run} loads the native "android_servers" lib, so no need to do so
+ // explicitly here. Classes that use this registration must not be initialized before this.
+
+ /** Registers native methods for ConsumerIrService. */
+ public static native void registerConsumerIrService();
+
+ /** Registers native methods for VrManagerService. */
+ public static native void registerVrManagerService();
+}
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index fbc0b56c2eb7..9f1cc8196736 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -10,6 +10,7 @@ per-file Watcher.java = file:/services/core/java/com/android/server/pm/OWNERS
per-file Watcher.java = shombert@google.com
per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file EventLogger.java = jmtrivi@google.com
+per-file LazyJniRegistrar.java = file:/PERFORMANCE_OWNERS
# Bug component : 158088 = per-file AnrTimer*.java
per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 6ce868540070..9213d96ad4ca 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -74,10 +74,6 @@ public class VcnContext {
return mFeatureFlags;
}
- public boolean isFlagSafeModeTimeoutConfigEnabled() {
- return mFeatureFlags.safeModeTimeoutConfig();
- }
-
/**
* Verifies that the caller is running on the VcnContext Thread.
*
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 2d3bc84debff..2325f358e301 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1263,7 +1263,7 @@ public class VcnGatewayConnection extends StateMachine {
final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp);
int resultSeconds = defaultSeconds;
- if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) {
+ if (carrierConfig != null) {
resultSeconds =
carrierConfig.getInt(
VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds);
diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
index df44e50d2839..a92ac679b0f4 100644
--- a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
@@ -45,6 +45,7 @@ final class ExternalVibrationSession extends Vibration
void onExternalVibrationReleased(long vibrationId);
}
+ private final long mSessionId = VibrationSession.nextSessionId();
private final ExternalVibration mExternalVibration;
private final ExternalVibrationScale mScale = new ExternalVibrationScale();
private final VibratorManagerHooks mManagerHooks;
@@ -65,6 +66,11 @@ final class ExternalVibrationSession extends Vibration
}
@Override
+ public long getSessionId() {
+ return mSessionId;
+ }
+
+ @Override
public long getCreateUptimeMillis() {
return stats.getCreateUptimeMillis();
}
@@ -148,7 +154,12 @@ final class ExternalVibrationSession extends Vibration
@Override
public void notifySyncedVibratorsCallback(long vibrationId) {
- // ignored, external control does not expect callbacks from the vibrator manager
+ // ignored, external control does not expect callbacks from the vibrator manager for sync
+ }
+
+ @Override
+ public void notifySessionCallback() {
+ // ignored, external control does not expect callbacks from the vibrator manager for session
}
boolean isHoldingSameVibration(ExternalVibration vib) {
@@ -174,7 +185,8 @@ final class ExternalVibrationSession extends Vibration
@Override
public String toString() {
return "ExternalVibrationSession{"
- + "id=" + id
+ + "sessionId=" + mSessionId
+ + ", vibrationId=" + id
+ ", callerInfo=" + callerInfo
+ ", externalVibration=" + mExternalVibration
+ ", scale=" + mScale
diff --git a/services/core/java/com/android/server/vibrator/SingleVibrationSession.java b/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
index 67ba25f6b0b9..628221b09d77 100644
--- a/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
@@ -35,6 +35,7 @@ final class SingleVibrationSession implements VibrationSession, IBinder.DeathRec
private static final String TAG = "SingleVibrationSession";
private final Object mLock = new Object();
+ private final long mSessionId = VibrationSession.nextSessionId();
private final IBinder mCallerToken;
private final HalVibration mVibration;
@@ -58,6 +59,11 @@ final class SingleVibrationSession implements VibrationSession, IBinder.DeathRec
}
@Override
+ public long getSessionId() {
+ return mSessionId;
+ }
+
+ @Override
public long getCreateUptimeMillis() {
return mVibration.stats.getCreateUptimeMillis();
}
@@ -155,9 +161,15 @@ final class SingleVibrationSession implements VibrationSession, IBinder.DeathRec
}
@Override
+ public void notifySessionCallback() {
+ // ignored, external control does not expect callbacks from the vibrator manager for session
+ }
+
+ @Override
public String toString() {
return "SingleVibrationSession{"
- + "callerToken= " + mCallerToken
+ + "sessionId= " + mSessionId
+ + ", callerToken= " + mCallerToken
+ ", vibration=" + mVibration
+ '}';
}
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
new file mode 100644
index 000000000000..07478e360d27
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.android.server.vibrator.VibrationSession.DebugInfo.formatTime;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioAttributes;
+import android.os.CancellationSignal;
+import android.os.CombinedVibration;
+import android.os.ExternalVibration;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.VibrationAttributes;
+import android.os.vibrator.IVibrationSession;
+import android.os.vibrator.IVibrationSessionCallback;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+
+/**
+ * A vibration session started by a vendor request that can trigger {@link CombinedVibration}.
+ */
+final class VendorVibrationSession extends IVibrationSession.Stub
+ implements VibrationSession, CancellationSignal.OnCancelListener, IBinder.DeathRecipient {
+ private static final String TAG = "VendorVibrationSession";
+
+ /** Calls into VibratorManager functionality needed for playing an {@link ExternalVibration}. */
+ interface VibratorManagerHooks {
+
+ /** Tells the manager to end the vibration session. */
+ void endSession(long sessionId, boolean shouldAbort);
+
+ /**
+ * Tells the manager that the vibration session is finished and the vibrators can now be
+ * used for another vibration.
+ */
+ void onSessionReleased(long sessionId);
+ }
+
+ private final Object mLock = new Object();
+ private final long mSessionId = VibrationSession.nextSessionId();
+ private final ICancellationSignal mCancellationSignal = CancellationSignal.createTransport();
+ private final int[] mVibratorIds;
+ private final long mCreateUptime;
+ private final long mCreateTime; // for debugging
+ private final IVibrationSessionCallback mCallback;
+ private final CallerInfo mCallerInfo;
+ private final VibratorManagerHooks mManagerHooks;
+ private final Handler mHandler;
+
+ @GuardedBy("mLock")
+ private Status mStatus = Status.RUNNING;
+ @GuardedBy("mLock")
+ private Status mEndStatusRequest;
+ @GuardedBy("mLock")
+ private long mStartTime; // for debugging
+ @GuardedBy("mLock")
+ private long mEndUptime;
+ @GuardedBy("mLock")
+ private long mEndTime; // for debugging
+
+ VendorVibrationSession(@NonNull CallerInfo callerInfo, @NonNull Handler handler,
+ @NonNull VibratorManagerHooks managerHooks, @NonNull int[] vibratorIds,
+ @NonNull IVibrationSessionCallback callback) {
+ mCreateUptime = SystemClock.uptimeMillis();
+ mCreateTime = System.currentTimeMillis();
+ mVibratorIds = vibratorIds;
+ mHandler = handler;
+ mCallback = callback;
+ mCallerInfo = callerInfo;
+ mManagerHooks = managerHooks;
+ CancellationSignal.fromTransport(mCancellationSignal).setOnCancelListener(this);
+ }
+
+ @Override
+ public void vibrate(CombinedVibration vibration, String reason) {
+ // TODO(b/345414356): implement vibration support
+ throw new UnsupportedOperationException("Vendor session vibrations not yet implemented");
+ }
+
+ @Override
+ public void finishSession() {
+ // Do not abort session in HAL, wait for ongoing vibration requests to complete.
+ // This might take a while to end the session, but it can be aborted by cancelSession.
+ requestEndSession(Status.FINISHED, /* shouldAbort= */ false);
+ }
+
+ @Override
+ public void cancelSession() {
+ // Always abort session in HAL while cancelling it.
+ // This might be triggered after finishSession was already called.
+ requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true);
+ }
+
+ @Override
+ public long getSessionId() {
+ return mSessionId;
+ }
+
+ @Override
+ public long getCreateUptimeMillis() {
+ return mCreateUptime;
+ }
+
+ @Override
+ public boolean isRepeating() {
+ return false;
+ }
+
+ @Override
+ public CallerInfo getCallerInfo() {
+ return mCallerInfo;
+ }
+
+ @Override
+ public IBinder getCallerToken() {
+ return mCallback.asBinder();
+ }
+
+ @Override
+ public DebugInfo getDebugInfo() {
+ synchronized (mLock) {
+ return new DebugInfoImpl(mStatus, mCallerInfo, mCreateUptime, mCreateTime, mStartTime,
+ mEndUptime, mEndTime);
+ }
+ }
+
+ @Override
+ public boolean wasEndRequested() {
+ synchronized (mLock) {
+ return mEndStatusRequest != null;
+ }
+ }
+
+ @Override
+ public void onCancel() {
+ Slog.d(TAG, "Cancellation signal received, cancelling vibration session...");
+ requestEnd(Status.CANCELLED_BY_USER, /* endedBy= */ null, /* immediate= */ false);
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Binder died, cancelling vibration session...");
+ requestEnd(Status.CANCELLED_BINDER_DIED, /* endedBy= */ null, /* immediate= */ false);
+ }
+
+ @Override
+ public boolean linkToDeath() {
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error linking session to token death", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void unlinkToDeath() {
+ try {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.wtf(TAG, "Failed to unlink session to token death", e);
+ }
+ }
+
+ @Override
+ public void requestEnd(@NonNull Status status, @Nullable CallerInfo endedBy,
+ boolean immediate) {
+ // All requests to end a session should abort it to stop ongoing vibrations, even if
+ // immediate flag is false. Only the #finishSession API will not abort and wait for
+ // session vibrations to complete, which might take a long time.
+ requestEndSession(status, /* shouldAbort= */ true);
+ }
+
+ @Override
+ public void notifyVibratorCallback(int vibratorId, long vibrationId) {
+ // TODO(b/345414356): implement vibration support
+ }
+
+ @Override
+ public void notifySyncedVibratorsCallback(long vibrationId) {
+ // TODO(b/345414356): implement vibration support
+ }
+
+ @Override
+ public void notifySessionCallback() {
+ synchronized (mLock) {
+ // If end was not requested then the HAL has cancelled the session.
+ maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON);
+ maybeSetStatusToRequestedLocked();
+ }
+ mManagerHooks.onSessionReleased(mSessionId);
+ }
+
+ @Override
+ public String toString() {
+ synchronized (mLock) {
+ return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true)
+ + ", startTime: " + (mStartTime == 0 ? null : formatTime(mStartTime,
+ /* includeDate= */ true))
+ + ", endTime: " + (mEndTime == 0 ? null : formatTime(mEndTime,
+ /* includeDate= */ true))
+ + ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ + ", callerInfo: " + mCallerInfo
+ + ", vibratorIds: " + Arrays.toString(mVibratorIds);
+ }
+ }
+
+ public Status getStatus() {
+ synchronized (mLock) {
+ return mStatus;
+ }
+ }
+
+ public boolean isStarted() {
+ synchronized (mLock) {
+ return mStartTime > 0;
+ }
+ }
+
+ public boolean isEnded() {
+ synchronized (mLock) {
+ return mStatus != Status.RUNNING;
+ }
+ }
+
+ public int[] getVibratorIds() {
+ return mVibratorIds;
+ }
+
+ public ICancellationSignal getCancellationSignal() {
+ return mCancellationSignal;
+ }
+
+ public void notifyStart() {
+ boolean isAlreadyEnded = false;
+ synchronized (mLock) {
+ if (isEnded()) {
+ // Session already ended, skip start callbacks.
+ isAlreadyEnded = true;
+ } else {
+ mStartTime = System.currentTimeMillis();
+ // Run client callback in separate thread.
+ mHandler.post(() -> {
+ try {
+ mCallback.onStarted(this);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying vendor session started", e);
+ }
+ });
+ }
+ }
+ if (isAlreadyEnded) {
+ // Session already ended, make sure we end it in the HAL.
+ mManagerHooks.endSession(mSessionId, /* shouldAbort= */ true);
+ }
+ }
+
+ private void requestEndSession(Status status, boolean shouldAbort) {
+ boolean shouldTriggerSessionHook = false;
+ synchronized (mLock) {
+ maybeSetEndRequestLocked(status);
+ if (isStarted()) {
+ // Always trigger session hook after it has started, in case new request aborts an
+ // already finishing session. Wait for HAL callback before actually ending here.
+ shouldTriggerSessionHook = true;
+ } else {
+ // Session did not start in the HAL, end it right away.
+ maybeSetStatusToRequestedLocked();
+ }
+ }
+ if (shouldTriggerSessionHook) {
+ mManagerHooks.endSession(mSessionId, shouldAbort);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void maybeSetEndRequestLocked(Status status) {
+ if (mEndStatusRequest != null) {
+ // End already requested, keep first requested status and time.
+ return;
+ }
+ mEndStatusRequest = status;
+ mEndTime = System.currentTimeMillis();
+ mEndUptime = SystemClock.uptimeMillis();
+ if (isStarted()) {
+ // Only trigger "finishing" callback if session started.
+ // Run client callback in separate thread.
+ mHandler.post(() -> {
+ try {
+ mCallback.onFinishing();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying vendor session is finishing", e);
+ }
+ });
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void maybeSetStatusToRequestedLocked() {
+ if (isEnded()) {
+ // End already set, keep first requested status and time.
+ return;
+ }
+ if (mEndStatusRequest == null) {
+ // No end status was requested, nothing to set.
+ return;
+ }
+ mStatus = mEndStatusRequest;
+ // Run client callback in separate thread.
+ final Status endStatus = mStatus;
+ mHandler.post(() -> {
+ try {
+ mCallback.onFinished(toSessionStatus(endStatus));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying vendor session is finishing", e);
+ }
+ });
+ }
+
+ @android.os.vibrator.VendorVibrationSession.Status
+ private static int toSessionStatus(Status status) {
+ // Exhaustive switch to cover all possible internal status.
+ return switch (status) {
+ case FINISHED
+ -> android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS;
+ case IGNORED_UNSUPPORTED
+ -> STATUS_UNSUPPORTED;
+ case CANCELLED_BINDER_DIED, CANCELLED_BY_APP_OPS, CANCELLED_BY_USER,
+ CANCELLED_SUPERSEDED, CANCELLED_BY_FOREGROUND_USER, CANCELLED_BY_SCREEN_OFF,
+ CANCELLED_BY_SETTINGS_UPDATE, CANCELLED_BY_UNKNOWN_REASON
+ -> android.os.vibrator.VendorVibrationSession.STATUS_CANCELED;
+ case IGNORED_APP_OPS, IGNORED_BACKGROUND, IGNORED_FOR_EXTERNAL, IGNORED_FOR_ONGOING,
+ IGNORED_FOR_POWER, IGNORED_FOR_SETTINGS, IGNORED_FOR_HIGHER_IMPORTANCE,
+ IGNORED_FOR_RINGER_MODE, IGNORED_FROM_VIRTUAL_DEVICE, IGNORED_SUPERSEDED,
+ IGNORED_MISSING_PERMISSION, IGNORED_ON_WIRELESS_CHARGER
+ -> android.os.vibrator.VendorVibrationSession.STATUS_IGNORED;
+ case UNKNOWN, IGNORED_ERROR_APP_OPS, IGNORED_ERROR_CANCELLING, IGNORED_ERROR_SCHEDULING,
+ IGNORED_ERROR_TOKEN, FORWARDED_TO_INPUT_DEVICES, FINISHED_UNEXPECTED, RUNNING
+ -> android.os.vibrator.VendorVibrationSession.STATUS_UNKNOWN_ERROR;
+ };
+ }
+
+ /**
+ * Holds lightweight debug information about the session that could potentially be kept in
+ * memory for a long time for bugreport dumpsys operations.
+ *
+ * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to
+ * potentially expensive or resource-linked objects, such as {@link IBinder}.
+ */
+ static final class DebugInfoImpl implements VibrationSession.DebugInfo {
+ private final Status mStatus;
+ private final CallerInfo mCallerInfo;
+
+ private final long mCreateUptime;
+ private final long mCreateTime;
+ private final long mStartTime;
+ private final long mEndTime;
+ private final long mDurationMs;
+
+ DebugInfoImpl(Status status, CallerInfo callerInfo, long createUptime, long createTime,
+ long startTime, long endUptime, long endTime) {
+ mStatus = status;
+ mCallerInfo = callerInfo;
+ mCreateUptime = createUptime;
+ mCreateTime = createTime;
+ mStartTime = startTime;
+ mEndTime = endTime;
+ mDurationMs = endUptime > 0 ? endUptime - createUptime : -1;
+ }
+
+ @Override
+ public Status getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ public long getCreateUptimeMillis() {
+ return mCreateUptime;
+ }
+
+ @Override
+ public CallerInfo getCallerInfo() {
+ return mCallerInfo;
+ }
+
+ @Nullable
+ @Override
+ public Object getDumpAggregationKey() {
+ return null; // No aggregation.
+ }
+
+ @Override
+ public void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(VibrationProto.END_TIME, mEndTime);
+ proto.write(VibrationProto.DURATION_MS, mDurationMs);
+ proto.write(VibrationProto.STATUS, mStatus.ordinal());
+
+ final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
+ final VibrationAttributes attrs = mCallerInfo.attrs;
+ proto.write(VibrationAttributesProto.USAGE, attrs.getUsage());
+ proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage());
+ proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags());
+ proto.end(attrsToken);
+
+ proto.end(token);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VibrationSession:");
+ pw.increaseIndent();
+ pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
+ pw.println("durationMs = " + mDurationMs);
+ pw.println("createTime = " + formatTime(mCreateTime, /*includeDate=*/ true));
+ pw.println("startTime = " + formatTime(mStartTime, /*includeDate=*/ true));
+ pw.println("endTime = " + (mEndTime == 0 ? null
+ : formatTime(mEndTime, /*includeDate=*/ true)));
+ pw.println("callerInfo = " + mCallerInfo);
+ pw.decreaseIndent();
+ }
+
+ @Override
+ public void dumpCompact(IndentingPrintWriter pw) {
+ // Follow pattern from Vibration.DebugInfoImpl for better debugging from dumpsys.
+ String timingsStr = String.format(Locale.ROOT,
+ "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s",
+ formatTime(mCreateTime, /*includeDate=*/ true),
+ "session",
+ mStatus.name().toLowerCase(Locale.ROOT),
+ mDurationMs,
+ mStartTime == 0 ? "" : formatTime(mStartTime, /*includeDate=*/ false),
+ mEndTime == 0 ? "" : formatTime(mEndTime, /*includeDate=*/ false));
+ String paramStr = String.format(Locale.ROOT,
+ " | flags: %4s | usage: %s",
+ Long.toBinaryString(mCallerInfo.attrs.getFlags()),
+ mCallerInfo.attrs.usageToString());
+ // Optional, most vibrations should not be defined via AudioAttributes
+ // so skip them to simplify the logs
+ String audioUsageStr =
+ mCallerInfo.attrs.getOriginalAudioUsage() != AudioAttributes.USAGE_UNKNOWN
+ ? " | audioUsage=" + AudioAttributes.usageToString(
+ mCallerInfo.attrs.getOriginalAudioUsage())
+ : "";
+ String callerStr = String.format(Locale.ROOT,
+ " | %s (uid=%d, deviceId=%d) | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, mCallerInfo.reason);
+ pw.println(timingsStr + paramStr + audioUsageStr + callerStr);
+ }
+
+ @Override
+ public String toString() {
+ return "createTime: " + formatTime(mCreateTime, /* includeDate= */ true)
+ + ", startTime: " + formatTime(mStartTime, /* includeDate= */ true)
+ + ", endTime: " + (mEndTime == 0 ? null : formatTime(mEndTime,
+ /* includeDate= */ true))
+ + ", durationMs: " + mDurationMs
+ + ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ + ", callerInfo: " + mCallerInfo;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index bb2a17c698ee..27f92b2080e6 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -16,6 +16,8 @@
package com.android.server.vibrator;
+import static com.android.server.vibrator.VibrationSession.DebugInfo.formatTime;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioAttributes;
@@ -31,9 +33,6 @@ import android.os.vibrator.VibrationEffectSegment;
import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -42,11 +41,6 @@ import java.util.concurrent.atomic.AtomicInteger;
* The base class for all vibrations.
*/
abstract class Vibration {
- private static final DateTimeFormatter DEBUG_TIME_FORMATTER = DateTimeFormatter.ofPattern(
- "HH:mm:ss.SSS");
- private static final DateTimeFormatter DEBUG_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(
- "MM-dd HH:mm:ss.SSS");
-
// Used to generate globally unique vibration ids.
private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
@@ -399,12 +393,5 @@ abstract class Vibration {
proto.write(PrimitiveSegmentProto.DELAY, segment.getDelay());
proto.end(token);
}
-
- private String formatTime(long timeInMillis, boolean includeDate) {
- return (includeDate ? DEBUG_DATE_TIME_FORMATTER : DEBUG_TIME_FORMATTER)
- // Ensure timezone is retrieved at formatting time
- .withZone(ZoneId.systemDefault())
- .format(Instant.ofEpochMilli(timeInMillis));
- }
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java
index b511ba8be405..ae95a70e2a4f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSession.java
@@ -25,7 +25,11 @@ import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a generic vibration session that plays one or more vibration requests.
@@ -39,6 +43,16 @@ import java.util.Objects;
*/
interface VibrationSession {
+ // Used to generate globally unique session ids.
+ AtomicInteger sNextSessionId = new AtomicInteger(1); // 0 = no callback
+
+ static long nextSessionId() {
+ return sNextSessionId.getAndIncrement();
+ }
+
+ /** Returns the session id. */
+ long getSessionId();
+
/** Returns the session creation time from {@link android.os.SystemClock#uptimeMillis()}. */
long getCreateUptimeMillis();
@@ -105,6 +119,14 @@ interface VibrationSession {
void notifySyncedVibratorsCallback(long vibrationId);
/**
+ * Notify vibrator manager have completed the vibration session.
+ *
+ * <p>This will be called by the vibrator manager hardware callback indicating the session
+ * is complete, either because it was ended or cancelled by the service or the vendor.
+ */
+ void notifySessionCallback();
+
+ /**
* Session status with reference to values from vibratormanagerservice.proto for logging.
*/
enum Status {
@@ -212,6 +234,17 @@ interface VibrationSession {
*/
interface DebugInfo {
+ DateTimeFormatter DEBUG_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
+ DateTimeFormatter DEBUG_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(
+ "MM-dd HH:mm:ss.SSS");
+
+ static String formatTime(long timeInMillis, boolean includeDate) {
+ return (includeDate ? DEBUG_DATE_TIME_FORMATTER : DEBUG_TIME_FORMATTER)
+ // Ensure timezone is retrieved at formatting time
+ .withZone(ZoneId.systemDefault())
+ .format(Instant.ofEpochMilli(timeInMillis));
+ }
+
/** Return the vibration session status. */
Status getStatus();
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index ff3491182a5f..476448148e28 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -32,6 +32,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
@@ -40,6 +41,7 @@ import android.os.ExternalVibration;
import android.os.ExternalVibrationScale;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.IExternalVibratorService;
import android.os.IVibratorManagerService;
import android.os.IVibratorStateListener;
@@ -57,6 +59,7 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.Flags;
+import android.os.vibrator.IVibrationSessionCallback;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
@@ -103,7 +106,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
private static final String VIBRATOR_CONTROL_SERVICE =
"android.frameworks.vibrator.IVibratorControlService/default";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
@@ -159,12 +162,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
new VibrationThreadCallbacks();
private final ExternalVibrationCallbacks mExternalVibrationCallbacks =
new ExternalVibrationCallbacks();
+ private final VendorVibrationSessionCallbacks mVendorVibrationSessionCallbacks =
+ new VendorVibrationSessionCallbacks();
@GuardedBy("mLock")
private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
@GuardedBy("mLock")
- private VibrationSession mCurrentVibration;
+ private VibrationSession mCurrentSession;
@GuardedBy("mLock")
- private VibrationSession mNextVibration;
+ private VibrationSession mNextSession;
@GuardedBy("mLock")
private boolean mServiceReady;
@@ -191,14 +196,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// When the system is entering a non-interactive state, we want to cancel
// vibrations in case a misbehaving app has abandoned them.
synchronized (mLock) {
- maybeClearCurrentAndNextVibrationsLocked(
+ maybeClearCurrentAndNextSessionsLocked(
VibratorManagerService.this::shouldCancelOnScreenOffLocked,
Status.CANCELLED_BY_SCREEN_OFF);
}
} else if (android.multiuser.Flags.addUiForSoundsFromBackgroundUsers()
&& intent.getAction().equals(BackgroundUserSoundNotifier.ACTION_MUTE_SOUND)) {
synchronized (mLock) {
- maybeClearCurrentAndNextVibrationsLocked(
+ maybeClearCurrentAndNextSessionsLocked(
VibratorManagerService.this::shouldCancelOnFgUserRequest,
Status.CANCELLED_BY_FOREGROUND_USER);
}
@@ -215,14 +220,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return;
}
synchronized (mLock) {
- maybeClearCurrentAndNextVibrationsLocked(
+ maybeClearCurrentAndNextSessionsLocked(
VibratorManagerService.this::shouldCancelAppOpModeChangedLocked,
Status.CANCELLED_BY_APP_OPS);
}
}
};
- static native long nativeInit(OnSyncedVibrationCompleteListener listener);
+ static native long nativeInit(VibratorManagerNativeCallbacks listener);
static native long nativeGetFinalizer();
@@ -236,6 +241,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
static native void nativeCancelSynced(long nativeServicePtr);
+ static native boolean nativeStartSession(long nativeServicePtr, long sessionId,
+ int[] vibratorIds);
+
+ static native void nativeEndSession(long nativeServicePtr, long sessionId, boolean shouldAbort);
+
+ static native void nativeClearSessions(long nativeServicePtr);
+
@VisibleForTesting
VibratorManagerService(Context context, Injector injector) {
mContext = context;
@@ -303,6 +315,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Reset the hardware to a default state, in case this is a runtime restart instead of a
// fresh boot.
mNativeWrapper.cancelSynced();
+ if (Flags.vendorVibrationEffects()) {
+ mNativeWrapper.clearSessions();
+ }
for (int i = 0; i < mVibrators.size(); i++) {
mVibrators.valueAt(i).reset();
}
@@ -363,6 +378,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override // Binder call
+ public int getCapabilities() {
+ return (int) mCapabilities;
+ }
+
+ @Override // Binder call
@Nullable
public VibratorInfo getVibratorInfo(int vibratorId) {
final VibratorController controller = mVibrators.get(vibratorId);
@@ -590,11 +610,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_ERROR_TOKEN);
return null;
}
- if (effect.hasVendorEffects()
- && !hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) {
- Slog.e(TAG, "vibrate; no permission for vendor effects");
- logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_MISSING_PERMISSION);
- return null;
+ if (effect.hasVendorEffects()) {
+ if (!Flags.vendorVibrationEffects()) {
+ Slog.e(TAG, "vibrate; vendor effects feature disabled");
+ logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED);
+ return null;
+ }
+ if (!hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) {
+ Slog.e(TAG, "vibrate; no permission for vendor effects");
+ logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_MISSING_PERMISSION);
+ return null;
+ }
}
enforceUpdateAppOpsStatsPermission(uid);
if (!isEffectValid(effect)) {
@@ -623,7 +649,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Check if ongoing vibration is more important than this vibration.
if (ignoreStatus == null) {
- Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(session);
+ Vibration.EndInfo vibrationEndInfo = shouldIgnoreForOngoingLocked(session);
if (vibrationEndInfo != null) {
ignoreStatus = vibrationEndInfo.status;
ignoredBy = vibrationEndInfo.endedBy;
@@ -634,8 +660,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (ignoreStatus == null) {
final long ident = Binder.clearCallingIdentity();
try {
- if (mCurrentVibration != null) {
- if (shouldPipelineVibrationLocked(mCurrentVibration, vib)) {
+ if (mCurrentSession != null) {
+ if (shouldPipelineVibrationLocked(mCurrentSession, vib)) {
// Don't cancel the current vibration if it's pipeline-able.
// Note that if there is a pending next vibration that can't be
// pipelined, it will have already cancelled the current one, so we
@@ -645,12 +671,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
} else {
vib.stats.reportInterruptedAnotherVibration(
- mCurrentVibration.getCallerInfo());
- mCurrentVibration.requestEnd(Status.CANCELLED_SUPERSEDED, callerInfo,
+ mCurrentSession.getCallerInfo());
+ mCurrentSession.requestEnd(Status.CANCELLED_SUPERSEDED, callerInfo,
/* immediate= */ false);
}
}
- clearNextVibrationLocked(Status.CANCELLED_SUPERSEDED, callerInfo);
+ clearNextSessionLocked(Status.CANCELLED_SUPERSEDED, callerInfo);
ignoreStatus = startVibrationLocked(session);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -659,7 +685,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Ignored or failed to start the vibration, end it and report metrics right away.
if (ignoreStatus != null) {
- endVibrationLocked(session, ignoreStatus, ignoredBy);
+ endSessionLocked(session, ignoreStatus, ignoredBy);
}
return vib;
}
@@ -681,19 +707,154 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
try {
// TODO(b/370948466): investigate why token not checked on external vibrations.
IBinder cancelToken =
- (mNextVibration instanceof ExternalVibrationSession) ? null : token;
- if (shouldCancelVibration(mNextVibration, usageFilter, cancelToken)) {
- clearNextVibrationLocked(Status.CANCELLED_BY_USER);
+ (mNextSession instanceof ExternalVibrationSession) ? null : token;
+ if (shouldCancelSession(mNextSession, usageFilter, cancelToken)) {
+ clearNextSessionLocked(Status.CANCELLED_BY_USER);
}
cancelToken =
- (mCurrentVibration instanceof ExternalVibrationSession) ? null : token;
- if (shouldCancelVibration(mCurrentVibration, usageFilter, cancelToken)) {
- mCurrentVibration.requestEnd(Status.CANCELLED_BY_USER);
+ (mCurrentSession instanceof ExternalVibrationSession) ? null : token;
+ if (shouldCancelSession(mCurrentSession, usageFilter, cancelToken)) {
+ mCurrentSession.requestEnd(Status.CANCELLED_BY_USER);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @android.annotation.EnforcePermission(allOf = {
+ android.Manifest.permission.VIBRATE,
+ android.Manifest.permission.VIBRATE_VENDOR_EFFECTS,
+ android.Manifest.permission.START_VIBRATION_SESSIONS,
+ })
+ @Override // Binder call
+ public ICancellationSignal startVendorVibrationSession(int uid, int deviceId, String opPkg,
+ int[] vibratorIds, VibrationAttributes attrs, String reason,
+ IVibrationSessionCallback callback) {
+ startVendorVibrationSession_enforcePermission();
+ Trace.traceBegin(TRACE_TAG_VIBRATOR, "startVibrationSession");
+ try {
+ VendorVibrationSession session = startVendorVibrationSessionInternal(
+ uid, deviceId, opPkg, vibratorIds, attrs, reason, callback);
+ return session == null ? null : session.getCancellationSignal();
+ } finally {
+ Trace.traceEnd(TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ VendorVibrationSession startVendorVibrationSessionInternal(int uid, int deviceId, String opPkg,
+ int[] vibratorIds, VibrationAttributes attrs, String reason,
+ IVibrationSessionCallback callback) {
+ if (!Flags.vendorVibrationEffects()) {
+ throw new UnsupportedOperationException("Vibration sessions not supported");
+ }
+ attrs = fixupVibrationAttributes(attrs, /* effect= */ null);
+ CallerInfo callerInfo = new CallerInfo(attrs, uid, deviceId, opPkg, reason);
+ if (callback == null) {
+ Slog.e(TAG, "session callback must not be null");
+ logAndRecordSessionAttempt(callerInfo, Status.IGNORED_ERROR_TOKEN);
+ return null;
+ }
+ if (vibratorIds == null) {
+ vibratorIds = new int[0];
+ }
+ enforceUpdateAppOpsStatsPermission(uid);
+ VendorVibrationSession session = new VendorVibrationSession(callerInfo, mHandler,
+ mVendorVibrationSessionCallbacks, vibratorIds, callback);
+
+ if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
+ // Force update of user settings before checking if this vibration effect should
+ // be ignored or scaled.
+ mVibrationSettings.update();
+ }
+
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting session " + session.getSessionId());
+ }
+
+ Status ignoreStatus = null;
+ CallerInfo ignoredBy = null;
+
+ // Check if HAL has capability to start sessions.
+ if ((mCapabilities & IVibratorManager.CAP_START_SESSIONS) == 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Missing capability to start sessions, ignoring request");
+ }
+ ignoreStatus = Status.IGNORED_UNSUPPORTED;
+ }
+
+ // Check if any vibrator ID was requested.
+ if (ignoreStatus == null && vibratorIds.length == 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Empty vibrator ids to start session, ignoring request");
+ }
+ ignoreStatus = Status.IGNORED_UNSUPPORTED;
+ }
+
+ // Check if user settings or DnD is set to ignore this session.
+ if (ignoreStatus == null) {
+ ignoreStatus = shouldIgnoreVibrationLocked(callerInfo);
+ }
+
+ // Check if ongoing vibration is more important than this session.
+ if (ignoreStatus == null) {
+ Vibration.EndInfo vibrationEndInfo = shouldIgnoreForOngoingLocked(session);
+ if (vibrationEndInfo != null) {
+ ignoreStatus = vibrationEndInfo.status;
+ ignoredBy = vibrationEndInfo.endedBy;
+ }
+ }
+
+ if (ignoreStatus == null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // If not ignored so far then stop ongoing sessions before starting this one.
+ clearNextSessionLocked(Status.CANCELLED_SUPERSEDED, callerInfo);
+ if (mCurrentSession != null) {
+ mNextSession = session;
+ mCurrentSession.requestEnd(Status.CANCELLED_SUPERSEDED, callerInfo,
+ /* immediate= */ false);
+ } else {
+ ignoreStatus = startVendorSessionLocked(session);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+
+ // Ignored or failed to start the session, end it and report metrics right away.
+ if (ignoreStatus != null) {
+ endSessionLocked(session, ignoreStatus, ignoredBy);
+ }
+ return session;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private Status startVendorSessionLocked(VendorVibrationSession session) {
+ Trace.traceBegin(TRACE_TAG_VIBRATOR, "startSessionLocked");
+ try {
+ if (session.isEnded()) {
+ // Session already ended, possibly cancelled by app cancellation signal.
+ return session.getStatus();
+ }
+ if (!session.linkToDeath()) {
+ return Status.IGNORED_ERROR_TOKEN;
+ }
+ if (!mNativeWrapper.startSession(session.getSessionId(), session.getVibratorIds())) {
+ Slog.e(TAG, "Error starting session " + session.getSessionId()
+ + " on vibrators " + Arrays.toString(session.getVibratorIds()));
+ session.unlinkToDeath();
+ return Status.IGNORED_UNSUPPORTED;
+ }
+ session.notifyStart();
+ mCurrentSession = session;
+ return null;
} finally {
Trace.traceEnd(TRACE_TAG_VIBRATOR);
}
@@ -747,8 +908,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println("CurrentVibration:");
pw.increaseIndent();
- if (mCurrentVibration != null) {
- mCurrentVibration.getDebugInfo().dump(pw);
+ if (mCurrentSession != null) {
+ mCurrentSession.getDebugInfo().dump(pw);
} else {
pw.println("null");
}
@@ -757,8 +918,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println("NextVibration:");
pw.increaseIndent();
- if (mNextVibration != null) {
- mNextVibration.getDebugInfo().dump(pw);
+ if (mNextSession != null) {
+ mNextSession.getDebugInfo().dump(pw);
} else {
pw.println("null");
}
@@ -782,8 +943,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
synchronized (mLock) {
mVibrationSettings.dump(proto);
mVibrationScaler.dump(proto);
- if (mCurrentVibration != null) {
- mCurrentVibration.getDebugInfo().dump(proto,
+ if (mCurrentSession != null) {
+ mCurrentSession.getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
}
for (int i = 0; i < mVibrators.size(); i++) {
@@ -816,18 +977,18 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
// TODO(b/372241975): investigate why external vibrations were not handled here before
- if (mCurrentVibration == null
- || (mCurrentVibration instanceof ExternalVibrationSession)) {
+ if (mCurrentSession == null
+ || (mCurrentSession instanceof ExternalVibrationSession)) {
return;
}
- Status ignoreStatus = shouldIgnoreVibrationLocked(mCurrentVibration.getCallerInfo());
+ Status ignoreStatus = shouldIgnoreVibrationLocked(mCurrentSession.getCallerInfo());
if (inputDevicesChanged || (ignoreStatus != null)) {
if (DEBUG) {
Slog.d(TAG, "Canceling vibration because settings changed: "
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
}
- mCurrentVibration.requestEnd(Status.CANCELLED_BY_SETTINGS_UPDATE);
+ mCurrentSession.requestEnd(Status.CANCELLED_BY_SETTINGS_UPDATE);
}
}
}
@@ -866,15 +1027,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mInputDeviceDelegate.isAvailable()) {
return startVibrationOnInputDevicesLocked(session.getVibration());
}
- if (mCurrentVibration == null) {
+ if (mCurrentSession == null) {
return startVibrationOnThreadLocked(session);
}
// If there's already a vibration queued (waiting for the previous one to finish
// cancelling), end it cleanly and replace it with the new one.
// Note that we don't consider pipelining here, because new pipelined ones should
// replace pending non-executing pipelined ones anyway.
- clearNextVibrationLocked(Status.IGNORED_SUPERSEDED, session.getCallerInfo());
- mNextVibration = session;
+ clearNextSessionLocked(Status.IGNORED_SUPERSEDED, session.getCallerInfo());
+ mNextSession = session;
return null;
} finally {
Trace.traceEnd(TRACE_TAG_VIBRATOR);
@@ -891,16 +1052,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
case AppOpsManager.MODE_ALLOWED:
Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0);
// Make sure mCurrentVibration is set while triggering the VibrationThread.
- mCurrentVibration = session;
- if (!mCurrentVibration.linkToDeath()) {
+ mCurrentSession = session;
+ if (!mCurrentSession.linkToDeath()) {
// Shouldn't happen. The method call already logs.
- mCurrentVibration = null; // Aborted.
+ mCurrentSession = null; // Aborted.
return Status.IGNORED_ERROR_TOKEN;
}
if (!mVibrationThread.runVibrationOnVibrationThread(conductor)) {
// Shouldn't happen. The method call already logs.
session.setVibrationConductor(null); // Rejected by thread, clear it in session.
- mCurrentVibration = null; // Aborted.
+ mCurrentSession = null; // Aborted.
return Status.IGNORED_ERROR_SCHEDULING;
}
return null;
@@ -914,23 +1075,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@GuardedBy("mLock")
- private void maybeStartNextSingleVibrationLocked() {
- if (mNextVibration instanceof SingleVibrationSession session) {
- mNextVibration = null;
+ private void maybeStartNextSessionLocked() {
+ if (mNextSession instanceof SingleVibrationSession session) {
+ mNextSession = null;
Status errorStatus = startVibrationOnThreadLocked(session);
if (errorStatus != null) {
- endVibrationLocked(session, errorStatus);
+ endSessionLocked(session, errorStatus);
}
- }
+ } else if (mNextSession instanceof VendorVibrationSession session) {
+ mNextSession = null;
+ Status errorStatus = startVendorSessionLocked(session);
+ if (errorStatus != null) {
+ endSessionLocked(session, errorStatus);
+ }
+ } // External vibrations cannot be started asynchronously.
}
@GuardedBy("mLock")
- private void endVibrationLocked(VibrationSession session, Status status) {
- endVibrationLocked(session, status, /* endedBy= */ null);
+ private void endSessionLocked(VibrationSession session, Status status) {
+ endSessionLocked(session, status, /* endedBy= */ null);
}
@GuardedBy("mLock")
- private void endVibrationLocked(VibrationSession session, Status status, CallerInfo endedBy) {
+ private void endSessionLocked(VibrationSession session, Status status, CallerInfo endedBy) {
session.requestEnd(status, endedBy, /* immediate= */ false);
logAndRecordVibration(session.getDebugInfo());
}
@@ -975,6 +1142,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibrationScaler.ADAPTIVE_SCALE_NONE));
}
+ private void logAndRecordSessionAttempt(CallerInfo callerInfo, Status status) {
+ logAndRecordVibration(
+ new VendorVibrationSession.DebugInfoImpl(status, callerInfo,
+ SystemClock.uptimeMillis(), System.currentTimeMillis(),
+ /* startTime= */ 0, /* endUptime= */ 0, /* endTime= */ 0));
+ }
+
private void logAndRecordVibration(DebugInfo info) {
info.logMetrics(mFrameworkStatsLogger);
logVibrationStatus(info.getCallerInfo().uid, info.getCallerInfo().attrs, info.getStatus());
@@ -1026,25 +1200,40 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ private void onVibrationSessionComplete(long sessionId) {
+ synchronized (mLock) {
+ if (mCurrentSession == null || mCurrentSession.getSessionId() != sessionId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration session " + sessionId + " callback ignored");
+ }
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration session " + sessionId + " complete, notifying session");
+ }
+ mCurrentSession.notifySessionCallback();
+ }
+ }
+
private void onSyncedVibrationComplete(long vibrationId) {
synchronized (mLock) {
- if (mCurrentVibration != null) {
+ if (mCurrentSession != null) {
if (DEBUG) {
Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
}
- mCurrentVibration.notifySyncedVibratorsCallback(vibrationId);
+ mCurrentSession.notifySyncedVibratorsCallback(vibrationId);
}
}
}
private void onVibrationComplete(int vibratorId, long vibrationId) {
synchronized (mLock) {
- if (mCurrentVibration != null) {
+ if (mCurrentSession != null) {
if (DEBUG) {
Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
+ " complete, notifying thread");
}
- mCurrentVibration.notifyVibratorCallback(vibratorId, vibrationId);
+ mCurrentSession.notifyVibratorCallback(vibratorId, vibrationId);
}
}
}
@@ -1056,10 +1245,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*/
@GuardedBy("mLock")
@Nullable
- private Vibration.EndInfo shouldIgnoreVibrationForOngoingLocked(VibrationSession session) {
- if (mNextVibration != null) {
- Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationForOngoing(session,
- mNextVibration);
+ private Vibration.EndInfo shouldIgnoreForOngoingLocked(VibrationSession session) {
+ if (mNextSession != null) {
+ Vibration.EndInfo vibrationEndInfo = shouldIgnoreForOngoing(session,
+ mNextSession);
if (vibrationEndInfo != null) {
// Next vibration has higher importance than the new one, so the new vibration
// should be ignored.
@@ -1067,13 +1256,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
- if (mCurrentVibration != null) {
- if (mCurrentVibration.wasEndRequested()) {
+ if (mCurrentSession != null) {
+ if (mCurrentSession.wasEndRequested()) {
// Current session has ended or is cancelling, should not block incoming vibrations.
return null;
}
- return shouldIgnoreVibrationForOngoing(session, mCurrentVibration);
+ return shouldIgnoreForOngoing(session, mCurrentSession);
}
return null;
@@ -1086,7 +1275,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* @return a Vibration.EndInfo if the vibration should be ignored, null otherwise.
*/
@Nullable
- private static Vibration.EndInfo shouldIgnoreVibrationForOngoing(
+ private static Vibration.EndInfo shouldIgnoreForOngoing(
@NonNull VibrationSession newSession, @NonNull VibrationSession ongoingSession) {
int newSessionImportance = getVibrationImportance(newSession);
@@ -1214,11 +1403,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
* @param tokenFilter The binder token to identify the vibration origin. Only vibrations
* started with the same token can be cancelled with it.
*/
- private boolean shouldCancelVibration(@Nullable VibrationSession session, int usageFilter,
+ private boolean shouldCancelSession(@Nullable VibrationSession session, int usageFilter,
@Nullable IBinder tokenFilter) {
if (session == null) {
return false;
}
+ if (session instanceof VendorVibrationSession) {
+ // Vendor sessions should not be cancelled by Vibrator.cancel API.
+ return false;
+ }
if ((tokenFilter != null) && (tokenFilter != session.getCallerToken())) {
// Vibration from a different app, this should not cancel it.
return false;
@@ -1572,10 +1765,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "onVibrationThreadReleased");
try {
synchronized (mLock) {
- if (!(mCurrentVibration instanceof SingleVibrationSession session)) {
+ if (!(mCurrentSession instanceof SingleVibrationSession session)) {
if (Build.IS_DEBUGGABLE) {
Slog.wtf(TAG, "VibrationSession invalid on vibration thread release."
- + " currentSession=" + mCurrentVibration);
+ + " currentSession=" + mCurrentSession);
}
// Only single vibration sessions are ended by thread being released. Abort.
return;
@@ -1586,11 +1779,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
+ " expected=%d, released=%d",
session.getVibration().id, vibrationId));
}
- finishAppOpModeLocked(mCurrentVibration.getCallerInfo());
- clearCurrentVibrationLocked();
+ finishAppOpModeLocked(mCurrentSession.getCallerInfo());
+ clearCurrentSessionLocked();
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
- // Start next vibration if it's a single vibration waiting for the thread.
- maybeStartNextSingleVibrationLocked();
+ // Start next vibration if it's waiting for the thread.
+ maybeStartNextSessionLocked();
}
} finally {
Trace.traceEnd(TRACE_TAG_VIBRATOR);
@@ -1613,10 +1806,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationReleased");
try {
synchronized (mLock) {
- if (!(mCurrentVibration instanceof ExternalVibrationSession session)) {
+ if (!(mCurrentSession instanceof ExternalVibrationSession session)) {
if (Build.IS_DEBUGGABLE) {
Slog.wtf(TAG, "VibrationSession invalid on external vibration release."
- + " currentSession=" + mCurrentVibration);
+ + " currentSession=" + mCurrentSession);
}
// Only external vibration sessions are ended by this callback. Abort.
return;
@@ -1627,10 +1820,62 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
+ " expected=%d, released=%d", session.id, vibrationId));
}
setExternalControl(false, session.stats);
- clearCurrentVibrationLocked();
- // Start next vibration if it's a single vibration waiting for the external
- // control to be over.
- maybeStartNextSingleVibrationLocked();
+ clearCurrentSessionLocked();
+ // Start next vibration if it's waiting for the external control to be over.
+ maybeStartNextSessionLocked();
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_VIBRATOR);
+ }
+ }
+ }
+
+ /**
+ * Implementation of {@link ExternalVibrationSession.VibratorManagerHooks} that controls
+ * external vibrations and reports them when finished.
+ */
+ private final class VendorVibrationSessionCallbacks
+ implements VendorVibrationSession.VibratorManagerHooks {
+
+ @Override
+ public void endSession(long sessionId, boolean shouldAbort) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration session " + sessionId
+ + (shouldAbort ? " aborting" : " ending"));
+ }
+ Trace.traceBegin(TRACE_TAG_VIBRATOR, "endSession");
+ try {
+ mNativeWrapper.endSession(sessionId, shouldAbort);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ @Override
+ public void onSessionReleased(long sessionId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration session " + sessionId + " released");
+ }
+ Trace.traceBegin(TRACE_TAG_VIBRATOR, "onVendorSessionReleased");
+ try {
+ synchronized (mLock) {
+ if (!(mCurrentSession instanceof VendorVibrationSession session)) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, "VibrationSession invalid on vibration session release."
+ + " currentSession=" + mCurrentSession);
+ }
+ // Only vendor vibration sessions are ended by this callback. Abort.
+ return;
+ }
+ if (Build.IS_DEBUGGABLE && (session.getSessionId() != sessionId)) {
+ Slog.wtf(TAG, TextUtils.formatSimple(
+ "SessionId mismatch on vendor vibration session release."
+ + " expected=%d, released=%d",
+ session.getSessionId(), sessionId));
+ }
+ clearCurrentSessionLocked();
+ // Start next vibration if it's waiting for the HAL session to be over.
+ maybeStartNextSessionLocked();
}
} finally {
Trace.traceEnd(TRACE_TAG_VIBRATOR);
@@ -1638,19 +1883,22 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
- /** Listener for synced vibration completion callbacks from native. */
+ /** Listener for vibrator manager completion callbacks from native. */
@VisibleForTesting
- interface OnSyncedVibrationCompleteListener {
+ interface VibratorManagerNativeCallbacks {
/** Callback triggered when synced vibration is complete. */
- void onComplete(long vibrationId);
+ void onSyncedVibrationComplete(long vibrationId);
+
+ /** Callback triggered when vibration session is complete. */
+ void onVibrationSessionComplete(long sessionId);
}
/**
* Implementation of listeners to native vibrators with a weak reference to this service.
*/
private static final class VibrationCompleteListener implements
- VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener {
+ VibratorController.OnVibrationCompleteListener, VibratorManagerNativeCallbacks {
private WeakReference<VibratorManagerService> mServiceRef;
VibrationCompleteListener(VibratorManagerService service) {
@@ -1658,7 +1906,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override
- public void onComplete(long vibrationId) {
+ public void onSyncedVibrationComplete(long vibrationId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
service.onSyncedVibrationComplete(vibrationId);
@@ -1666,6 +1914,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override
+ public void onVibrationSessionComplete(long sessionId) {
+ VibratorManagerService service = mServiceRef.get();
+ if (service != null) {
+ service.onVibrationSessionComplete(sessionId);
+ }
+ }
+
+ @Override
public void onComplete(int vibratorId, long vibrationId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
@@ -1698,7 +1954,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private long mNativeServicePtr = 0;
/** Returns native pointer to newly created controller and connects with HAL service. */
- public void init(OnSyncedVibrationCompleteListener listener) {
+ public void init(VibratorManagerNativeCallbacks listener) {
mNativeServicePtr = nativeInit(listener);
long finalizerPtr = nativeGetFinalizer();
@@ -1734,6 +1990,21 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public void cancelSynced() {
nativeCancelSynced(mNativeServicePtr);
}
+
+ /** Start vibration session. */
+ public boolean startSession(long sessionId, @NonNull int[] vibratorIds) {
+ return nativeStartSession(mNativeServicePtr, sessionId, vibratorIds);
+ }
+
+ /** End vibration session. */
+ public void endSession(long sessionId, boolean shouldAbort) {
+ nativeEndSession(mNativeServicePtr, sessionId, shouldAbort);
+ }
+
+ /** Clear vibration sessions. */
+ public void clearSessions() {
+ nativeClearSessions(mNativeServicePtr);
+ }
}
/** Keep records of vibrations played and provide debug information for this service. */
@@ -1853,46 +2124,46 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
/** Clears mNextVibration if set, ending it cleanly */
@GuardedBy("mLock")
- private void clearNextVibrationLocked(Status status) {
- clearNextVibrationLocked(status, /* endedBy= */ null);
+ private void clearNextSessionLocked(Status status) {
+ clearNextSessionLocked(status, /* endedBy= */ null);
}
/** Clears mNextVibration if set, ending it cleanly */
@GuardedBy("mLock")
- private void clearNextVibrationLocked(Status status, CallerInfo endedBy) {
- if (mNextVibration != null) {
+ private void clearNextSessionLocked(Status status, CallerInfo endedBy) {
+ if (mNextSession != null) {
if (DEBUG) {
- Slog.d(TAG, "Dropping pending vibration from " + mNextVibration.getCallerInfo()
+ Slog.d(TAG, "Dropping pending vibration from " + mNextSession.getCallerInfo()
+ " with status: " + status);
}
// Clearing next vibration before playing it, end it and report metrics right away.
- endVibrationLocked(mNextVibration, status, endedBy);
- mNextVibration = null;
+ endSessionLocked(mNextSession, status, endedBy);
+ mNextSession = null;
}
}
/** Clears mCurrentVibration if set, reporting metrics */
@GuardedBy("mLock")
- private void clearCurrentVibrationLocked() {
- if (mCurrentVibration != null) {
- mCurrentVibration.unlinkToDeath();
- logAndRecordVibration(mCurrentVibration.getDebugInfo());
- mCurrentVibration = null;
+ private void clearCurrentSessionLocked() {
+ if (mCurrentSession != null) {
+ mCurrentSession.unlinkToDeath();
+ logAndRecordVibration(mCurrentSession.getDebugInfo());
+ mCurrentSession = null;
mLock.notify(); // Notify if waiting for current vibration to end.
}
}
@GuardedBy("mLock")
- private void maybeClearCurrentAndNextVibrationsLocked(
+ private void maybeClearCurrentAndNextSessionsLocked(
Predicate<VibrationSession> shouldEndSessionPredicate, Status endStatus) {
// TODO(b/372241975): investigate why external vibrations were not handled here before
- if (!(mNextVibration instanceof ExternalVibrationSession)
- && shouldEndSessionPredicate.test(mNextVibration)) {
- clearNextVibrationLocked(endStatus);
+ if (!(mNextSession instanceof ExternalVibrationSession)
+ && shouldEndSessionPredicate.test(mNextSession)) {
+ clearNextSessionLocked(endStatus);
}
- if (!(mCurrentVibration instanceof ExternalVibrationSession)
- && shouldEndSessionPredicate.test(mCurrentVibration)) {
- mCurrentVibration.requestEnd(endStatus);
+ if (!(mCurrentSession instanceof ExternalVibrationSession)
+ && shouldEndSessionPredicate.test(mCurrentSession)) {
+ mCurrentSession.requestEnd(endStatus);
}
}
@@ -1902,12 +2173,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*
* @return true if the vibration completed, or false if waiting timed out.
*/
- public boolean waitForCurrentVibrationEnd(long maxWaitMillis) {
+ public boolean waitForCurrentSessionEnd(long maxWaitMillis) {
long now = SystemClock.elapsedRealtime();
long deadline = now + maxWaitMillis;
synchronized (mLock) {
while (true) {
- if (mCurrentVibration == null) {
+ if (mCurrentSession == null) {
return true; // Done
}
if (now >= deadline) { // Note that thread.wait(0) waits indefinitely.
@@ -1965,7 +2236,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
synchronized (mLock) {
if (!hasExternalControlCapability()) {
- endVibrationLocked(session, Status.IGNORED_UNSUPPORTED);
+ endSessionLocked(session, Status.IGNORED_UNSUPPORTED);
return session.getScale();
}
@@ -1976,17 +2247,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ " tried to play externally controlled vibration"
+ " without VIBRATE permission, ignoring.");
- endVibrationLocked(session, Status.IGNORED_MISSING_PERMISSION);
+ endSessionLocked(session, Status.IGNORED_MISSING_PERMISSION);
return session.getScale();
}
Status ignoreStatus = shouldIgnoreVibrationLocked(session.callerInfo);
if (ignoreStatus != null) {
- endVibrationLocked(session, ignoreStatus);
+ endSessionLocked(session, ignoreStatus);
return session.getScale();
}
- if ((mCurrentVibration instanceof ExternalVibrationSession evs)
+ if ((mCurrentSession instanceof ExternalVibrationSession evs)
&& evs.isHoldingSameVibration(vib)) {
// We are already playing this external vibration, so we can return the same
// scale calculated in the previous call to this method.
@@ -1994,17 +2265,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
// Check if ongoing vibration is more important than this vibration.
- Vibration.EndInfo ignoreInfo = shouldIgnoreVibrationForOngoingLocked(session);
+ Vibration.EndInfo ignoreInfo = shouldIgnoreForOngoingLocked(session);
if (ignoreInfo != null) {
- endVibrationLocked(session, ignoreInfo.status, ignoreInfo.endedBy);
+ endSessionLocked(session, ignoreInfo.status, ignoreInfo.endedBy);
return session.getScale();
}
// First clear next request, so it won't start when the current one ends.
- clearNextVibrationLocked(Status.IGNORED_FOR_EXTERNAL, session.callerInfo);
- mNextVibration = session;
+ clearNextSessionLocked(Status.IGNORED_FOR_EXTERNAL, session.callerInfo);
+ mNextSession = session;
- if (mCurrentVibration != null) {
+ if (mCurrentSession != null) {
// Cancel any vibration that may be playing and ready the vibrator, even if
// we have an externally controlled vibration playing already.
// Since the interface defines that only one externally controlled
@@ -2016,36 +2287,36 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// as we would need to mute the old one still if it came from a different
// controller.
session.stats.reportInterruptedAnotherVibration(
- mCurrentVibration.getCallerInfo());
- mCurrentVibration.requestEnd(Status.CANCELLED_SUPERSEDED,
+ mCurrentSession.getCallerInfo());
+ mCurrentSession.requestEnd(Status.CANCELLED_SUPERSEDED,
session.callerInfo, /* immediate= */ true);
waitForCompletion = true;
}
}
// Wait for lock and interact with HAL to set external control outside main lock.
if (waitForCompletion) {
- if (!waitForCurrentVibrationEnd(VIBRATION_CANCEL_WAIT_MILLIS)) {
+ if (!waitForCurrentSessionEnd(VIBRATION_CANCEL_WAIT_MILLIS)) {
Slog.e(TAG, "Timed out waiting for vibration to cancel");
synchronized (mLock) {
- if (mNextVibration == session) {
- mNextVibration = null;
+ if (mNextSession == session) {
+ mNextSession = null;
}
- endVibrationLocked(session, Status.IGNORED_ERROR_CANCELLING);
+ endSessionLocked(session, Status.IGNORED_ERROR_CANCELLING);
return session.getScale();
}
}
}
synchronized (mLock) {
- if (mNextVibration == session) {
+ if (mNextSession == session) {
// This is still the next vibration to be played.
- mNextVibration = null;
+ mNextSession = null;
} else {
// A new request took the place of this one, maybe with higher importance.
// Next vibration already cleared with the right status, just return here.
return session.getScale();
}
if (!session.linkToDeath()) {
- endVibrationLocked(session, Status.IGNORED_ERROR_TOKEN);
+ endSessionLocked(session, Status.IGNORED_ERROR_TOKEN);
return session.getScale();
}
if (DEBUG) {
@@ -2062,7 +2333,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// should be ignored or scaled.
mVibrationSettings.update();
}
- mCurrentVibration = session;
+ mCurrentSession = session;
session.scale(mVibrationScaler, attrs.getUsage());
// Vibrator will start receiving data from external channels after this point.
@@ -2080,12 +2351,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationStop");
try {
synchronized (mLock) {
- if ((mCurrentVibration instanceof ExternalVibrationSession evs)
+ if ((mCurrentSession instanceof ExternalVibrationSession evs)
&& evs.isHoldingSameVibration(vib)) {
if (DEBUG) {
Slog.d(TAG, "Stopping external vibration: " + vib);
}
- mCurrentVibration.requestEnd(Status.FINISHED);
+ mCurrentSession.requestEnd(Status.FINISHED);
}
}
} finally {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 1ff01a6c70bf..9cfe3bab6e0b 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -67,6 +67,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
+import com.android.server.utils.LazyJniRegistrar;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
import com.android.server.utils.ManagedApplicationService.LogEvent;
@@ -131,6 +132,10 @@ public class VrManagerService extends SystemService
private static native void initializeNative();
private static native void setVrModeNative(boolean enabled);
+ static {
+ LazyJniRegistrar.registerVrManagerService();
+ }
+
private final Object mLock = new Object();
private final IBinder mOverlayToken = new Binder();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index d5bea4adaf8c..b3e68a35764b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -19,6 +19,7 @@ package com.android.server.wallpaper;
import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
import static android.app.WallpaperManager.getOrientation;
import static android.app.WallpaperManager.getRotatedOrientation;
+import static android.app.Flags.accurateWallpaperDownsampling;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE;
@@ -378,7 +379,14 @@ public class WallpaperCropper {
for (int i = 0; i < wallpaper.mCropHints.size(); i++) {
Rect adjustedRect = new Rect(wallpaper.mCropHints.valueAt(i));
adjustedRect.offset(-wallpaper.cropHint.left, -wallpaper.cropHint.top);
- adjustedRect.scale(1f / wallpaper.mSampleSize);
+ if (accurateWallpaperDownsampling()) {
+ adjustedRect.left = (int) (0.5f + adjustedRect.left / wallpaper.mSampleSize);
+ adjustedRect.top = (int) (0.5f + adjustedRect.top / wallpaper.mSampleSize);
+ adjustedRect.right = (int) Math.floor(adjustedRect.right / wallpaper.mSampleSize);
+ adjustedRect.bottom = (int) Math.floor(adjustedRect.bottom / wallpaper.mSampleSize);
+ } else {
+ adjustedRect.scale(1f / wallpaper.mSampleSize);
+ }
result.put(wallpaper.mCropHints.keyAt(i), adjustedRect);
}
return result;
@@ -603,6 +611,11 @@ public class WallpaperCropper {
float sampleSizeForThisOrientation = Math.max(1f, Math.min(
crop.width() / displayForThisOrientation.x,
crop.height() / displayForThisOrientation.y));
+ if (accurateWallpaperDownsampling()) {
+ sampleSizeForThisOrientation = Math.max(1f, Math.min(
+ (float) crop.width() / displayForThisOrientation.x,
+ (float) crop.height() / displayForThisOrientation.y));
+ }
sampleSize = Math.min(sampleSize, sampleSizeForThisOrientation);
}
// If the total crop has more width or height than either the max texture size
@@ -746,8 +759,8 @@ public class WallpaperCropper {
final ImageDecoder.Source srcData =
ImageDecoder.createSource(wallpaper.getWallpaperFile());
final int finalScale = scale;
- final int rescaledBitmapWidth = (int) (0.5f + bitmapSize.x / sampleSize);
- final int rescaledBitmapHeight = (int) (0.5f + bitmapSize.y / sampleSize);
+ final int rescaledBitmapWidth = (int) Math.ceil(bitmapSize.x / sampleSize);
+ final int rescaledBitmapHeight = (int) Math.ceil(bitmapSize.y / sampleSize);
Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
if (!multiCrop()) decoder.setTargetSampleSize(finalScale);
if (multiCrop()) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 5cff37a36656..d019516cd069 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -858,10 +858,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo,
wallpaper.getDescription());
} else {
+ WallpaperDescription desc = new WallpaperDescription.Builder().setComponent(
+ (connection.mInfo != null) ? connection.mInfo.getComponent()
+ : null).build();
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
wpdData.mWidth, wpdData.mHeight,
- wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo,
- /* description= */ null);
+ wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo, desc);
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
@@ -2378,8 +2380,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
SparseArray<Rect> relativeSuggestedCrops =
mWallpaperCropper.getRelativeCropHints(wallpaper);
Point croppedBitmapSize = new Point(
- (int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize),
- (int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize));
+ (int) Math.ceil(wallpaper.cropHint.width() / wallpaper.mSampleSize),
+ (int) Math.ceil(wallpaper.cropHint.height() / wallpaper.mSampleSize));
if (croppedBitmapSize.equals(0, 0)) {
// There is an ImageWallpaper, but there are no crop hints and the bitmap size is
// unknown (e.g. the default wallpaper). Return a special "null" value that will be
@@ -2408,6 +2410,27 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
@Override
+ public Bundle getCurrentBitmapCrops(int which, int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getBitmapCrop", null);
+ synchronized (mLock) {
+ checkPermission(READ_WALLPAPER_INTERNAL);
+ WallpaperData wallpaper = (which == FLAG_LOCK) ? mLockWallpaperMap.get(userId)
+ : mWallpaperMap.get(userId);
+ if (wallpaper == null || !mImageWallpaper.equals(wallpaper.getComponent())) {
+ return null;
+ }
+ Bundle bundle = new Bundle();
+ for (int i = 0; i < wallpaper.mCropHints.size(); i++) {
+ String key = String.valueOf(wallpaper.mCropHints.keyAt(i));
+ Rect rect = wallpaper.mCropHints.valueAt(i);
+ bundle.putParcelable(key, rect);
+ }
+ return bundle;
+ }
+ }
+
+ @Override
public List<Rect> getFutureBitmapCrops(Point bitmapSize, List<Point> displaySizes,
int[] screenOrientations, List<Rect> crops) {
SparseArray<Rect> cropMap = getCropMap(screenOrientations, crops);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 69643002750b..1c11c6701643 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -254,6 +254,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -707,9 +708,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private boolean mOccludesParent;
- /** Whether the activity have style floating */
- private boolean mStyleFloating;
-
/**
* Unlike {@link #mOccludesParent} which can be changed at runtime. This is a static attribute
* from the style of activity. Because we don't want {@link WindowContainer#getOrientation()}
@@ -791,10 +789,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false.
private boolean mIsEligibleForFixedOrientationLetterbox;
- // activity is not displayed?
- // TODO: rename to mNoDisplay
- @VisibleForTesting
- boolean noDisplay;
+ /**
+ * Whether the activity is to be displayed. See {@link android.R.attr#windowNoDisplay}.
+ */
+ private boolean mNoDisplay;
final boolean mShowForAllUsers;
// TODO: Make this final
int mTargetSdk;
@@ -1178,7 +1176,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" inHistory="); pw.print(inHistory);
pw.print(" idle="); pw.println(idle);
pw.print(prefix); pw.print("occludesParent="); pw.print(occludesParent());
- pw.print(" noDisplay="); pw.print(noDisplay);
+ pw.print(" mNoDisplay="); pw.print(mNoDisplay);
pw.print(" immersive="); pw.print(immersive);
pw.print(" launchMode="); pw.println(launchMode);
pw.print(prefix); pw.print("mActivityType=");
@@ -2011,20 +2009,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (ent != null) {
final boolean styleTranslucent = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- mStyleFloating = ent.array.getBoolean(
+ final boolean styleFloating = ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsFloating, false);
- mOccludesParent = !(styleTranslucent || mStyleFloating)
+ mOccludesParent = !(styleTranslucent || styleFloating)
// This style is propagated to the main window attributes with
// FLAG_SHOW_WALLPAPER from PhoneWindow#generateLayout.
|| ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
mStyleFillsParent = mOccludesParent;
- noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ mNoDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
mOptOutEdgeToEdge = ent.array.getBoolean(
R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false);
} else {
- mStyleFloating = false;
mStyleFillsParent = mOccludesParent = true;
- noDisplay = false;
+ mNoDisplay = false;
mOptOutEdgeToEdge = false;
}
@@ -3091,8 +3088,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return occludesParent(true /* includingFinishing */);
}
- boolean isStyleFloating() {
- return mStyleFloating;
+ boolean isNoDisplay() {
+ return mNoDisplay;
+ }
+
+ /**
+ * Exposed only for testing and should not be used to modify value of {@link #mNoDisplay}.
+ */
+ @VisibleForTesting
+ void setIsNoDisplay(boolean isNoDisplay) {
+ mNoDisplay = isNoDisplay;
}
/** Returns true if this activity is not finishing, is opaque and fills the entire space of
@@ -3192,6 +3197,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mWmService.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(packageName)) {
return false;
}
+ if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) {
+ return false;
+ }
// If the user preference respects aspect ratio, then it becomes non-resizable.
return !mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides()
.shouldApplyUserMinAspectRatioOverride();
@@ -5542,8 +5550,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!visible) {
if (mTransitionController.inPlayingTransition(this)) {
mTransitionChangeFlags |= FLAG_IS_OCCLUDED;
- } else if (mTransitionController.inFinishingTransition(this)) {
- mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
+ if (mTransitionController.mFinishingTransition != null
+ && mTransitionController.mFinishingTransition.isTransientLaunch(this)) {
+ mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
+ }
}
} else {
mTransitionChangeFlags &= ~FLAG_IS_OCCLUDED;
@@ -6069,7 +6079,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void notifyUnknownVisibilityLaunchedForKeyguardTransition() {
// No display activities never add a window, so there is no point in waiting them for
// relayout.
- if (noDisplay || !isKeyguardLocked()) {
+ if (mNoDisplay || !isKeyguardLocked()) {
return;
}
@@ -8886,6 +8896,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAppCompatController.getAppCompatSizeCompatModePolicy();
if (scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
+ && mAppCompatDisplayInsets != null
&& !mAppCompatDisplayInsets.mIsInFixedOrientationOrAspectRatioLetterbox) {
// App prefers to keep its original size.
// If the size compat is from previous fixed orientation letterboxing, we may want to
@@ -10301,6 +10312,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (pictureInPictureArgs != null && pictureInPictureArgs.hasSourceBoundsHint()) {
pictureInPictureArgs.getSourceRectHint().offset(windowBounds.left, windowBounds.top);
}
+
+ if (android.app.Flags.enableTvImplicitEnterPipRestriction()) {
+ PackageManager pm = mAtmService.mContext.getPackageManager();
+ if (pictureInPictureArgs.isAutoEnterEnabled()
+ && pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && pm.checkPermission(Manifest.permission.TV_IMPLICIT_ENTER_PIP, packageName)
+ == PackageManager.PERMISSION_DENIED) {
+ Log.i(TAG,
+ "Auto-enter PiP only allowed on TV if android.permission"
+ + ".TV_IMPLICIT_ENTER_PIP permission is held by the app.");
+ PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
+ builder.setAutoEnterEnabled(false);
+ pictureInPictureArgs.copyOnlySet(builder.build());
+ }
+ }
}
private void applyLocaleOverrideIfNeeded(Configuration resolvedConfig) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 05a96d9fcf5e..2e2ca147dcdd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1037,7 +1037,6 @@ class ActivityStarter {
mLastStartReason = request.reason;
mLastStartActivityTimeMs = System.currentTimeMillis();
- final ActivityRecord previousStart = mLastStartActivityRecord;
final IApplicationThread caller = request.caller;
Intent intent = request.intent;
NeededUriGrants intentGrants = request.intentGrants;
@@ -2350,7 +2349,8 @@ class ActivityStarter {
// When there is a reused activity and the current result is a trampoline activity,
// set the reused activity as the result.
if (mLastStartActivityRecord != null
- && (mLastStartActivityRecord.finishing || mLastStartActivityRecord.noDisplay)) {
+ && (mLastStartActivityRecord.finishing
+ || mLastStartActivityRecord.isNoDisplay())) {
mLastStartActivityRecord = targetTaskTop;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7963d096fc88..198e14a16500 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1854,6 +1854,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
assertPackageMatchesCallingUid(callingPackage);
+ mAmInternal.addCreatorToken(intent, callingPackage);
+
final ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setLaunchTaskId(taskId);
// Pass in the system UID to allow setting launch taskId with MANAGE_GAME_ACTIVITY.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 30b53d1dbab4..4857b02eaf7c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1725,9 +1725,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return;
}
Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
- if (transit == null) {
- transit = task.mTransitionController.getCollectingTransition();
- }
if (transit != null) {
transit.collectClose(task);
if (!task.mTransitionController.useFullReadyTracking()) {
@@ -1739,7 +1736,15 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// before anything that may need it to wait (setReady(false)).
transit.setReady(task, true);
}
+ } else {
+ // If we failed to create a transition, there might be already a currently collecting
+ // transition. Let's use it if possible.
+ transit = task.mTransitionController.getCollectingTransition();
+ if (transit != null) {
+ transit.collectClose(task);
+ }
}
+
// Consume the stopping activities immediately so activity manager won't skip killing
// the process because it is still foreground state, i.e. RESUMED -> PAUSING set from
// removeActivities -> finishIfPossible.
@@ -2478,7 +2483,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
/** Notifies that the top activity of the task is forced to be resizeable. */
private void handleForcedResizableTaskIfNeeded(Task task, int reason) {
final ActivityRecord topActivity = task.getTopNonFinishingActivity();
- if (topActivity == null || topActivity.noDisplay
+ if (topActivity == null || topActivity.isNoDisplay()
|| !topActivity.canForceResizeNonResizable(task.getWindowingMode())) {
return;
}
@@ -2894,10 +2899,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
private boolean mIncludeInvisibleAndFinishing;
private boolean mIgnoringKeyguard;
- ActivityRecord getOpaqueActivity(
- @NonNull WindowContainer<?> container, boolean ignoringKeyguard) {
+ ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) {
mIncludeInvisibleAndFinishing = true;
- mIgnoringKeyguard = ignoringKeyguard;
+ mIgnoringKeyguard = true;
return container.getActivity(this,
true /* traverseTopToBottom */, null /* boundary */);
}
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 548c0a34bf99..e8eae4f96a04 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -70,15 +70,8 @@ class AppCompatAspectRatioPolicy {
mAppCompatAspectRatioState.reset();
}
- float getDesiredAspectRatio(@NonNull Configuration newParentConfig,
+ private float getDesiredAspectRatio(@NonNull Configuration newParentConfig,
@NonNull Rect parentBounds) {
- // If in camera compat mode, aspect ratio from the camera compat policy has priority over
- // default letterbox aspect ratio.
- if (AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
- mActivityRecord)) {
- return AppCompatCameraPolicy.getCameraCompatAspectRatio(mActivityRecord);
- }
-
final float letterboxAspectRatioOverride =
mAppCompatOverrides.getAppCompatAspectRatioOverrides()
.getFixedOrientationLetterboxAspectRatio(newParentConfig);
@@ -120,7 +113,16 @@ class AppCompatAspectRatioPolicy {
if (mTransparentPolicy.isRunning()) {
return mTransparentPolicy.getInheritedMinAspectRatio();
}
+
final ActivityInfo info = mActivityRecord.info;
+
+ // If in camera compat mode, aspect ratio from the camera compat policy has priority over
+ // the default aspect ratio.
+ if (AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(mActivityRecord)) {
+ return Math.max(AppCompatCameraPolicy.getCameraCompatMinAspectRatio(mActivityRecord),
+ info.getMinAspectRatio());
+ }
+
final AppCompatAspectRatioOverrides aspectRatioOverrides =
mAppCompatOverrides.getAppCompatAspectRatioOverrides();
if (aspectRatioOverrides.shouldApplyUserMinAspectRatioOverride()) {
@@ -128,10 +130,11 @@ class AppCompatAspectRatioPolicy {
}
if (!aspectRatioOverrides.shouldOverrideMinAspectRatio()
&& !AppCompatCameraPolicy.shouldOverrideMinAspectRatioForCamera(mActivityRecord)) {
- if (mActivityRecord.isUniversalResizeable()) {
+ final float minAspectRatio = info.getMinAspectRatio();
+ if (minAspectRatio == 0 || mActivityRecord.isUniversalResizeable()) {
return 0;
}
- return info.getMinAspectRatio();
+ return minAspectRatio;
}
if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
@@ -173,10 +176,11 @@ class AppCompatAspectRatioPolicy {
if (mTransparentPolicy.isRunning()) {
return mTransparentPolicy.getInheritedMaxAspectRatio();
}
- if (mActivityRecord.isUniversalResizeable()) {
+ final float maxAspectRatio = mActivityRecord.info.getMaxAspectRatio();
+ if (maxAspectRatio == 0 || mActivityRecord.isUniversalResizeable()) {
return 0;
}
- return mActivityRecord.info.getMaxAspectRatio();
+ return maxAspectRatio;
}
@Nullable
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 8c5689c1ef57..8be66ccfbd70 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -234,7 +234,7 @@ class AppCompatCameraPolicy {
}
// TODO(b/369070416): have policies implement the same interface.
- static float getCameraCompatAspectRatio(@NonNull ActivityRecord activity) {
+ static float getCameraCompatMinAspectRatio(@NonNull ActivityRecord activity) {
final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity);
if (cameraPolicy == null) {
return 1.0f;
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 6c344c6d850a..145a3767c149 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -15,12 +15,15 @@
*/
package com.android.server.wm;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY;
+
import android.annotation.NonNull;
import android.content.pm.PackageManager;
import com.android.server.wm.utils.OptPropFactory;
import java.io.PrintWriter;
+import java.util.function.BooleanSupplier;
/**
* Allows the interaction with all the app compat policies and configurations
@@ -47,6 +50,8 @@ class AppCompatController {
private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
@NonNull
private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy;
+ @NonNull
+ final BooleanSupplier mAllowRestrictedResizability;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
@@ -70,6 +75,17 @@ class AppCompatController {
mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(mActivityRecord,
mAppCompatOverrides);
+ mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
+ try {
+ return packageManager.getPropertyAsUser(
+ PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
+ mActivityRecord.mActivityComponent.getPackageName(),
+ mActivityRecord.mActivityComponent.getClassName(),
+ mActivityRecord.mUserId).getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ });
}
@NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index db76eb9ac5d9..ebb50db54693 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -164,41 +164,46 @@ final class AppCompatUtils {
appCompatTaskInfo.setIsFromLetterboxDoubleTap(reachabilityOverrides.isFromDoubleTap());
- final Rect bounds = top.getBounds();
- final Rect appBounds = getAppBounds(top);
- appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
- appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
- appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
- appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
+ final boolean isTopActivityLetterboxed = top.areBoundsLetterboxed();
+ appCompatTaskInfo.setTopActivityLetterboxed(isTopActivityLetterboxed);
+ if (isTopActivityLetterboxed) {
+ final Rect bounds = top.getBounds();
+ final Rect appBounds = getAppBounds(top);
+ appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
+ appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
+ appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
+ appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
- // We need to consider if letterboxed or pillarboxed.
- // TODO(b/336807329) Encapsulate reachability logic
- appCompatTaskInfo.setLetterboxDoubleTapEnabled(reachabilityOverrides
- .isLetterboxDoubleTapEducationEnabled());
- if (appCompatTaskInfo.isLetterboxDoubleTapEnabled()) {
- if (appCompatTaskInfo.isTopActivityPillarboxed()) {
- if (reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()) {
- // Pillarboxed.
- appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
- reachabilityOverrides.getLetterboxPositionForHorizontalReachability();
- } else {
- appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
- }
- } else {
- if (reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()) {
- // Letterboxed.
- appCompatTaskInfo.topActivityLetterboxVerticalPosition =
- reachabilityOverrides.getLetterboxPositionForVerticalReachability();
+ // We need to consider if letterboxed or pillarboxed.
+ // TODO(b/336807329) Encapsulate reachability logic
+ appCompatTaskInfo.setLetterboxDoubleTapEnabled(reachabilityOverrides
+ .isLetterboxDoubleTapEducationEnabled());
+ if (appCompatTaskInfo.isLetterboxDoubleTapEnabled()) {
+ if (appCompatTaskInfo.isTopActivityPillarboxShaped()) {
+ if (reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()) {
+ // Pillarboxed.
+ appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
+ reachabilityOverrides
+ .getLetterboxPositionForHorizontalReachability();
+ } else {
+ appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
+ }
} else {
- appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
+ if (reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()) {
+ // Letterboxed.
+ appCompatTaskInfo.topActivityLetterboxVerticalPosition =
+ reachabilityOverrides.getLetterboxPositionForVerticalReachability();
+ } else {
+ appCompatTaskInfo.setLetterboxDoubleTapEnabled(false);
+ }
}
}
}
+
final boolean eligibleForAspectRatioButton =
!info.isTopActivityTransparent && !appCompatTaskInfo.isTopActivityInSizeCompat()
&& aspectRatioOverrides.shouldEnableUserAspectRatioSettings();
appCompatTaskInfo.setEligibleForUserAspectRatioButton(eligibleForAspectRatioButton);
- appCompatTaskInfo.setTopActivityLetterboxed(top.areBoundsLetterboxed());
appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode =
AppCompatCameraPolicy.getCameraCompatFreeformMode(top);
appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 2fe023e55d31..4ed8b09cd652 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -26,6 +26,8 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_FINISH_AND_REMOVE_TASK;
+import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_BACK_PREVIEW;
import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS;
@@ -60,6 +62,7 @@ import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
import android.window.IWindowlessStartingSurfaceCallback;
import android.window.OnBackInvokedCallbackInfo;
+import android.window.SystemOverrideOnBackInvokedCallback;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
@@ -226,10 +229,19 @@ class BackNavigationController {
&& callbackInfo.isAnimationCallback());
mNavigationMonitor.startMonitor(window, navigationObserver);
+ int requestOverride = callbackInfo.getOverrideBehavior();
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
+ "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s",
currentTask, currentActivity, callbackInfo, window);
-
+ if (requestOverride == OVERRIDE_FINISH_AND_REMOVE_TASK) {
+ final ActivityRecord rootR = currentTask != null ? currentTask.getRootActivity()
+ : null;
+ if (currentActivity != null && rootR != currentActivity) {
+ // The top activity is not root activity, the activity cannot remove task when
+ // finishAndRemoveTask called.
+ requestOverride = OVERRIDE_UNDEFINED;
+ }
+ }
// Clear the pointer down outside focus if any.
mWindowManagerService.clearPointerDownOutsideFocusRunnable();
@@ -274,7 +286,8 @@ class BackNavigationController {
} else if (hasTranslucentActivity(currentActivity, prevActivities)) {
// skip if one of participant activity is translucent
backType = BackNavigationInfo.TYPE_CALLBACK;
- } else if (prevActivities.size() > 0) {
+ } else if (prevActivities.size() > 0
+ && requestOverride == SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED) {
if ((!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities))
&& isAllActivitiesCreated(prevActivities)) {
// We have another Activity in the same currentTask to go to
@@ -1025,6 +1038,12 @@ class BackNavigationController {
return;
}
+ if (mWindowManagerService.mRoot.mTransitionController.isCollecting()) {
+ Slog.v(TAG, "Skip predictive back transition, another transition is collecting");
+ cancelPendingAnimation();
+ return;
+ }
+
// Ensure the final animation targets which hidden by transition could be visible.
for (int i = 0; i < targets.size(); i++) {
final WindowContainer wc = targets.get(i).mContainer;
@@ -1053,6 +1072,7 @@ class BackNavigationController {
Slog.e(TAG, "Remote animation gone", e);
}
mPendingAnimationBuilder = null;
+ mNavigationMonitor.stopMonitorTransition();
}
/**
@@ -1550,6 +1570,9 @@ class BackNavigationController {
}
void createStartingSurface(@Nullable TaskSnapshot snapshot) {
+ if (Flags.deferPredictiveAnimationIfNoSnapshot() && snapshot == null) {
+ return;
+ }
if (mAdaptors[0].mSwitchType == DIALOG_CLOSE) {
return;
}
@@ -1851,7 +1874,7 @@ class BackNavigationController {
tc.requestStartTransition(prepareOpen,
null /*startTask */, null /* remoteTransition */,
null /* displayChange */);
- prepareOpen.setReady(makeVisibles.get(0), true);
+ prepareOpen.setReady(mCloseTarget, true);
return prepareOpen;
} else if (mSnapshot == null) {
return setLaunchBehind(visibleOpenActivities);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index ec171c5e5766..bce8c2be271e 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -46,10 +46,8 @@ import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
import static com.android.window.flags.Flags.balAdditionalStartModes;
import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
-import static com.android.window.flags.Flags.balImproveRealCallerVisibilityCheck;
import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
-import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
import static com.android.window.flags.Flags.balShowToastsBlocked;
import static com.android.window.flags.Flags.balStrictModeRo;
@@ -299,7 +297,8 @@ public class BackgroundActivityStartController {
private final int mCallingUid;
private final int mCallingPid;
private final @ActivityTaskManagerService.AppSwitchState int mAppSwitchState;
- private final boolean mCallingUidHasAnyVisibleWindow;
+ private final boolean mCallingUidHasVisibleActivity;
+ private final boolean mCallingUidHasNonAppVisibleWindow;
private final @ActivityManager.ProcessState int mCallingUidProcState;
private final boolean mIsCallingUidPersistentSystemProcess;
final BackgroundStartPrivileges mBalAllowedByPiSender;
@@ -308,7 +307,8 @@ public class BackgroundActivityStartController {
private final String mRealCallingPackage;
private final int mRealCallingUid;
private final int mRealCallingPid;
- private final boolean mRealCallingUidHasAnyVisibleWindow;
+ private final boolean mRealCallingUidHasVisibleActivity;
+ private final boolean mRealCallingUidHasNonAppVisibleWindow;
private final @ActivityManager.ProcessState int mRealCallingUidProcState;
private final boolean mIsRealCallingUidPersistentSystemProcess;
private final PendingIntentRecord mOriginatingPendingIntent;
@@ -348,11 +348,7 @@ public class BackgroundActivityStartController {
@BackgroundActivityStartMode int realCallerBackgroundActivityStartMode =
checkedOptions.getPendingIntentBackgroundActivityStartMode();
- if (!balImproveRealCallerVisibilityCheck()) {
- // without this fix the auto-opt ins below would violate CTS tests
- mAutoOptInReason = null;
- mAutoOptInCaller = false;
- } else if (originatingPendingIntent == null) {
+ if (originatingPendingIntent == null) {
mAutoOptInReason = AUTO_OPT_IN_NOT_PENDING_INTENT;
mAutoOptInCaller = true;
} else if (mIsCallForResult) {
@@ -407,16 +403,21 @@ public class BackgroundActivityStartController {
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
mCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
- mCallingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
+ mCallingUidHasVisibleActivity =
+ mService.mVisibleActivityProcessTracker.hasVisibleActivity(callingUid);
+ mCallingUidHasNonAppVisibleWindow = mService.mActiveUids.hasNonAppVisibleWindow(
+ callingUid);
if (realCallingUid == NO_PROCESS_UID) {
// no process provided
mRealCallingUidProcState = PROCESS_STATE_NONEXISTENT;
- mRealCallingUidHasAnyVisibleWindow = false;
+ mRealCallingUidHasVisibleActivity = false;
+ mRealCallingUidHasNonAppVisibleWindow = false;
mRealCallerApp = null;
mIsRealCallingUidPersistentSystemProcess = false;
} else if (callingUid == realCallingUid) {
mRealCallingUidProcState = mCallingUidProcState;
- mRealCallingUidHasAnyVisibleWindow = mCallingUidHasAnyVisibleWindow;
+ mRealCallingUidHasVisibleActivity = mCallingUidHasVisibleActivity;
+ mRealCallingUidHasNonAppVisibleWindow = mCallingUidHasNonAppVisibleWindow;
// In the PendingIntent case callerApp is not passed in, so resolve it ourselves.
mRealCallerApp = callerApp == null
? mService.getProcessController(realCallingPid, realCallingUid)
@@ -424,8 +425,10 @@ public class BackgroundActivityStartController {
mIsRealCallingUidPersistentSystemProcess = mIsCallingUidPersistentSystemProcess;
} else {
mRealCallingUidProcState = mService.mActiveUids.getUidState(realCallingUid);
- mRealCallingUidHasAnyVisibleWindow =
- mService.hasActiveVisibleWindow(realCallingUid);
+ mRealCallingUidHasVisibleActivity =
+ mService.mVisibleActivityProcessTracker.hasVisibleActivity(realCallingUid);
+ mRealCallingUidHasNonAppVisibleWindow =
+ mService.mActiveUids.hasNonAppVisibleWindow(realCallingUid);
mRealCallerApp = mService.getProcessController(realCallingPid, realCallingUid);
mIsRealCallingUidPersistentSystemProcess =
mRealCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
@@ -552,7 +555,9 @@ public class BackgroundActivityStartController {
sb.append("; callingUid: ").append(mCallingUid);
sb.append("; callingPid: ").append(mCallingPid);
sb.append("; appSwitchState: ").append(mAppSwitchState);
- sb.append("; callingUidHasAnyVisibleWindow: ").append(mCallingUidHasAnyVisibleWindow);
+ sb.append("; callingUidHasVisibleActivity: ").append(mCallingUidHasVisibleActivity);
+ sb.append("; callingUidHasNonAppVisibleWindow: ").append(
+ mCallingUidHasNonAppVisibleWindow);
sb.append("; callingUidProcState: ").append(DebugUtils.valueToString(
ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState));
sb.append("; isCallingUidPersistentSystemProcess: ")
@@ -581,8 +586,10 @@ public class BackgroundActivityStartController {
.append(getTargetSdk(mRealCallingPackage));
sb.append("; realCallingUid: ").append(mRealCallingUid);
sb.append("; realCallingPid: ").append(mRealCallingPid);
- sb.append("; realCallingUidHasAnyVisibleWindow: ")
- .append(mRealCallingUidHasAnyVisibleWindow);
+ sb.append("; realCallingUidHasVisibleActivity: ")
+ .append(mRealCallingUidHasVisibleActivity);
+ sb.append("; realCallingUidHasNonAppVisibleWindow: ")
+ .append(mRealCallingUidHasNonAppVisibleWindow);
sb.append("; realCallingUidProcState: ").append(DebugUtils.valueToString(
ActivityManager.class, "PROCESS_STATE_", mRealCallingUidProcState));
sb.append("; isRealCallingUidPersistentSystemProcess: ")
@@ -599,12 +606,8 @@ public class BackgroundActivityStartController {
mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
}
// features
- sb.append("; balImproveRealCallerVisibilityCheck: ")
- .append(balImproveRealCallerVisibilityCheck());
sb.append("; balRequireOptInByPendingIntentCreator: ")
.append(balRequireOptInByPendingIntentCreator());
- sb.append("; balRespectAppSwitchStateWhenCheckBoundByForegroundUid: ")
- .append(balRespectAppSwitchStateWhenCheckBoundByForegroundUid());
sb.append("; balDontBringExistingBackgroundTaskStackToFg: ")
.append(balDontBringExistingBackgroundTaskStackToFg());
sb.append("]");
@@ -1007,11 +1010,11 @@ public class BackgroundActivityStartController {
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
|| state.mAppSwitchState == APP_SWITCH_FG_ONLY;
- if (appSwitchAllowedOrFg && state.mCallingUidHasAnyVisibleWindow) {
+ if (appSwitchAllowedOrFg && state.mCallingUidHasVisibleActivity) {
return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
/*background*/ false, "callingUid has visible window");
}
- if (mService.mActiveUids.hasNonAppVisibleWindow(state.mCallingUid)) {
+ if (state.mCallingUidHasNonAppVisibleWindow) {
return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
/*background*/ false, "callingUid has non-app visible window");
}
@@ -1133,23 +1136,13 @@ public class BackgroundActivityStartController {
final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
|| state.mAppSwitchState == APP_SWITCH_FG_ONLY
|| isHomeApp(state.mRealCallingUid, state.mRealCallingPackage);
- if (balImproveRealCallerVisibilityCheck()) {
- if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false, "realCallingUid has visible window");
- }
- if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
- return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
- /*background*/ false, "realCallingUid has non-app visible window");
- }
- } else {
- // don't abort if the realCallingUid has a visible window
- // TODO(b/171459802): We should check appSwitchAllowed also
- if (state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false,
- "realCallingUid has visible (non-toast) window.");
- }
+ if (appSwitchAllowedOrFg && state.mRealCallingUidHasVisibleActivity) {
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ /*background*/ false, "realCallingUid has visible window");
+ }
+ if (state.mRealCallingUidHasNonAppVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
+ /*background*/ false, "realCallingUid has non-app visible window");
}
// Don't abort if the realCallerApp or other processes of that uid are considered to be in
@@ -1892,10 +1885,12 @@ public class BackgroundActivityStartController {
state.mCallingUid,
state.mCallingPackage,
state.mCallingUidProcState,
- state.mCallingUidHasAnyVisibleWindow,
+ state.mCallingUidHasVisibleActivity
+ || state.mCallingUidHasNonAppVisibleWindow,
state.mRealCallingUid,
state.mRealCallingUidProcState,
- state.mRealCallingUidHasAnyVisibleWindow,
+ state.mRealCallingUidHasVisibleActivity
+ || state.mRealCallingUidHasNonAppVisibleWindow,
(state.mOriginatingPendingIntent != null));
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 264c8beb44bf..ccf1aedb3177 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -50,7 +50,6 @@ import android.util.IntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
-import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -137,10 +136,8 @@ class BackgroundLaunchProcessController {
}
// Allow if the caller is bound by a UID that's currently foreground.
// But still respect the appSwitchState.
- if (checkConfiguration.checkVisibility && (
- Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid()
- ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
- : isBoundByForegroundUid())) {
+ if (checkConfiguration.checkVisibility && appSwitchState != APP_SWITCH_DISALLOW
+ && isBoundByForegroundUid()) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND
: BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
"process bound by foreground uid");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e827f44cb2a2..e9e550e72a00 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -159,7 +159,6 @@ import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELD
import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -3426,14 +3425,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (!mWmService.mSupportsHighPerfTransitions) {
return;
}
- if (!explicitRefreshRateHints()) {
- if (enable) {
- getPendingTransaction().setEarlyWakeupStart();
- } else {
- getPendingTransaction().setEarlyWakeupEnd();
- }
- return;
- }
if (enable) {
if (mTransitionPrefSession == null) {
mTransitionPrefSession = mWmService.mSystemPerformanceHinter.createSession(
@@ -3446,10 +3437,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void enableHighFrameRate(boolean enable) {
- if (!explicitRefreshRateHints()) {
- // Done by RefreshRatePolicy.
- return;
- }
if (enable) {
if (mHighFrameRateSession == null) {
mHighFrameRateSession = mWmService.mSystemPerformanceHinter.createSession(
@@ -7072,7 +7059,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
public void setImeInputTargetRequestedVisibility(boolean visible) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
- // TODO(b/329229469) we won't have the statsToken in all cases, but should still log
+ // TODO(b/353463205) we won't have the statsToken in all cases, but should still log
try {
mRemoteInsetsController.setImeInputTargetRequestedVisibility(visible);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c7d57fe89972..76e8a70768c1 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1735,9 +1735,9 @@ public class DisplayPolicy {
}
// Show IME over the keyguard if the target allows it.
- final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisible()
- && win.mIsImWindow && (imeTarget.canShowWhenLocked()
- || !imeTarget.canBeHiddenByKeyguard());
+ final boolean showImeOverKeyguard =
+ imeTarget != null && win.mIsImWindow && imeTarget.isDisplayed() && (
+ imeTarget.canShowWhenLocked() || !imeTarget.canBeHiddenByKeyguard());
if (showImeOverKeyguard) {
return false;
}
@@ -3053,7 +3053,7 @@ public class DisplayPolicy {
@InsetsType int insetsType) {
for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
final InsetsSource source = insetsState.sourceAt(i);
- if ((source.getType() & insetsType) == 0 || !source.isVisible()) {
+ if ((source.getType() & insetsType) == 0) {
continue;
}
if (Rect.intersects(bounds, source.getFrame())) {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index e9c6e93891df..49f717e228d2 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -100,21 +100,22 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
// isLeashReadyForDispatching (used to dispatch the leash of the control) is
// depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
// again, so that the control with leash can be eventually dispatched
- if (!mGivenInsetsReady && mServerVisible && !givenInsetsPending) {
+ if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending) {
mGivenInsetsReady = true;
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
mStateController.notifyControlChanged(mControlTarget, this);
setImeShowing(true);
- } else if (wasServerVisible && mServerVisible && mGivenInsetsReady
+ } else if (wasServerVisible && isServerVisible() && mGivenInsetsReady
&& givenInsetsPending) {
// If the server visibility didn't change (still visible), and mGivenInsetsReady
// is set, we won't call into notifyControlChanged. Therefore, we can reset the
// statsToken, if available.
+ ProtoLog.d(WM_DEBUG_IME, "onPostLayout cancel statsToken, ws=%s", ws);
ImeTracker.forLogging().onCancelled(mStatsToken,
ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
mStatsToken = null;
- } else if (wasServerVisible && !mServerVisible) {
+ } else if (wasServerVisible && !isServerVisible()) {
setImeShowing(false);
}
}
@@ -134,11 +135,15 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
@Override
protected boolean isLeashReadyForDispatching() {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ // We should only dispatch the leash, if the following conditions are fulfilled:
+ // 1. parent isLeashReadyForDispatching, 2. mGivenInsetsReady (means there are no
+ // givenInsetsPending), 3. the IME surface is drawn, 4. either the IME is
+ // serverVisible (the unfrozen state)
final WindowState ws =
mWindowContainer != null ? mWindowContainer.asWindowState() : null;
final boolean isDrawn = ws != null && ws.isDrawn();
return super.isLeashReadyForDispatching()
- && mServerVisible && isDrawn && mGivenInsetsReady;
+ && isServerVisible() && isDrawn && mGivenInsetsReady;
} else {
return super.isLeashReadyForDispatching();
}
@@ -170,9 +175,13 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
if (control != null && control.getLeash() != null) {
ImeTracker.Token statsToken = getAndClearStatsToken();
- ImeTracker.forLogging().onProgress(statsToken,
- ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH);
- control.setImeStatsToken(statsToken);
+ if (statsToken == null) {
+ ProtoLog.d(WM_DEBUG_IME, "IME getControl without statsToken");
+ } else {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH);
+ control.setImeStatsToken(statsToken);
+ }
}
}
return control;
@@ -254,7 +263,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
// Refer WindowState#getImeControlTarget().
target = target.getWindow().getImeControlTarget();
}
- // TODO(b/329229469) make sure that the statsToken of all callers is non-null (currently
+ // TODO(b/353463205) make sure that the statsToken of all callers is non-null (currently
// not the case)
super.updateControlForTarget(target, force, statsToken);
if (Flags.refactorInsetsController()) {
@@ -290,12 +299,14 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
changed |= mDisplayContent.onImeInsetsClientVisibilityUpdate();
if (Flags.refactorInsetsController()) {
if (changed) {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY);
invokeOnImeRequestedChangedListener(mDisplayContent.getImeInputTarget(),
statsToken);
} else {
- // TODO(b/329229469) change phase and check cancelled / failed
+ // TODO(b/353463205) check cancelled / failed
ImeTracker.forLogging().onCancelled(statsToken,
- ImeTracker.PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES);
+ ImeTracker.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY);
}
}
return changed;
@@ -460,7 +471,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
// This can later become ready, so we don't want to cancel the pending request here.
return;
}
- // TODO(b/329229469) check if this is still triggered, as we don't go into STATE_SHOW_IME
+ // TODO(b/353463205) check if this is still triggered, as we don't go into STATE_SHOW_IME
// (DefaultImeVisibilityApplier)
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// The IME is drawn, so call into {@link WindowState#notifyInsetsControlChanged}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1d4d6eb82c44..7276007481ab 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -730,6 +730,10 @@ class InsetsSourceProvider {
return mFakeControlTarget;
}
+ boolean isServerVisible() {
+ return mServerVisible;
+ }
+
boolean isClientVisible() {
return mClientVisible;
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 5dddf36a8d8b..4b2d45430bb4 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -317,9 +317,9 @@ class InsetsStateController {
// aborted.
provider.updateFakeControlTarget(target);
} else {
- // TODO(b/329229469) if the IME controlTarget changes, any pending requests should fail
+ // TODO(b/353463205) if the IME controlTarget changes, any pending requests should fail
provider.updateControlForTarget(target, false /* force */,
- null /* TODO(b/329229469) check if needed here */);
+ null /* TODO(b/353463205) check if needed here */);
// Get control target again in case the provider didn't accept the one we passed to it.
target = provider.getControlTarget();
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2664d8c9cfe4..6091b8334438 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -771,20 +771,21 @@ class KeyguardController {
mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
}
- boolean hasChange = false;
- if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
+ final boolean startedGoingAway = (!lastKeyguardGoingAway && mKeyguardGoingAway);
+ final boolean occludedChanged = (lastOccluded != mOccluded);
+
+ if (startedGoingAway) {
writeEventLog("dismissIfInsecure");
controller.handleDismissInsecureKeyguard(display);
controller.scheduleGoingAwayTimeout(mDisplayId);
- hasChange = true;
- } else if (lastOccluded != mOccluded) {
+ }
+ if (occludedChanged && (reduceKeyguardTransitions() || !startedGoingAway)) {
controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
- hasChange = true;
}
// Collect the participants for shell transition, so that transition won't happen too
// early since the transition was set ready.
- if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) {
+ if (top != null && (startedGoingAway || (occludedChanged && mOccluded))) {
display.mTransitionController.collect(top);
}
}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index e983edf0d5ae..2401f9072ea3 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -35,4 +35,5 @@ per-file *WindowTracing* = file:platform/development:/tools/winscope/OWNERS
# Files related to activity security
per-file ActivityStarter.java = file:/ACTIVITY_SECURITY_OWNERS
+per-file ActivityStartController.java = file:/ACTIVITY_SECURITY_OWNERS
per-file ActivityTaskManagerService.java = file:/ACTIVITY_SECURITY_OWNERS
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 8cab7d979d07..e4c34ed52359 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -19,8 +19,6 @@ package com.android.server.wm;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
-
import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.Display.Mode;
@@ -60,7 +58,6 @@ class RefreshRatePolicy {
}
private final DisplayInfo mDisplayInfo;
- private final Mode mDefaultMode;
private final Mode mLowRefreshRateMode;
private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate();
private final HighRefreshRateDenylist mHighRefreshRateDenylist;
@@ -92,8 +89,7 @@ class RefreshRatePolicy {
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
HighRefreshRateDenylist denylist) {
mDisplayInfo = displayInfo;
- mDefaultMode = displayInfo.getDefaultMode();
- mLowRefreshRateMode = findLowRefreshRateMode(displayInfo, mDefaultMode);
+ mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
mHighRefreshRateDenylist = denylist;
mWmService = wmService;
}
@@ -102,7 +98,8 @@ class RefreshRatePolicy {
* Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
* default mode.
*/
- private Mode findLowRefreshRateMode(DisplayInfo displayInfo, Mode defaultMode) {
+ private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
+ final Mode defaultMode = displayInfo.getDefaultMode();
float[] refreshRates = displayInfo.getDefaultRefreshRates();
float bestRefreshRate = defaultMode.getRefreshRate();
mMinSupportedRefreshRate = bestRefreshRate;
@@ -135,33 +132,6 @@ class RefreshRatePolicy {
// Unspecified, use default mode.
return 0;
}
-
- // If app is animating, it's not able to control refresh rate because we want the animation
- // to run in default refresh rate. But if the display size of default mode is different
- // from the using preferred mode, then still keep the preferred mode to avoid disturbing
- // the animation.
- if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) {
- Display.Mode preferredMode = null;
- for (Display.Mode mode : mDisplayInfo.supportedModes) {
- if (preferredDisplayModeId == mode.getModeId()) {
- preferredMode = mode;
- break;
- }
- }
- if (preferredMode != null) {
- final int pW = preferredMode.getPhysicalWidth();
- final int pH = preferredMode.getPhysicalHeight();
- if ((pW != mDefaultMode.getPhysicalWidth()
- || pH != mDefaultMode.getPhysicalHeight())
- && pW == mDisplayInfo.getNaturalWidth()
- && pH == mDisplayInfo.getNaturalHeight()) {
- // Prefer not to change display size when animating.
- return preferredDisplayModeId;
- }
- }
- return 0;
- }
-
return preferredDisplayModeId;
}
@@ -264,12 +234,6 @@ class RefreshRatePolicy {
return w.mFrameRateVote.reset();
}
- // If app is animating, it's not able to control refresh rate because we want the animation
- // to run in default refresh rate.
- if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) {
- return w.mFrameRateVote.reset();
- }
-
// If the app set a preferredDisplayModeId, the preferred refresh rate is the refresh rate
// of that mode id.
if (refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 077127c031e1..1bb4c41e79e0 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -715,7 +715,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (embeddedWindow != null) {
// If there is no WindowState for the IWindow, it could be still an
// EmbeddedWindow. Therefore, check the EmbeddedWindowController as well
- // TODO(b/329229469) Use different phase here
+ // TODO(b/353463205) Use different phase here
ImeTracker.forLogging().onProgress(imeStatsToken,
ImeTracker.PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES);
embeddedWindow.setRequestedVisibleTypes(
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 52994c70f183..3ee2e6048634 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -152,7 +152,10 @@ class SnapshotController {
if (mOpenActivities.isEmpty()) {
return false;
}
- if (Flags.alwaysCaptureActivitySnapshot()) {
+ // TODO (b/362183912) always capture activity snapshot will cause performance
+ // regression, remove flag after ramp up
+ if (!Flags.deferPredictiveAnimationIfNoSnapshot()
+ && Flags.alwaysCaptureActivitySnapshot()) {
return true;
}
for (int i = mOpenActivities.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 3f6e91590cce..9a48d5b8880d 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
-import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -90,8 +89,6 @@ public class SurfaceAnimator {
@Nullable
private Runnable mAnimationCancelledCallback;
- private boolean mAnimationStartDelayed;
-
private boolean mAnimationFinished;
/**
@@ -188,10 +185,6 @@ public class SurfaceAnimator {
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
- if (mAnimationStartDelayed) {
- ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
- return;
- }
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.DEBUG)) {
StringWriter sw = new StringWriter();
@@ -215,36 +208,7 @@ public class SurfaceAnimator {
null /* animationCancelledCallback */, null /* snapshotAnim */, null /* freezer */);
}
- /**
- * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation}
- * will not start the animation until {@link #endDelayingAnimationStart} is called. When an
- * animation start is being delayed, the animator is considered animating already.
- */
- void startDelayingAnimationStart() {
-
- // We only allow delaying animation start we are not currently animating
- if (!isAnimating()) {
- mAnimationStartDelayed = true;
- }
- }
-
- /**
- * See {@link #startDelayingAnimationStart}.
- */
- void endDelayingAnimationStart() {
- final boolean delayed = mAnimationStartDelayed;
- mAnimationStartDelayed = false;
- if (delayed && mAnimation != null) {
- mAnimation.startAnimation(mLeash, mAnimatable.getSyncTransaction(),
- mAnimationType, mInnerAnimationFinishedCallback);
- mAnimatable.commitPendingTransaction();
- }
- }
-
- /**
- * @return Whether we are currently running an animation, or we have a pending animation that
- * is waiting to be started with {@link #endDelayingAnimationStart}
- */
+ /** Returns whether it is currently running an animation. */
boolean isAnimating() {
return mAnimation != null;
}
@@ -290,15 +254,6 @@ public class SurfaceAnimator {
}
/**
- * Reparents the surface.
- *
- * @see #setLayer
- */
- void reparent(Transaction t, SurfaceControl newParent) {
- t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent);
- }
-
- /**
* @return True if the surface is attached to the leash; false otherwise.
*/
boolean hasLeash() {
@@ -319,7 +274,6 @@ public class SurfaceAnimator {
Slog.w(TAG, "Unable to transfer animation, because " + from + " animation is finished");
return;
}
- endDelayingAnimationStart();
final Transaction t = mAnimatable.getSyncTransaction();
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mLeash = from.mLeash;
@@ -336,10 +290,6 @@ public class SurfaceAnimator {
mService.mAnimationTransferMap.put(mAnimation, this);
}
- boolean isAnimationStartDelayed() {
- return mAnimationStartDelayed;
- }
-
/**
* Cancels the animation, and resets the leash.
*
@@ -361,7 +311,7 @@ public class SurfaceAnimator {
final SurfaceFreezer.Snapshot snapshot = mSnapshot;
reset(t, false);
if (animation != null) {
- if (!mAnimationStartDelayed && forwardCancel) {
+ if (forwardCancel) {
animation.onAnimationCancelled(leash);
if (animationCancelledCallback != null) {
animationCancelledCallback.run();
@@ -386,10 +336,6 @@ public class SurfaceAnimator {
mService.scheduleAnimationLocked();
}
}
-
- if (!restarting) {
- mAnimationStartDelayed = false;
- }
}
private void reset(Transaction t, boolean destroyLeash) {
@@ -495,14 +441,12 @@ public class SurfaceAnimator {
if (mLeash != null) {
mLeash.dumpDebug(proto, LEASH);
}
- proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed);
proto.end(token);
}
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
- pw.print(" mAnimationType=" + animationTypeToString(mAnimationType));
- pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : "");
+ pw.print(" mAnimationType="); pw.println(animationTypeToString(mAnimationType));
pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation);
if (mAnimation != null) {
mAnimation.dump(pw, prefix + " ");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e13577c6809f..352dc528f815 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3432,9 +3432,9 @@ class Task extends TaskFragment {
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
info.isVisibleRequested = isVisibleRequested();
+ info.isTopActivityNoDisplay = top != null && top.isNoDisplay();
info.isSleeping = shouldSleepActivities();
info.isTopActivityTransparent = top != null && !top.fillsParent();
- info.isTopActivityStyleFloating = top != null && top.isStyleFloating();
info.lastNonFullscreenBounds = topTask.mLastNonFullscreenBounds;
final WindowState windowState = top != null ? top.findMainWindow() : null;
info.requestedVisibleTypes = (windowState != null && Flags.enableFullyImmersiveInDesktop())
@@ -3918,7 +3918,9 @@ class Task extends TaskFragment {
sb.append(" aI=");
sb.append(affinityIntent.getComponent().flattenToShortString());
}
- sb.append(" isResizeable=").append(isResizeable());
+ if (!isResizeable()) {
+ sb.append(" nonResizable");
+ }
if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) {
sb.append(" minWidth=").append(mMinWidth);
sb.append(" minHeight=").append(mMinHeight);
@@ -4722,7 +4724,7 @@ class Task extends TaskFragment {
}
}
if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
- && topActivity != null && !topActivity.noDisplay
+ && topActivity != null && !topActivity.isNoDisplay()
&& topActivity.canForceResizeNonResizable(likelyResolvedMode)) {
// Inform the user that they are starting an app that may not work correctly in
// multi-window mode.
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 606d51d8ec50..e090b1980c6d 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1090,8 +1090,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return true;
}
// Including finishing Activity if the TaskFragment is becoming invisible in the transition.
- return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this,
- true /* ignoringKeyguard */) == null;
+ return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this) == null;
}
/**
@@ -1734,6 +1733,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (!hasDirectChildActivities()) {
return false;
}
+ if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) {
+ // Even if the transient activity is occluded, defer pausing (addToStopping will still
+ // be called) it until the transient transition is done. So the current resuming
+ // activity won't need to wait for additional pause complete.
+ return false;
+ }
ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
mResumedActivity);
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 5c9a84db002a..c39671d76929 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -449,7 +449,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// If the source activity is a no-display activity, pass on the launch display area token
// from source activity as currently preferred.
- if (taskDisplayArea == null && source != null && source.noDisplay) {
+ if (taskDisplayArea == null && source != null && source.isNoDisplay()) {
taskDisplayArea = source.mHandoverTaskDisplayArea;
if (taskDisplayArea != null) {
if (DEBUG) appendLog("display-area-from-no-display-source=" + taskDisplayArea);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 454e43120ede..20481f25fa5c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -465,6 +465,31 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
+ /**
+ * This ensures that all changes for previously transient-hide containers are flagged such that
+ * they will report changes and be included in this transition.
+ */
+ void updateChangesForRestoreTransientHideTasks(Transition transientLaunchTransition) {
+ if (transientLaunchTransition.mTransientHideTasks == null) {
+ // Skip if the transient-launch transition has no transient-hide tasks
+ ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Skipping update changes for restore transient hide tasks");
+ return;
+ }
+
+ // For each change, if it was previously transient-hidden, then we should force a flag to
+ // ensure that it is included in the next transition
+ for (int i = 0; i < mChanges.size(); i++) {
+ final WindowContainer container = mChanges.keyAt(i);
+ if (transientLaunchTransition.isInTransientHide(container)) {
+ ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Force update transient hide task for restore %d: %s", mSyncId, container);
+ final ChangeInfo info = mChanges.valueAt(i);
+ info.mRestoringTransientHide = true;
+ }
+ }
+ }
+
/** Returns {@code true} if the task should keep visible if this is a transient transition. */
boolean isTransientVisible(@NonNull Task task) {
if (mTransientLaunches == null) return false;
@@ -477,20 +502,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (transientRoot == null) continue;
final WindowContainer<?> rootParent = transientRoot.getParent();
if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
- final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor.mOpaqueActivityHelper
- .getOpaqueActivity(rootParent, true /* ignoringKeyguard */);
- if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
- occludedCount++;
+ for (int j = rootParent.getChildCount() - 1; j >= 0; --j) {
+ final WindowContainer<?> sibling = rootParent.getChildAt(j);
+ if (sibling == transientRoot) break;
+ if (!sibling.getWindowConfiguration().isAlwaysOnTop() && mController.mAtm
+ .mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(sibling) != null) {
+ occludedCount++;
+ break;
+ }
}
}
if (occludedCount == numTransient) {
- for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
- if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
- // Keep transient activity visible until transition finished, so it won't pause
- // with transient-hide tasks that may delay resuming the next top.
- return true;
- }
- }
// Let transient-hide activities pause before transition is finished.
return false;
}
@@ -3481,6 +3503,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// State tracking
boolean mExistenceChanged = false;
+ // This state indicates that we are restoring transient order as a part of an
+ // end-transition. Because the visibility for transient hide containers has not actually
+ // changed, we need to ensure that hasChanged() still reports the relevant changes
+ boolean mRestoringTransientHide = false;
// before change state
boolean mVisible;
int mWindowingMode;
@@ -3555,7 +3581,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
|| !mContainer.getBounds().equals(mAbsoluteBounds)
|| mRotation != mContainer.getWindowConfiguration().getRotation()
|| mDisplayId != getDisplayId(mContainer)
- || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0;
+ || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0
+ // If we are restoring transient-hide containers, then we should consider them
+ // important for the transition as well (their requested visibilities would not
+ // have changed for the checks below to consider it).
+ || mRestoringTransientHide;
}
@TransitionInfo.TransitionMode
@@ -3568,6 +3598,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
final boolean nowVisible = wc.isVisibleRequested();
if (nowVisible == mVisible) {
+ if (mRestoringTransientHide) {
+ // The requested visibility has not changed for transient-hide containers, but
+ // we are restoring them so we should considering them moving to front again
+ return TRANSIT_TO_FRONT;
+ }
return TRANSIT_CHANGE;
}
if (mExistenceChanged) {
@@ -3589,6 +3624,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (wc.mWmService.mAtmService.mBackNavigationController.isMonitorTransitionTarget(wc)) {
flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
}
+ final TaskDisplayArea tda = wc.asTaskDisplayArea();
+ if (tda != null) {
+ flags |= TransitionInfo.FLAG_IS_TASK_DISPLAY_AREA;
+ }
final Task task = wc.asTask();
if (task != null) {
final ActivityRecord topActivity = task.getTopNonFinishingActivity();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 87bdfa4f5d75..143d1b72fff9 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -37,6 +37,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -524,6 +525,23 @@ class TransitionController {
return false;
}
+ /**
+ * @return A pair of the transition and restore-behind target for the given {@param container}.
+ * @param container An ancestor of a transient-launch activity
+ */
+ @Nullable
+ Pair<Transition, Task> getTransientLaunchTransitionAndTarget(
+ @NonNull WindowContainer container) {
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ final Transition transition = mPlayingTransitions.get(i);
+ final Task restoreBehindTask = transition.getTransientLaunchRestoreTarget(container);
+ if (restoreBehindTask != null) {
+ return new Pair<>(transition, restoreBehindTask);
+ }
+ }
+ return null;
+ }
+
/** Returns {@code true} if the display contains a transient-launch transition. */
boolean hasTransientLaunch(@NonNull DisplayContent dc) {
if (mCollectingTransition != null && mCollectingTransition.hasTransientLaunch()
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e0c473de0f33..5f92bb626154 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3215,8 +3215,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
&& isChangingAppTransition();
- // Delaying animation start isn't compatible with remote animations at all.
- if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
+ if (controller != null) {
// Here we load App XML in order to read com.android.R.styleable#Animation_showBackdrop.
boolean showBackdrop = false;
// Optionally set backdrop color if App explicitly provides it through
@@ -3639,20 +3638,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return getAnimatingContainer(PARENTS, ANIMATION_TYPE_ALL);
}
- /**
- * @see SurfaceAnimator#startDelayingAnimationStart
- */
- void startDelayingAnimationStart() {
- mSurfaceAnimator.startDelayingAnimationStart();
- }
-
- /**
- * @see SurfaceAnimator#endDelayingAnimationStart
- */
- void endDelayingAnimationStart() {
- mSurfaceAnimator.endDelayingAnimationStart();
- }
-
@Override
public int getSurfaceWidth() {
return mSurfaceControl.getWidth();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 166d74b132bd..ead12826c263 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -111,6 +111,7 @@ import android.os.RemoteException;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import android.view.RemoteAnimationAdapter;
import android.view.SurfaceControl;
@@ -813,6 +814,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
if (deferResume) {
mService.mTaskSupervisor.endDeferResume();
+ // Transient launching the Recents via HIERARCHY_OP_TYPE_PENDING_INTENT directly
+ // resume the Recents activity with no TRANSACT_EFFECTS_LIFECYCLE. Explicitly
+ // checks if the top resumed activity should be updated after defer-resume ended.
+ mService.mTaskSupervisor.updateTopResumedActivityIfNeeded("endWCT");
}
mService.continueWindowLayout();
}
@@ -1371,16 +1376,56 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: {
- if (!chain.isFinishing()) break;
+ if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // Only allow restoring transient order when finishing a transition
+ if (!chain.isFinishing()) break;
+ }
+ // Validate the container
final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
- if (container == null) break;
+ if (container == null) {
+ ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Restoring transient order: invalid container");
+ break;
+ }
final Task thisTask = container.asActivityRecord() != null
? container.asActivityRecord().getTask() : container.asTask();
- if (thisTask == null) break;
- final Task restoreAt = chain.mTransition.getTransientLaunchRestoreTarget(container);
- if (restoreAt == null) break;
+ if (thisTask == null) {
+ ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Restoring transient order: invalid task");
+ break;
+ }
+
+ // Find the task to restore behind
+ final Pair<Transition, Task> transientRestore =
+ mTransitionController.getTransientLaunchTransitionAndTarget(container);
+ if (transientRestore == null) {
+ ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Restoring transient order: no restore task");
+ break;
+ }
+ final Transition transientLaunchTransition = transientRestore.first;
+ final Task restoreAt = transientRestore.second;
+ ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Restoring transient order: restoring behind task=%d", restoreAt.mTaskId);
+
+ // Restore the position of the given container behind the target task
final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea();
taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt);
+
+ if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) {
+ // Because we are in a transient launch transition, the requested visibility of
+ // tasks does not actually change for the transient-hide tasks, but we do want
+ // the restoration of these transient-hide tasks to top to be a part of this
+ // finish transition
+ final Transition collectingTransition =
+ mTransitionController.getCollectingTransition();
+ if (collectingTransition != null) {
+ collectingTransition.updateChangesForRestoreTransientHideTasks(
+ transientLaunchTransition);
+ }
+ }
+
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d01e29b2fd5e..079170a70433 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -157,6 +157,7 @@ import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT;
import static com.android.server.wm.WindowStateProto.ANIMATOR;
import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
import static com.android.server.wm.WindowStateProto.DESTROYING;
+import static com.android.server.wm.WindowStateProto.DIM_BOUNDS;
import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
@@ -181,7 +182,6 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static com.android.window.flags.Flags.secureWindowState;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
@@ -4118,6 +4118,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mMergedLocalInsetsSources.valueAt(i).dumpDebug(proto, MERGED_LOCAL_INSETS_SOURCES);
}
}
+ if (getDimController() != null) {
+ final Rect dimBounds = getDimController().getDimBounds();
+ if (dimBounds != null) {
+ dimBounds.dumpDebug(proto, DIM_BOUNDS);
+ }
+ }
proto.end(token);
}
@@ -5297,12 +5303,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (voteChanged) {
getPendingTransaction()
.setFrameRate(mSurfaceControl, mFrameRateVote.mRefreshRate,
- mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS);
- if (explicitRefreshRateHints()) {
- getPendingTransaction().setFrameRateSelectionStrategy(mSurfaceControl,
- mFrameRateVote.mSelectionStrategy);
- }
-
+ mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS)
+ .setFrameRateSelectionStrategy(mSurfaceControl,
+ mFrameRateVote.mSelectionStrategy);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 5bde8b5a507c..44e237aa27de 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -614,6 +614,12 @@ class WindowToken extends WindowContainer<WindowState> {
final int rotation = getRelativeDisplayRotation();
if (rotation == Surface.ROTATION_0) return mFixedRotationTransformLeash;
if (mFixedRotationTransformLeash != null) return mFixedRotationTransformLeash;
+ if (ActivityTaskManagerService.isPip2ExperimentEnabled() && asActivityRecord() != null
+ && mTransitionController.getWindowingModeAtStart(
+ asActivityRecord()) == WINDOWING_MODE_PINNED) {
+ // PiP handles fixed rotation animation in Shell, so do not create the rotation leash.
+ return null;
+ }
final SurfaceControl leash = makeSurface().setContainerLayer()
.setParent(getParentSurfaceControl())
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index ea0b02c58974..eaa3a37d5bf3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -55,7 +55,6 @@ cc_library_static {
"com_android_server_powerstats_PowerStatsService.cpp",
"com_android_server_power_stats_CpuPowerStatsCollector.cpp",
"com_android_server_hint_HintManagerService.cpp",
- "com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
"com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp",
"com_android_server_stats_pull_StatsPullAtomService.cpp",
@@ -76,6 +75,7 @@ cc_library_static {
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"com_android_server_sensor_SensorService.cpp",
+ "com_android_server_utils_LazyJniRegistrar.cpp",
"com_android_server_wm_TaskFpsCallbackController.cpp",
"onload.cpp",
":lib_cachedAppOptimizer_native",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index b622751fc3e8..8052b092a1e1 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -30,6 +30,7 @@ per-file com_android_server_vibrator_* = file:/services/core/java/com/android/se
per-file com_android_server_am_CachedAppOptimizer.cpp = file:/PERFORMANCE_OWNERS
per-file com_android_server_am_Freezer.cpp = file:/PERFORMANCE_OWNERS
per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
+per-file com_android_server_utils_LazyJniRegistrar.cpp = file:/PERFORMANCE_OWNERS
# Memory
per-file com_android_server_am_OomConnection.cpp = file:/MEMORY_OWNERS
diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp
deleted file mode 100644
index 6600c981b68d..000000000000
--- a/services/core/jni/com_android_server_SerialService.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "SerialServiceJNI"
-#include "utils/Log.h"
-
-#include "jni.h"
-#include <nativehelper/JNIPlatformHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-namespace android
-{
-
-static struct parcel_file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
-} gParcelFileDescriptorOffsets;
-
-static jobject android_server_SerialService_open(JNIEnv *env, jobject /* thiz */, jstring path)
-{
- const char *pathStr = env->GetStringUTFChars(path, NULL);
-
- int fd = open(pathStr, O_RDWR | O_NOCTTY);
- if (fd < 0) {
- ALOGE("could not open %s", pathStr);
- env->ReleaseStringUTFChars(path, pathStr);
- return NULL;
- }
- env->ReleaseStringUTFChars(path, pathStr);
-
- jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
- if (fileDescriptor == NULL) {
- close(fd);
- return NULL;
- }
- return env->NewObject(gParcelFileDescriptorOffsets.mClass,
- gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
-}
-
-
-static const JNINativeMethod method_table[] = {
- { "native_open", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
- (void*)android_server_SerialService_open },
-};
-
-int register_android_server_SerialService(JNIEnv *env)
-{
- jclass clazz = env->FindClass("com/android/server/SerialService");
- if (clazz == NULL) {
- ALOGE("Can't find com/android/server/SerialService");
- return -1;
- }
-
- clazz = env->FindClass("android/os/ParcelFileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
- gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
- LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
- "Unable to find constructor for android.os.ParcelFileDescriptor");
-
- return jniRegisterNativeMethods(env, "com/android/server/SerialService",
- method_table, NELEM(method_table));
-}
-
-};
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 416e60f06c06..e38337540ad9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -110,6 +110,7 @@ static struct {
jmethodID notifyInputDevicesChanged;
jmethodID notifyTouchpadHardwareState;
jmethodID notifyTouchpadGestureInfo;
+ jmethodID notifyTouchpadThreeFingerTap;
jmethodID notifySwitch;
jmethodID notifyInputChannelBroken;
jmethodID notifyNoFocusedWindowAnr;
@@ -209,6 +210,7 @@ static struct {
jfieldID lightTypePlayerId;
jfieldID lightTypeKeyboardBacklight;
jfieldID lightTypeKeyboardMicMute;
+ jfieldID lightTypeKeyboardVolumeMute;
jfieldID lightCapabilityBrightness;
jfieldID lightCapabilityColorRgb;
} gLightClassInfo;
@@ -345,6 +347,7 @@ public:
void setTouchpadTapDraggingEnabled(bool enabled);
void setShouldNotifyTouchpadHardwareState(bool enabled);
void setTouchpadRightClickZoneEnabled(bool enabled);
+ void setTouchpadThreeFingerTapShortcutEnabled(bool enabled);
void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
void setShowTouches(bool enabled);
void setNonInteractiveDisplays(const std::set<ui::LogicalDisplayId>& displayIds);
@@ -370,6 +373,7 @@ public:
void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
int32_t deviceId) override;
void notifyTouchpadGestureInfo(enum GestureType type, int32_t deviceId) override;
+ void notifyTouchpadThreeFingerTap() override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) override;
@@ -510,6 +514,10 @@ private:
// into context (a.k.a. "right") clicks.
bool touchpadRightClickZoneEnabled{false};
+ // True to use three-finger tap as a customizable shortcut; false to use it as a
+ // middle-click.
+ bool touchpadThreeFingerTapShortcutEnabled{false};
+
// True if a pointer icon should be shown for stylus pointers.
bool stylusPointerIconEnabled{false};
@@ -780,6 +788,8 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
outConfig->touchpadTapDraggingEnabled = mLocked.touchpadTapDraggingEnabled;
outConfig->shouldNotifyTouchpadHardwareState = mLocked.shouldNotifyTouchpadHardwareState;
outConfig->touchpadRightClickZoneEnabled = mLocked.touchpadRightClickZoneEnabled;
+ outConfig->touchpadThreeFingerTapShortcutEnabled =
+ mLocked.touchpadThreeFingerTapShortcutEnabled;
outConfig->disabledDevices = mLocked.disabledInputDevices;
@@ -1034,6 +1044,13 @@ void NativeInputManager::notifyTouchpadGestureInfo(enum GestureType type, int32_
checkAndClearExceptionFromCallback(env, "notifyTouchpadGestureInfo");
}
+void NativeInputManager::notifyTouchpadThreeFingerTap() {
+ ATRACE_CALL();
+ JNIEnv* env = jniEnv();
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyTouchpadThreeFingerTap);
+ checkAndClearExceptionFromCallback(env, "notifyTouchpadThreeFingerTap");
+}
+
std::shared_ptr<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) {
@@ -1495,6 +1512,22 @@ void NativeInputManager::setTouchpadRightClickZoneEnabled(bool enabled) {
InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
}
+void NativeInputManager::setTouchpadThreeFingerTapShortcutEnabled(bool enabled) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ if (mLocked.touchpadThreeFingerTapShortcutEnabled == enabled) {
+ return;
+ }
+
+ ALOGI("Setting touchpad three finger tap shortcut to %s.", toString(enabled));
+ mLocked.touchpadThreeFingerTapShortcutEnabled = enabled;
+ } // release lock
+
+ mInputManager->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+}
+
void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled) {
bool refresh = false;
@@ -2437,6 +2470,11 @@ static void nativeSetTouchpadRightClickZoneEnabled(JNIEnv* env, jobject nativeIm
im->setTouchpadRightClickZoneEnabled(enabled);
}
+static void nativeSetTouchpadThreeFingerTapShortcutEnabled(JNIEnv* env, jobject nativeImplObj,
+ jboolean enabled) {
+ getNativeInputManager(env, nativeImplObj)->setTouchpadThreeFingerTapShortcutEnabled(enabled);
+}
+
static void nativeSetShowTouches(JNIEnv* env, jobject nativeImplObj, jboolean enabled) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2593,6 +2631,9 @@ static jobject nativeGetLights(JNIEnv* env, jobject nativeImplObj, jint deviceId
} else if (lightInfo.type == InputDeviceLightType::KEYBOARD_MIC_MUTE) {
jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
gLightClassInfo.lightTypeKeyboardMicMute);
+ } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_VOLUME_MUTE) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightTypeKeyboardVolumeMute);
} else {
ALOGW("Unknown light type %s", ftl::enum_string(lightInfo.type).c_str());
continue;
@@ -3119,6 +3160,8 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"setShouldNotifyTouchpadHardwareState", "(Z)V",
(void*)nativeSetShouldNotifyTouchpadHardwareState},
{"setTouchpadRightClickZoneEnabled", "(Z)V", (void*)nativeSetTouchpadRightClickZoneEnabled},
+ {"setTouchpadThreeFingerTapShortcutEnabled", "(Z)V",
+ (void*)nativeSetTouchpadThreeFingerTapShortcutEnabled},
{"setShowTouches", "(Z)V", (void*)nativeSetShowTouches},
{"setNonInteractiveDisplays", "([I)V", (void*)nativeSetNonInteractiveDisplays},
{"reloadCalibration", "()V", (void*)nativeReloadCalibration},
@@ -3229,6 +3272,8 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.notifyTouchpadGestureInfo, clazz, "notifyTouchpadGestureInfo",
"(II)V")
+ GET_METHOD_ID(gServiceClassInfo.notifyTouchpadThreeFingerTap, clazz,
+ "notifyTouchpadThreeFingerTap", "()V")
GET_METHOD_ID(gServiceClassInfo.notifySwitch, clazz,
"notifySwitch", "(JII)V");
@@ -3379,6 +3424,8 @@ int register_android_server_InputManager(JNIEnv* env) {
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_BACKLIGHT", "I");
gLightClassInfo.lightTypeKeyboardMicMute =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_MIC_MUTE", "I");
+ gLightClassInfo.lightTypeKeyboardVolumeMute =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_VOLUME_MUTE", "I");
gLightClassInfo.lightCapabilityBrightness =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I");
gLightClassInfo.lightCapabilityColorRgb =
diff --git a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
new file mode 100644
index 000000000000..ad7781e3b8b5
--- /dev/null
+++ b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIHelp.h>
+
+#include "jni.h"
+
+namespace android {
+
+// Forward declared per-class registration methods.
+int register_android_server_ConsumerIrService(JNIEnv* env);
+int register_android_server_vr_VrManagerService(JNIEnv* env);
+
+namespace {
+
+// TODO(b/)375264322: Remove these trampoline methods after finalizing the
+// registrar implementation. Instead, just update the called methods to take a
+// class arg, and hand those methods to jniRegisterNativeMethods directly.
+void registerConsumerIrService(JNIEnv* env, jclass) {
+ register_android_server_ConsumerIrService(env);
+}
+
+void registerVrManagerService(JNIEnv* env, jclass) {
+ register_android_server_vr_VrManagerService(env);
+}
+
+static const JNINativeMethod sJniRegistrarMethods[] = {
+ {"registerConsumerIrService", "()V", (void*)registerConsumerIrService},
+ {"registerVrManagerService", "()V", (void*)registerVrManagerService},
+};
+
+} // namespace
+
+int register_android_server_utils_LazyJniRegistrar(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/utils/LazyJniRegistrar",
+ sJniRegistrarMethods, NELEM(sJniRegistrarMethods));
+}
+
+} // namespace android
diff --git a/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp b/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp
index a47ab9d27c17..46be79e7c097 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp
@@ -16,27 +16,32 @@
#define LOG_TAG "VibratorManagerService"
-#include <nativehelper/JNIHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-#include "core_jni_helpers.h"
-#include "jni.h"
+#include "com_android_server_vibrator_VibratorManagerService.h"
+#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/misc.h>
-
#include <vibratorservice/VibratorManagerHalController.h>
-#include "com_android_server_vibrator_VibratorManagerService.h"
+#include <unordered_map>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
namespace android {
static JavaVM* sJvm = nullptr;
-static jmethodID sMethodIdOnComplete;
+static jmethodID sMethodIdOnSyncedVibrationComplete;
+static jmethodID sMethodIdOnVibrationSessionComplete;
static std::mutex gManagerMutex;
static vibrator::ManagerHalController* gManager GUARDED_BY(gManagerMutex) = nullptr;
class NativeVibratorManagerService {
public:
+ using IVibrationSession = aidl::android::hardware::vibrator::IVibrationSession;
+ using VibrationSessionConfig = aidl::android::hardware::vibrator::VibrationSessionConfig;
+
NativeVibratorManagerService(JNIEnv* env, jobject callbackListener)
: mHal(std::make_unique<vibrator::ManagerHalController>()),
mCallbackListener(env->NewGlobalRef(callbackListener)) {
@@ -52,15 +57,69 @@ public:
vibrator::ManagerHalController* hal() const { return mHal.get(); }
- std::function<void()> createCallback(jlong vibrationId) {
+ std::function<void()> createSyncedVibrationCallback(jlong vibrationId) {
return [vibrationId, this]() {
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
- jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId);
+ jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnSyncedVibrationComplete,
+ vibrationId);
};
}
+ std::function<void()> createVibrationSessionCallback(jlong sessionId) {
+ return [sessionId, this]() {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnVibrationSessionComplete,
+ sessionId);
+ std::lock_guard<std::mutex> lock(mSessionMutex);
+ auto it = mSessions.find(sessionId);
+ if (it != mSessions.end()) {
+ mSessions.erase(it);
+ }
+ };
+ }
+
+ bool startSession(jlong sessionId, const std::vector<int32_t>& vibratorIds) {
+ VibrationSessionConfig config;
+ auto callback = createVibrationSessionCallback(sessionId);
+ auto result = hal()->startSession(vibratorIds, config, callback);
+ if (!result.isOk()) {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> lock(mSessionMutex);
+ mSessions[sessionId] = std::move(result.value());
+ return true;
+ }
+
+ void closeSession(jlong sessionId) {
+ std::lock_guard<std::mutex> lock(mSessionMutex);
+ auto it = mSessions.find(sessionId);
+ if (it != mSessions.end()) {
+ it->second->close();
+ // Keep session, it can still be aborted.
+ }
+ }
+
+ void abortSession(jlong sessionId) {
+ std::lock_guard<std::mutex> lock(mSessionMutex);
+ auto it = mSessions.find(sessionId);
+ if (it != mSessions.end()) {
+ it->second->abort();
+ mSessions.erase(it);
+ }
+ }
+
+ void clearSessions() {
+ hal()->clearSessions();
+ std::lock_guard<std::mutex> lock(mSessionMutex);
+ mSessions.clear();
+ }
+
private:
+ std::mutex mSessionMutex;
const std::unique_ptr<vibrator::ManagerHalController> mHal;
+ std::unordered_map<jlong, std::shared_ptr<IVibrationSession>> mSessions
+ GUARDED_BY(mSessionMutex);
const jobject mCallbackListener;
};
@@ -142,7 +201,7 @@ static jboolean nativeTriggerSynced(JNIEnv* env, jclass /* clazz */, jlong servi
ALOGE("nativeTriggerSynced failed because native service was not initialized");
return JNI_FALSE;
}
- auto callback = service->createCallback(vibrationId);
+ auto callback = service->createSyncedVibrationCallback(vibrationId);
return service->hal()->triggerSynced(callback).isOk() ? JNI_TRUE : JNI_FALSE;
}
@@ -156,8 +215,47 @@ static void nativeCancelSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr
service->hal()->cancelSynced();
}
+static jboolean nativeStartSession(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
+ jlong sessionId, jintArray vibratorIds) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeStartSession failed because native service was not initialized");
+ return JNI_FALSE;
+ }
+ jsize size = env->GetArrayLength(vibratorIds);
+ std::vector<int32_t> ids(size);
+ env->GetIntArrayRegion(vibratorIds, 0, size, reinterpret_cast<jint*>(ids.data()));
+ return service->startSession(sessionId, ids) ? JNI_TRUE : JNI_FALSE;
+}
+
+static void nativeEndSession(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong sessionId,
+ jboolean shouldAbort) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeEndSession failed because native service was not initialized");
+ return;
+ }
+ if (shouldAbort) {
+ service->abortSession(sessionId);
+ } else {
+ service->closeSession(sessionId);
+ }
+}
+
+static void nativeClearSessions(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeClearSessions failed because native service was not initialized");
+ return;
+ }
+ service->clearSessions();
+}
+
inline static constexpr auto sNativeInitMethodSignature =
- "(Lcom/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener;)J";
+ "(Lcom/android/server/vibrator/VibratorManagerService$VibratorManagerNativeCallbacks;)J";
static const JNINativeMethod method_table[] = {
{"nativeInit", sNativeInitMethodSignature, (void*)nativeInit},
@@ -167,15 +265,20 @@ static const JNINativeMethod method_table[] = {
{"nativePrepareSynced", "(J[I)Z", (void*)nativePrepareSynced},
{"nativeTriggerSynced", "(JJ)Z", (void*)nativeTriggerSynced},
{"nativeCancelSynced", "(J)V", (void*)nativeCancelSynced},
+ {"nativeStartSession", "(JJ[I)Z", (void*)nativeStartSession},
+ {"nativeEndSession", "(JJZ)V", (void*)nativeEndSession},
+ {"nativeClearSessions", "(J)V", (void*)nativeClearSessions},
};
int register_android_server_vibrator_VibratorManagerService(JavaVM* jvm, JNIEnv* env) {
sJvm = jvm;
auto listenerClassName =
- "com/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener";
+ "com/android/server/vibrator/VibratorManagerService$VibratorManagerNativeCallbacks";
jclass listenerClass = FindClassOrDie(env, listenerClassName);
- sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V");
-
+ sMethodIdOnSyncedVibrationComplete =
+ GetMethodIDOrDie(env, listenerClass, "onSyncedVibrationComplete", "(J)V");
+ sMethodIdOnVibrationSessionComplete =
+ GetMethodIDOrDie(env, listenerClass, "onVibrationSessionComplete", "(J)V");
return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorManagerService",
method_table, NELEM(method_table));
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 3c55d18245d7..c170ae99da04 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -25,7 +25,6 @@
namespace android {
int register_android_server_BatteryStatsService(JNIEnv* env);
-int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
@@ -33,13 +32,11 @@ int register_android_server_PowerStatsService(JNIEnv* env);
int register_android_server_power_stats_CpuPowerStatsCollector(JNIEnv* env);
int register_android_server_HintManagerService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
-int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
int register_android_server_UsbAlsaMidiDevice(JNIEnv* env);
int register_android_server_UsbDeviceManager(JavaVM* vm, JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
-int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env);
int register_android_server_vibrator_VibratorManagerService(JavaVM* vm, JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
@@ -57,6 +54,7 @@ int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_Freezer(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_android_server_utils_AnrTimer(JNIEnv *env);
+int register_android_server_utils_LazyJniRegistrar(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env);
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
@@ -73,6 +71,9 @@ int register_com_android_server_display_DisplayControl(JNIEnv* env);
int register_com_android_server_SystemClockTime(JNIEnv* env);
int register_android_server_display_smallAreaDetectionController(JNIEnv* env);
int register_com_android_server_accessibility_BrailleDisplayConnection(JNIEnv* env);
+
+// Note: Consider adding new JNI entrypoints for optional services to
+// LazyJniRegistrar instead, and relying on lazy registration.
};
using namespace android;
@@ -94,21 +95,18 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_PowerStatsService(env);
register_android_server_power_stats_CpuPowerStatsCollector(env);
register_android_server_HintManagerService(env);
- register_android_server_SerialService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_UsbDeviceManager(vm, env);
register_android_server_UsbAlsaJackDetector(env);
register_android_server_UsbAlsaMidiDevice(env);
register_android_server_UsbHostManager(env);
- register_android_server_vr_VrManagerService(env);
register_android_server_vibrator_VibratorController(vm, env);
register_android_server_vibrator_VibratorManagerService(vm, env);
register_android_server_SystemServer(env);
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_devicepolicy_CryptoTestHelper(env);
- register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_tv_TvUinputBridge(env);
register_android_server_tv_TvInputHal(env);
@@ -122,6 +120,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_am_Freezer(env);
register_android_server_am_LowMemDetector(env);
register_android_server_utils_AnrTimer(env);
+ register_android_server_utils_LazyJniRegistrar(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env);
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
diff --git a/services/core/services-jarjar-rules.txt b/services/core/services-jarjar-rules.txt
new file mode 100644
index 000000000000..0d296b238c7a
--- /dev/null
+++ b/services/core/services-jarjar-rules.txt
@@ -0,0 +1,2 @@
+# For profiling flags
+rule android.os.profiling.** android.internal.os.profiling.@1
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index a58da81c6396..c19c58e4ba13 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -90,6 +90,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
/**
* Class responsible for setting, resolving, and enforcing policies set by multiple management
@@ -98,10 +99,11 @@ import java.util.Set;
final class DevicePolicyEngine {
static final String TAG = "DevicePolicyEngine";
- // TODO(b/281701062): reference role name from role manager once its exposed.
static final String DEVICE_LOCK_CONTROLLER_ROLE =
"android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+ static final String SYSTEM_SUPERVISION_ROLE = "android.app.role.SYSTEM_SUPERVISION";
+
private static final String CELLULAR_2G_USER_RESTRICTION_ID =
DevicePolicyIdentifiers.getIdentifierForUserRestriction(
UserManager.DISALLOW_CELLULAR_2G);
@@ -1030,11 +1032,11 @@ final class DevicePolicyEngine {
}
}
- private <V> void enforcePolicy(PolicyDefinition<V> policyDefinition,
+ private <V> CompletableFuture<Boolean> enforcePolicy(PolicyDefinition<V> policyDefinition,
@Nullable PolicyValue<V> policyValue, int userId) {
// null policyValue means remove any enforced policies, ensure callbacks handle this
// properly
- policyDefinition.enforcePolicy(
+ return policyDefinition.enforcePolicy(
policyValue == null ? null : policyValue.getValue(), mContext, userId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aca6f7235714..d221e8ccb9b7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -255,7 +255,6 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPR
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -462,7 +461,6 @@ import android.permission.PermissionControllerManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Telephony;
@@ -908,10 +906,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ "management app's authentication policy";
private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s";
- private static final String PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG =
- "enable_permission_based_access";
- private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
-
private static final int RETRY_COPY_ACCOUNT_ATTEMPTS = 3;
/**
@@ -3486,7 +3480,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@GuardedBy("getLockObject()")
private boolean maybeMigrateSuspendedPackagesLocked(String backupId) {
Slog.i(LOG_TAG, "Migrating suspended packages to policy engine");
- if (!Flags.unmanagedModeMigration()) {
+ if (!Flags.suspendPackagesCoexistence()) {
return false;
}
if (mOwners.isSuspendedPackagesMigrated()) {
@@ -3557,6 +3551,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return true;
}
+
+
+ @GuardedBy("getLockObject()")
+ private boolean maybeMigrateMemoryTaggingLocked(String backupId) {
+ if (!Flags.setMtePolicyCoexistence()) {
+ Slog.i(LOG_TAG, "Memory Tagging not migrated because coexistence "
+ + "support is disabled.");
+ return false;
+ }
+ if (mOwners.isMemoryTaggingMigrated()) {
+ // TODO: Remove log after Flags.setMtePolicyCoexistence full rollout.
+ Slog.v(LOG_TAG, "Memory Tagging was previously migrated to policy engine.");
+ return false;
+ }
+
+ Slog.i(LOG_TAG, "Migrating Memory Tagging to policy engine");
+
+ // Create backup if none exists
+ mDevicePolicyEngine.createBackup(backupId);
+ try {
+ iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> {
+ if (admin.mtePolicy != 0) {
+ Slog.i(LOG_TAG, "Setting Memory Tagging policy");
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.MEMORY_TAGGING,
+ enforcingAdmin,
+ new IntegerPolicyValue(admin.mtePolicy),
+ true /* No need to re-set system properties */);
+ }
+ });
+ } catch (Exception e) {
+ Slog.wtf(LOG_TAG,
+ "Failed to migrate Memory Tagging to policy engine", e);
+ }
+
+ Slog.i(LOG_TAG, "Marking Memory Tagging migration complete");
+ mOwners.markMemoryTaggingMigrated();
+ return true;
+ }
+
/** Register callbacks for statsd pulled atoms. */
private void registerStatsCallbacks() {
final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
@@ -4646,22 +4680,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@GuardedBy("getLockObject()")
private List<ActiveAdmin> getActiveAdminsForLockscreenPoliciesLocked(int userHandle) {
if (isSeparateProfileChallengeEnabled(userHandle)) {
-
- if (isPermissionCheckFlagEnabled()) {
- return getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userHandle);
- }
// If this user has a separate challenge, only return its restrictions.
return getUserDataUnchecked(userHandle).mAdminList;
}
// If isSeparateProfileChallengeEnabled is false and userHandle points to a managed profile
// we need to query the parent user who owns the credential.
- if (isPermissionCheckFlagEnabled()) {
- return getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(getProfileParentId(userHandle),
- (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
- } else {
- return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
- (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
- }
+ return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
+ (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
}
@@ -4684,33 +4709,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
(user) -> mLockPatternUtils.isProfileWithUnifiedChallenge(user.id));
}
- /**
- * Get the list of active admins for an affected user:
- * <ul>
- * <li>The active admins associated with the userHandle itself</li>
- * <li>The parent active admins for each managed profile associated with the userHandle</li>
- * <li>The permission based admin associated with the userHandle itself</li>
- * </ul>
- *
- * @param userHandle the affected user for whom to get the active admins
- * @return the list of active admins for the affected user
- */
- @GuardedBy("getLockObject()")
- private List<ActiveAdmin> getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
- int userHandle) {
- List<ActiveAdmin> list;
-
- if (isManagedProfile(userHandle)) {
- list = getUserDataUnchecked(userHandle).mAdminList;
- }
- list = getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(userHandle,
- /* shouldIncludeProfileAdmins */ (user) -> false);
-
- if (getUserData(userHandle).mPermissionBasedAdmin != null) {
- list.add(getUserData(userHandle).mPermissionBasedAdmin);
- }
- return list;
- }
/**
* Returns the list of admins on the given user, as well as parent admins for each managed
@@ -4763,44 +4761,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mDevicePolicyEngine.getResolvedPolicyAcrossUsers(policyDefinition, users);
}
- /**
- * Returns the list of admins on the given user, as well as parent admins for each managed
- * profile associated with the given user. Optionally also include the admin of each managed
- * profile.
- * <p> Should not be called on a profile user.
- */
- @GuardedBy("getLockObject()")
- private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(int userHandle,
- Predicate<UserInfo> shouldIncludeProfileAdmins) {
- ArrayList<ActiveAdmin> admins = new ArrayList<>();
- mInjector.binderWithCleanCallingIdentity(() -> {
- for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
- DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
- if (userInfo.id == userHandle) {
- admins.addAll(policy.mAdminList);
- if (policy.mPermissionBasedAdmin != null) {
- admins.add(policy.mPermissionBasedAdmin);
- }
- } else if (userInfo.isManagedProfile()) {
- for (int i = 0; i < policy.mAdminList.size(); i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.hasParentActiveAdmin()) {
- admins.add(admin.getParentActiveAdmin());
- }
- if (shouldIncludeProfileAdmins.test(userInfo)) {
- admins.add(admin);
- }
- }
- if (policy.mPermissionBasedAdmin != null
- && shouldIncludeProfileAdmins.test(userInfo)) {
- admins.add(policy.mPermissionBasedAdmin);
- }
- }
- }
- });
- return admins;
- }
-
private boolean isSeparateProfileChallengeEnabled(int userHandle) {
return mInjector.binderWithCleanCallingIdentity(() ->
mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle));
@@ -4893,25 +4853,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkArgumentNonnegative(timeout, "Timeout must be >= 0 ms");
int userHandle = mInjector.userHandleGetCallingUserId();
int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
synchronized (getLockObject()) {
ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- ap = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- caller.getPackageName(), affectedUserId)
- .getActiveAdmin();
- } else {
- ap = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD, parent);
- }
+ ap = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD, parent);
// Calling this API automatically bumps the expiration date
final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
ap.passwordExpirationDate = expiration;
@@ -4972,28 +4922,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
String packageName) {
- CallerIdentity caller;
+ CallerIdentity caller = getCallerIdentity(admin);
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
- ActiveAdmin activeAdmin;
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller));
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- caller.getUserId());
- activeAdmin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- synchronized (getLockObject()) {
- activeAdmin = getProfileOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin activeAdmin;
+ synchronized (getLockObject()) {
+ activeAdmin = getProfileOwnerLocked(caller.getUserId());
}
List<String> changedProviders = null;
@@ -5026,28 +4962,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String callerPackageName,
String packageName) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
+ CallerIdentity caller = getCallerIdentity(admin);
- ActiveAdmin activeAdmin;
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller));
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- caller.getUserId());
- activeAdmin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- synchronized (getLockObject()) {
- activeAdmin = getProfileOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin activeAdmin;
+ synchronized (getLockObject()) {
+ activeAdmin = getProfileOwnerLocked(caller.getUserId());
}
List<String> changedProviders = null;
@@ -5080,27 +5002,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin,
String callerPackageName) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
- ActiveAdmin activeAdmin;
+ CallerIdentity caller = getCallerIdentity(admin);
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- caller.getUserId());
- activeAdmin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- synchronized (getLockObject()) {
- activeAdmin = getProfileOwnerLocked(caller.getUserId());
- }
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller));
+
+ ActiveAdmin activeAdmin;
+ synchronized (getLockObject()) {
+ activeAdmin = getProfileOwnerLocked(caller.getUserId());
}
synchronized (getLockObject()) {
@@ -5449,24 +5358,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforceUserUnlocked(userHandle, parent);
synchronized (getLockObject()) {
- if (isPermissionCheckFlagEnabled()) {
- int affectedUser = parent ? getProfileParentId(userHandle) : userHandle;
- enforcePermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- callerPackageName, affectedUser);
- } else {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- }
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
final int userToCheck = getProfileParentUserIfRequested(userHandle, parent);
- boolean activePasswordSufficientForUserLocked = isActivePasswordSufficientForUserLocked(
+ return isActivePasswordSufficientForUserLocked(
policy.mPasswordValidAtLastCheckpoint, metrics, userToCheck);
- return activePasswordSufficientForUserLocked;
}
}
@@ -5622,21 +5524,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller),
"Only profile owner, device owner and system may call this method on parent.");
} else {
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
- || hasCallingOrSelfPermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS)
- || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
- "Must have " + REQUEST_PASSWORD_COMPLEXITY + " or " +
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS
- + " permissions, or be a profile owner or device owner.");
- } else {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
- || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
- "Must have " + REQUEST_PASSWORD_COMPLEXITY
- + " permission, or be a profile owner or device owner.");
- }
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)
+ || isDefaultDeviceOwner(caller) || isProfileOwner(caller),
+ "Must have " + REQUEST_PASSWORD_COMPLEXITY
+ + " permission, or be a profile owner or device owner.");
}
synchronized (getLockObject()) {
@@ -5728,26 +5620,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void setRequiredPasswordComplexityPreCoexistence(
String callerPackageName, int passwordComplexity, boolean calledOnParent) {
CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (!isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
- }
+
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+ Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
synchronized (getLockObject()) {
ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- // TODO: Make sure this returns the parent of the fake admin
- // TODO: Deal with null componentname
- int affectedUser = calledOnParent
- ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- admin = enforcePermissionAndGetEnforcingAdmin(
- null, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- caller.getPackageName(), affectedUser).getActiveAdmin();
- } else {
- admin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), calledOnParent);
- }
+ admin = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), calledOnParent);
if (admin.mPasswordComplexity != passwordComplexity) {
// We require the caller to explicitly clear any password quality requirements set
@@ -5907,14 +5788,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!isSystemUid(caller)) {
// This API can be called by an active device admin or by keyguard code.
if (!hasCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)) {
- if (isPermissionCheckFlagEnabled()) {
- int affectedUser = parent ? getProfileParentId(userHandle) : userHandle;
- enforcePermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- callerPackageName, affectedUser);
- } else {
- getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
- }
+ getActiveAdminForCallerLocked(
+ null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
}
}
@@ -5931,31 +5806,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
-
+ Objects.requireNonNull(who, "ComponentName is null");
int userId = mInjector.userHandleGetCallingUserId();
int affectedUserId = parent ? getProfileParentId(userId) : userId;
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- ap = enforcePermissionAndGetEnforcingAdmin(
- who,
- /*permission=*/ MANAGE_DEVICE_POLICY_WIPE_DATA,
- /* adminPolicy=*/ DeviceAdminInfo.USES_POLICY_WIPE_DATA,
- caller.getPackageName(), affectedUserId).getActiveAdmin();
- } else {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_WIPE_DATA, parent);
- ap = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
- }
+ // This API can only be called by an active device admin,
+ // so try to retrieve it to check that the caller is one.
+ getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_WIPE_DATA, parent);
+ ActiveAdmin ap = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
if (ap.maximumFailedPasswordsForWipe != num) {
ap.maximumFailedPasswordsForWipe = num;
@@ -6210,25 +6072,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(who, "ComponentName is null");
- }
+
+ Objects.requireNonNull(who, "ComponentName is null");
+
int userHandle = mInjector.userHandleGetCallingUserId();
int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- ap = enforcePermissionAndGetEnforcingAdmin(
- who,
- /*permission=*/ MANAGE_DEVICE_POLICY_LOCK,
- /*AdminPolicy=*/DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
- caller.getPackageName(),
- affectedUserId).getActiveAdmin();
- } else {
- ap = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
- }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
if (ap.maximumTimeToUnlock != timeMs) {
ap.maximumTimeToUnlock = timeMs;
@@ -6334,16 +6185,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
+
Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+
// timeoutMs with value 0 means that the admin doesn't participate
// timeoutMs is clamped to the interval in case the internal constants change in the future
final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs();
@@ -6357,17 +6205,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final int userHandle = caller.getUserId();
boolean changed = false;
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- int affectedUser = parent
- ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- ap = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- caller.getPackageName(), affectedUser).getActiveAdmin();
- } else {
- ap = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
- }
+ ActiveAdmin ap = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
if (ap.strongAuthUnlockTimeout != timeoutMs) {
ap.strongAuthUnlockTimeout = timeoutMs;
saveSettingsLocked(userHandle);
@@ -6664,16 +6503,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(!isUserSelectable, "The credential "
+ "management app is not allowed to install a user selectable key pair");
@@ -6733,16 +6565,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
isAliasInCredentialManagementAppPolicy(caller, alias),
@@ -6802,13 +6627,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private boolean canInstallCertificates(CallerIdentity caller) {
- if (isPermissionCheckFlagEnabled()) {
- return hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId());
- } else {
- return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
- }
+ return isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
}
private boolean canChooseCertificates(CallerIdentity caller) {
@@ -7001,16 +6821,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
caller.getPackageName(), caller.getUid()));
enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(
- caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (
- isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(
+ caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (
+ isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
isAliasInCredentialManagementAppPolicy(caller, alias),
@@ -7143,16 +6956,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- if (isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES,
- caller.getPackageName(), caller.getUserId())
- || isCredentialManagementApp);
- } else {
- Preconditions.checkCallAuthorization((caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
- || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
- }
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
isAliasInCredentialManagementAppPolicy(caller, alias),
@@ -8285,29 +8091,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Preconditions.checkNotNull(who, "ComponentName is null");
- }
+
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
- if (!isPermissionCheckFlagEnabled()) {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
+
checkCanExecuteOrThrowUnsafe(DevicePolicyManager
.OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY);
final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
synchronized (getLockObject()) {
ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_FACTORY_RESET, caller.getPackageName(),
- UserHandle.USER_ALL)
- .getActiveAdmin();
- } else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
admin.mFactoryResetProtectionPolicy = policy;
saveSettingsLocked(caller.getUserId());
}
@@ -8347,7 +8145,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| hasCallingPermission(permission.MASTER_CLEAR)
|| hasCallingPermission(MANAGE_DEVICE_POLICY_FACTORY_RESET),
"Must be called by the FRP management agent on device");
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
@@ -9270,35 +9068,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- CallerIdentity caller;
- if (Flags.setAutoTimeZoneEnabledCoexistence()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
+ CallerIdentity caller = getCallerIdentity(who);
- if (Flags.setAutoTimeZoneEnabledCoexistence()) {
- // The effect of this policy is device-wide.
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- SET_TIME_ZONE,
- caller.getPackageName(),
- UserHandle.USER_ALL
- );
- mDevicePolicyEngine.setGlobalPolicy(
- PolicyDefinition.AUTO_TIMEZONE,
- // TODO(b/260573124): add correct enforcing admin when permission changes are
- // merged.
- enforcingAdmin,
- new BooleanPolicyValue(enabled));
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
- caller));
- mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+ caller));
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE)
@@ -9316,24 +9093,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
- CallerIdentity caller;
- if (Flags.setAutoTimeZoneEnabledCoexistence()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
+ caller));
+ return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
+ }
+
+ /**
+ * Set auto time zone state.
+ */
+ public void setAutoTimeZonePolicy(String callerPackageName, int policy) {
+ if (!mHasFeature) {
+ return;
}
- if (Flags.setAutoTimeZoneEnabledCoexistence()) {
- // The effect of this policy is device-wide.
- enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ // The effect of this policy is device-wide.
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ /* who */ null,
+ SET_TIME_ZONE,
+ caller.getPackageName(),
+ UserHandle.USER_ALL
+ );
+
+ if (policy != DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY) {
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.AUTO_TIME_ZONE,
+ enforcingAdmin,
+ new IntegerPolicyValue(policy));
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE)
+ .setAdmin(caller.getPackageName())
+ .setBoolean(policy == DevicePolicyManager.AUTO_TIME_ZONE_ENABLED)
+ .write();
} else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(
- caller));
+ mDevicePolicyEngine.removeGlobalPolicy(
+ PolicyDefinition.AUTO_TIME_ZONE,
+ enforcingAdmin);
}
+ }
- return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
+ /**
+ * Returns whether auto time zone is used on the device or not.
+ */
+ @Override
+ public int getAutoTimeZonePolicy(String callerPackageName) {
+ if (!mHasFeature) {
+ return DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ // The effect of this policy is device-wide.
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ /* who */ null,
+ SET_TIME_ZONE,
+ caller.getPackageName(),
+ UserHandle.USER_ALL
+ );
+ Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.AUTO_TIME_ZONE, enforcingAdmin);
+ return state != null ? state : DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY;
}
// TODO (b/137101239): remove this method in follow-up CL
@@ -10247,15 +10068,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin;
}
- ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked() {
- ensureLocked();
- ActiveAdmin doOrPo = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- if (isPermissionCheckFlagEnabled() && doOrPo == null) {
- return getUserData(0).mPermissionBasedAdmin;
- }
- return doOrPo;
- }
-
@Override
public void clearDeviceOwner(String packageName) {
Objects.requireNonNull(packageName, "packageName is null");
@@ -10998,8 +10810,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* (2.1.1) The caller is the profile owner.
* (2.1.2) The caller is from another app in the same user as the profile owner, AND
* the caller is the delegated cert installer.
- * (3) The caller holds the
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CERTIFICATES} permission.
*
* For the device owner case, simply check that the caller is the device owner or the
* delegated certificate installer.
@@ -11013,24 +10823,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@VisibleForTesting
boolean hasDeviceIdAccessUnchecked(String packageName, int uid) {
final int userId = UserHandle.getUserId(uid);
- // TODO(b/280048070): Introduce a permission to handle device ID access
- if (isPermissionCheckFlagEnabled()
- && !(isUidProfileOwnerLocked(uid) || isUidDeviceOwnerLocked(uid))) {
- return hasPermission(MANAGE_DEVICE_POLICY_CERTIFICATES, packageName, userId);
- } else {
- ComponentName deviceOwner = getDeviceOwnerComponent(true);
- if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
- || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
- return true;
- }
- ComponentName profileOwner = getProfileOwnerAsUser(userId);
- final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
- && (profileOwner.getPackageName().equals(packageName)
- || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
- if (isCallerProfileOwnerOrDelegate && (isProfileOwnerOfOrganizationOwnedDevice(userId)
- || isUserAffiliatedWithDevice(userId))) {
- return true;
- }
+ ComponentName deviceOwner = getDeviceOwnerComponent(true);
+ if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
+ || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
+ return true;
+ }
+ ComponentName profileOwner = getProfileOwnerAsUser(userId);
+ final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
+ && (profileOwner.getPackageName().equals(packageName)
+ || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
+ if (isCallerProfileOwnerOrDelegate && (isProfileOwnerOfOrganizationOwnedDevice(userId)
+ || isUserAffiliatedWithDevice(userId))) {
+ return true;
}
return false;
}
@@ -11731,25 +11535,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setDefaultSmsApplication(ComponentName admin, String callerPackageName,
String packageName, boolean parent) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- } else {
- caller = getCallerIdentity(admin);
- }
+ CallerIdentity caller = getCallerIdentity(admin);
- final int userId;
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(
- MANAGE_DEVICE_POLICY_DEFAULT_SMS,
- caller.getPackageName(),
- getAffectedUser(parent));
- } else {
- Objects.requireNonNull(admin, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ Objects.requireNonNull(admin, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
if (!parent && isManagedProfile(caller.getUserId())
&& getManagedSubscriptionsPolicy().getPolicyType()
@@ -11759,6 +11550,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ "ManagedSubscriptions policy is set");
}
+ final int userId;
if (parent) {
userId = getProfileParentId(mInjector.userHandleGetCallingUserId());
mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
@@ -11957,10 +11749,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(admin, "admin is null");
- }
-
+ Objects.requireNonNull(admin, "admin is null");
Objects.requireNonNull(agent, "agent is null");
PolicySizeVerifier.enforceMaxPackageNameLength(agent.getPackageName());
@@ -11972,19 +11761,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
- ActiveAdmin ap;
- if (isPermissionCheckFlagEnabled()) {
- CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
- int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
- ap = enforcePermissionAndGetEnforcingAdmin(
- admin,
- /*permission=*/MANAGE_DEVICE_POLICY_KEYGUARD,
- /*adminPolicy=*/DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES,
- caller.getPackageName(), affectedUserId).getActiveAdmin();
- } else {
- ap = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
- }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_TRUST_AGENT_CONFIGURATION);
@@ -12080,27 +11858,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void addCrossProfileIntentFilter(ComponentName who, String callerPackageName,
IntentFilter filter, int flags) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- int callingUserId = caller.getUserId();
+ CallerIdentity caller = getCallerIdentity(who);
+
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- callingUserId);
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- }
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
try {
+ int callingUserId = caller.getUserId();
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
Slogf.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no "
@@ -12144,28 +11911,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void clearCrossProfileIntentFilters(ComponentName who, String callerPackageName) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- int callingUserId = caller.getUserId();
+ CallerIdentity caller = getCallerIdentity(who);
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- caller.getPackageName(),
- callingUserId);
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
try {
+ int callingUserId = caller.getUserId();
UserInfo parent = mUserManager.getProfileParent(callingUserId);
if (parent == null) {
Slogf.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no "
@@ -13360,7 +13115,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public String[] setPackagesSuspended(ComponentName who, String callerPackage,
String[] packageNames, boolean suspended) {
- if (!Flags.unmanagedModeMigration()) {
+ if (!Flags.suspendPackagesCoexistence()) {
return setPackagesSuspendedPreCoexistence(who, callerPackage, packageNames, suspended);
}
@@ -13450,7 +13205,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- if (Flags.unmanagedModeMigration()) {
+ if (Flags.suspendPackagesCoexistence()) {
enforcePermission(
MANAGE_DEVICE_POLICY_PACKAGE_STATE,
caller.getPackageName(),
@@ -15166,19 +14921,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- enforcePermission(MANAGE_DEVICE_POLICY_WIFI, caller.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Preconditions.checkNotNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
@@ -15197,16 +14945,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
CallerIdentity caller = getCallerIdentity(who);
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_WIFI, who.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- Preconditions.checkNotNull(who, "ComponentName is null");
-
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalGetInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) > 0);
@@ -15294,18 +15036,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean setTime(@Nullable ComponentName who, String callerPackageName, long millis) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- // This is a global action.
- enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set time when auto time is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
@@ -15322,18 +15057,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean setTimeZone(@Nullable ComponentName who, String callerPackageName,
String timeZone) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- // This is a global action.
- enforcePermission(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
// Don't allow set timezone when auto timezone is on.
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
@@ -16537,22 +16265,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy.validateAgainstPreviousFreezePeriod(record.first, record.second,
LocalDate.now());
}
- CallerIdentity caller;
- synchronized (getLockObject()) {
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- enforcePermission(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, caller.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
+ CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(
+ isProfileOwnerOfOrganizationOwnedDevice(caller)
|| isDefaultDeviceOwner(caller));
- }
- checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
+ synchronized (getLockObject()) {
if (policy == null) {
mOwners.clearSystemUpdatePolicy();
} else {
@@ -16699,7 +16420,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mUserManager.getUserInfo(UserHandle.getCallingUserId()).isMain()) {
Slogf.w(LOG_TAG, "Only the system update service in the main user can broadcast "
+ "update information.");
- return;
}
});
@@ -16723,7 +16443,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
// Get running users.
- final int runningUserIds[];
+ final int[] runningUserIds;
try {
runningUserIds = mInjector.getIActivityManager().getRunningUserIds();
} catch (RemoteException e) {
@@ -16966,10 +16686,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
}
- if (!isRuntimePermission(permission)) {
- return false;
- }
- return true;
+ return isRuntimePermission(permission);
}
private void enforcePermissionGrantStateOnFinancedDevice(
@@ -17384,18 +17101,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public String getWifiMacAddress(ComponentName admin, String callerPackageName) {
-// if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(admin, "ComponentName is null");
-// }
+ Objects.requireNonNull(admin, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(admin, callerPackageName);
-// if (isPermissionCheckFlagEnabled()) {
-// enforcePermission(MANAGE_DEVICE_POLICY_WIFI, UserHandle.USER_ALL);
-// } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
-// }
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
String[] macAddresses = mInjector.getWifiManager().getFactoryMacAddresses();
@@ -17462,25 +17173,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return;
}
- CallerIdentity caller;
- ActiveAdmin admin;
message = PolicySizeVerifier.truncateIfLonger(message, MAX_SHORT_SUPPORT_MESSAGE_LENGTH);
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- admin = getActiveAdminForUidLocked(who, caller.getUid());
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ admin = getActiveAdminForUidLocked(who, caller.getUid());
}
synchronized (getLockObject()) {
@@ -17501,23 +17202,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!mHasFeature) {
return null;
}
- CallerIdentity caller;
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- caller = getCallerIdentity(who);
- Objects.requireNonNull(who, "ComponentName is null");
- synchronized (getLockObject()) {
- admin = getActiveAdminForUidLocked(who, caller.getUid());
- }
+ CallerIdentity caller = getCallerIdentity(who);
+ Objects.requireNonNull(who, "ComponentName is null");
+
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ admin = getActiveAdminForUidLocked(who, caller.getUid());
}
return admin.shortSupportMessage;
}
@@ -17680,26 +17371,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
CallerIdentity caller = getCallerIdentity(who);
- ActiveAdmin admin = null;
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
text = PolicySizeVerifier.truncateIfLonger(text, MAX_ORG_NAME_LENGTH);
synchronized (getLockObject()) {
- if (!isPermissionCheckFlagEnabled()) {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
if (!TextUtils.equals(admin.organizationName, text)) {
admin.organizationName = (text == null || text.length() == 0)
? null : text.toString();
@@ -17714,23 +17393,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return null;
}
CallerIdentity caller = getCallerIdentity(who);
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- synchronized (getLockObject()) {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
}
return admin.organizationName;
@@ -18214,28 +17884,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- if (isPermissionCheckFlagEnabled()) {
- synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
- || areAllUsersAffiliatedWithDeviceLocked());
- enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
- UserHandle.USER_ALL);
- }
+ if (admin != null) {
+ Preconditions.checkCallAuthorization(
+ isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isDefaultDeviceOwner(caller));
} else {
- if (admin != null) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDefaultDeviceOwner(caller));
- } else {
- // A delegate app passes a null admin component, which is expected
- Preconditions.checkCallAuthorization(
- isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
- }
+ // A delegate app passes a null admin component, which is expected
+ Preconditions.checkCallAuthorization(
+ isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
+ }
- synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
- || areAllUsersAffiliatedWithDeviceLocked());
- }
+ synchronized (getLockObject()) {
+ Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+ || areAllUsersAffiliatedWithDeviceLocked());
}
DevicePolicyEventLogger
@@ -18259,7 +17920,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return new ParceledListSlice<SecurityEvent>(output);
} catch (IOException e) {
Slogf.w(LOG_TAG, "Fail to read previous events" , e);
- return new ParceledListSlice<SecurityEvent>(Collections.<SecurityEvent>emptyList());
+ return new ParceledListSlice<SecurityEvent>(Collections.emptyList());
}
}
@@ -18752,8 +18413,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private boolean hasIncompatibleAccounts(int userId) {
- return mHasIncompatibleAccounts == null ? true
- : mHasIncompatibleAccounts.getOrDefault(userId, /* default= */ false);
+ return mHasIncompatibleAccounts == null || mHasIncompatibleAccounts.getOrDefault(
+ userId, /* default= */ false);
}
/**
@@ -18870,7 +18531,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
}
- };
+ }
private boolean isAdb(CallerIdentity caller) {
return isShellUid(caller) || isRootUid(caller);
@@ -20168,21 +19829,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void installUpdateFromFile(ComponentName admin, String callerPackageName,
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
- if (!isPermissionCheckFlagEnabled()) {
- Objects.requireNonNull(admin, "ComponentName is null");
- }
+ Objects.requireNonNull(admin, "ComponentName is null");
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(admin, callerPackageName);
- enforcePermission(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, caller.getPackageName(),
- UserHandle.USER_ALL);
- } else {
- caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
- }
+ CallerIdentity caller = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE);
DevicePolicyEventLogger
@@ -20752,32 +20404,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setCommonCriteriaModeEnabled(ComponentName who, String callerPackageName,
boolean enabled) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(who, callerPackageName);
- } else {
- caller = getCallerIdentity(who);
- }
- final ActiveAdmin admin;
+ CallerIdentity caller = getCallerIdentity(who);
- if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
- caller.getPackageName(),
- caller.getUserId());
- admin = enforcingAdmin.getActiveAdmin();
- } else {
- Objects.requireNonNull(who, "ComponentName is null");
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "Common Criteria mode can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
- synchronized (getLockObject()) {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
- }
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "Common Criteria mode can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
admin.mCommonCriteriaMode = enabled;
saveSettingsLocked(caller.getUserId());
}
@@ -20809,7 +20444,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// their ActiveAdmin, instead of iterating through all admins.
ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- return admin != null ? admin.mCommonCriteriaMode : false;
+ return admin != null && admin.mCommonCriteriaMode;
}
}
@@ -21418,6 +21053,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public boolean removeManagedProfile(int userId) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ if (!isManagedProfile(userId)){
+ throw new IllegalArgumentException("Cannot remove user as it is not a managed profile");
+ }
+
+ boolean success = false;
+ final long identity = Binder.clearCallingIdentity();
+ try{
+ success = mUserManager.removeUserEvenWhenDisallowed(userId);
+ } catch (Exception e) {
+ Slogf.e(LOG_TAG, "Remove managed profile failed due to: ", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return success;
+ }
+
+ @Override
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams,
@NonNull String callerPackage) {
@@ -22209,7 +21865,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else {
owner = getDeviceOrProfileOwnerAdminLocked(userId);
}
- boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+ boolean canGrant = owner != null && owner.mAdminCanGrantSensorsPermissions;
mPolicyCache.setAdminCanGrantSensorsPermissions(canGrant);
}
}
@@ -22408,27 +22064,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setMinimumRequiredWifiSecurityLevel(String callerPackageName, int level) {
- CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(callerPackageName);
- } else {
- caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "Wi-Fi minimum security level can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
- }
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "Wi-Fi minimum security level can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
boolean valueChanged = false;
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(/* admin= */ null,
- MANAGE_DEVICE_POLICY_WIFI, caller.getPackageName(), caller.getUserId())
- .getActiveAdmin();
- } else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
if (admin.mWifiMinimumSecurityLevel != level) {
admin.mWifiMinimumSecurityLevel = level;
saveSettingsLocked(caller.getUserId());
@@ -22450,21 +22094,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public WifiSsidPolicy getWifiSsidPolicy(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity();
- if (isPermissionCheckFlagEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_WIFI, callerPackageName,
- caller.getUserId());
- } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || canQueryAdminPolicy(caller),
- "SSID policy can only be retrieved by a device owner or "
- + "a profile owner on an organization-owned device or "
- + "an app with the QUERY_ADMIN_POLICY permission.");
- }
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller)
+ || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || canQueryAdminPolicy(caller),
+ "SSID policy can only be retrieved by a device owner or "
+ + "a profile owner on an organization-owned device or "
+ + "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
ActiveAdmin admin;
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
return admin != null ? admin.mWifiSsidPolicy : null;
}
}
@@ -22485,29 +22124,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setWifiSsidPolicy(String callerPackageName, WifiSsidPolicy policy) {
- CallerIdentity caller;
-
- if (isPermissionCheckFlagEnabled()) {
- caller = getCallerIdentity(callerPackageName);
- } else {
- caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
- "SSID denylist can only be controlled by a device owner or "
- + "a profile owner on an organization-owned device.");
- }
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "SSID denylist can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
boolean changed = false;
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
- /* admin= */ null, MANAGE_DEVICE_POLICY_WIFI,
- caller.getPackageName(),
- caller.getUserId()).getActiveAdmin();
- } else {
- admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- }
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
if (!Objects.equals(policy, admin.mWifiSsidPolicy)) {
admin.mWifiSsidPolicy = policy;
changed = true;
@@ -22715,7 +22340,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private final class DevicePolicyManagementRoleObserver implements OnRoleHoldersChangedListener {
- private RoleManager mRm;
+ private final RoleManager mRm;
private final Executor mExecutor;
private final Context mContext;
@@ -22732,13 +22357,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
mDevicePolicyEngine.handleRoleChanged(roleName, user.getIdentifier());
- if (RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) {
- handleDevicePolicyManagementRoleChange(user);
- return;
- }
- if (RoleManager.ROLE_FINANCED_DEVICE_KIOSK.equals(roleName)) {
- handleFinancedDeviceKioskRoleChange();
- return;
+ switch (roleName) {
+ case RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT ->
+ handleDevicePolicyManagementRoleChange(user);
+ case RoleManager.ROLE_FINANCED_DEVICE_KIOSK ->
+ handleFinancedDeviceKioskRoleChange();
}
}
@@ -23390,26 +23013,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Checks if the calling process has been granted permission to apply a device policy on a
- * specific user.
- * The given permission will be checked along with its associated cross-user permission if it
- * exists and the target user is different to the calling user.
- * Returns an {@link EnforcingAdmin} for the caller.
- *
- * @param admin the component name of the admin.
- * @param callerPackageName The package name of the calling application.
- * @param permission The name of the permission being checked.
- * @param deviceAdminPolicy The userId of the user which the caller needs permission to act on.
- * @throws SecurityException if the caller has not been granted the given permission,
- * the associated cross-user permission if the caller's user is different to the target user.
- */
- private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin,
- String permission, int deviceAdminPolicy, String callerPackageName, int targetUserId) {
- enforcePermission(permission, deviceAdminPolicy, callerPackageName, targetUserId);
- return getEnforcingAdminForCaller(admin, callerPackageName);
- }
-
- /**
- * Checks if the calling process has been granted permission to apply a device policy on a
* specific user. Only one permission provided in the list needs to be granted to pass this
* check.
* The given permissions will be checked along with their associated cross-user permissions if
@@ -23431,23 +23034,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
/**
- * Checks whether the calling process has been granted permission to query a device policy on
- * a specific user.
- * The given permission will be checked along with its associated cross-user permission if it
- * exists and the target user is different to the calling user.
- *
- * @param permission The name of the permission being checked.
- * @param targetUserId The userId of the user which the caller needs permission to act on.
- * @throws SecurityException if the caller has not been granted the given permission,
- * the associated cross-user permission if the caller's user is different to the target user.
- */
- private EnforcingAdmin enforceCanQueryAndGetEnforcingAdmin(@Nullable ComponentName admin,
- String permission, String callerPackageName, int targetUserId) {
- enforceCanQuery(permission, callerPackageName, targetUserId);
- return getEnforcingAdminForCaller(admin, callerPackageName);
- }
-
- /**
* Checks if the calling process has been granted permission to apply a device policy.
*
* @param callerPackageName The package name of the calling application.
@@ -23754,13 +23340,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return NOT_A_DPC;
}
- private boolean isPermissionCheckFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- PERMISSION_BASED_ACCESS_EXPERIMENT_FLAG,
- DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
- }
-
private static boolean isSetStatusBarDisabledCoexistenceEnabled() {
return false;
}
@@ -23837,58 +23416,83 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
}
- if (isPermissionCheckFlagEnabled()) {
+ if (Flags.setMtePolicyCoexistence()) {
enforcePermission(MANAGE_DEVICE_POLICY_MTE, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller));
}
+
synchronized (getLockObject()) {
- ActiveAdmin admin =
+ if (Flags.setMtePolicyCoexistence()) {
+ final EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(null,
+ MANAGE_DEVICE_POLICY_MTE, callerPackageName, caller.getUserId());
+ if (flags != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.MEMORY_TAGGING,
+ admin,
+ new IntegerPolicyValue(flags));
+ } else {
+ mDevicePolicyEngine.removeGlobalPolicy(
+ PolicyDefinition.MEMORY_TAGGING,
+ admin);
+ }
+ } else {
+ ActiveAdmin admin =
getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
-
- if (admin != null) {
- final String memtagProperty = "arm64.memtag.bootctl";
- if (flags == DevicePolicyManager.MTE_ENABLED) {
- mInjector.systemPropertiesSet(memtagProperty, "memtag");
- } else if (flags == DevicePolicyManager.MTE_DISABLED) {
- mInjector.systemPropertiesSet(memtagProperty, "memtag-off");
- } else if (flags == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
- if (admin.mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
- mInjector.systemPropertiesSet(memtagProperty, "default");
+ if (admin != null) {
+ final String memtagProperty = "arm64.memtag.bootctl";
+ if (flags == DevicePolicyManager.MTE_ENABLED) {
+ mInjector.systemPropertiesSet(memtagProperty, "memtag");
+ } else if (flags == DevicePolicyManager.MTE_DISABLED) {
+ mInjector.systemPropertiesSet(memtagProperty, "memtag-off");
+ } else if (flags == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ if (admin.mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ mInjector.systemPropertiesSet(memtagProperty, "default");
+ }
}
+ admin.mtePolicy = flags;
+ saveSettingsLocked(caller.getUserId());
}
- admin.mtePolicy = flags;
- saveSettingsLocked(caller.getUserId());
-
- DevicePolicyEventLogger.createEvent(DevicePolicyEnums.SET_MTE_POLICY)
- .setInt(flags)
- .setAdmin(caller.getPackageName())
- .write();
}
+
+ DevicePolicyEventLogger.createEvent(DevicePolicyEnums.SET_MTE_POLICY)
+ .setInt(flags)
+ .setAdmin(caller.getPackageName())
+ .write();
}
}
@Override
public int getMtePolicy(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (isPermissionCheckFlagEnabled()) {
+ if (Flags.setMtePolicyCoexistence()) {
enforcePermission(MANAGE_DEVICE_POLICY_MTE, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller)
- || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isSystemUid(caller));
+ || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isSystemUid(caller));
}
+
synchronized (getLockObject()) {
- ActiveAdmin admin =
+ if (Flags.setMtePolicyCoexistence()) {
+ final EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(null,
+ MANAGE_DEVICE_POLICY_MTE, callerPackageName, caller.getUserId());
+ final Integer policyFromAdmin = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.MEMORY_TAGGING, admin);
+ return (policyFromAdmin != null ? policyFromAdmin
+ : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY);
+ } else {
+ ActiveAdmin admin =
getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- return admin != null
- ? admin.mtePolicy
- : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+ return admin != null
+ ? admin.mtePolicy
+ : DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+ }
}
}
@@ -24205,9 +23809,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Slogf.i(LOG_TAG,
"Started device policies migration to the device policy engine.");
// TODO(b/359188869): Move this to the current migration method.
- if (Flags.setAutoTimeZoneEnabledCoexistence()) {
- migrateAutoTimezonePolicy();
- }
if (Flags.setPermissionGrantStateCoexistence()) {
migratePermissionGrantStatePolicies();
}
@@ -24235,27 +23836,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
maybeMigrateSecurityLoggingPolicyLocked();
// ID format: <sdk-int>.<auto_increment_id>.<descriptions>'
String unmanagedBackupId = "35.1.unmanaged-mode";
- boolean unmanagedMigrated = false;
- unmanagedMigrated =
- unmanagedMigrated | maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
- unmanagedMigrated =
- unmanagedMigrated | maybeMigrateSuspendedPackagesLocked(unmanagedBackupId);
+ boolean unmanagedMigrated = maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
if (unmanagedMigrated) {
Slogf.i(LOG_TAG, "Backup made: " + unmanagedBackupId);
}
String supervisionBackupId = "36.2.supervision-support";
boolean supervisionMigrated = maybeMigrateResetPasswordTokenLocked(supervisionBackupId);
+ supervisionMigrated |= maybeMigrateSuspendedPackagesLocked(supervisionBackupId);
if (supervisionMigrated) {
Slogf.i(LOG_TAG, "Backup made: " + supervisionBackupId);
}
- // Additional migration steps should repeat the pattern above with a new backupId.
- }
+ String memoryTaggingBackupId = "36.3.memory-tagging";
+ boolean memoryTaggingMigrated = maybeMigrateMemoryTaggingLocked(memoryTaggingBackupId);
+ if (memoryTaggingMigrated) {
+ Slogf.i(LOG_TAG, "Backup made: " + memoryTaggingBackupId);
+ }
- private void migrateAutoTimezonePolicy() {
- Slogf.i(LOG_TAG, "Skipping Migration of AUTO_TIMEZONE policy to device policy engine,"
- + "as no way to identify if the value was set by the admin or the user.");
+ // Additional migration steps should repeat the pattern above with a new backupId.
}
private void migratePermissionGrantStatePolicies() {
@@ -24666,7 +24265,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| isCallerDevicePolicyManagementRoleHolder(caller)
|| isCallerSystemSupervisionRoleHolder(caller));
return getFinancedDeviceKioskRoleHolderOnAnyUser() != null;
- };
+ }
@Override
public String getFinancedDeviceKioskRoleHolder(String callerPackageName) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index b3c8408ff54b..be4eea42f09e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -682,6 +682,19 @@ class Owners {
}
}
+ void markMemoryTaggingMigrated() {
+ synchronized (mData) {
+ mData.mMemoryTaggingMigrated = true;
+ mData.writeDeviceOwner();
+ }
+ }
+
+ boolean isMemoryTaggingMigrated() {
+ synchronized (mData) {
+ return mData.mMemoryTaggingMigrated;
+ }
+ }
+
@GuardedBy("mData")
void pushToAppOpsLocked() {
if (!mSystemReady) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 10e43d955fab..1cae924a8cd1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -93,6 +93,9 @@ class OwnersData {
private static final String ATTR_SUSPENDED_PACKAGES_MIGRATED = "suspendedPackagesMigrated";
private static final String ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED =
"resetPasswordWithTokenMigrated";
+ private static final String ATTR_MEMORY_TAGGING_MIGRATED =
+ "memoryTaggingMigrated";
+
private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade";
// Internal state for the device owner package.
@@ -125,6 +128,7 @@ class OwnersData {
boolean mRequiredPasswordComplexityMigrated = false;
boolean mSuspendedPackagesMigrated = false;
boolean mResetPasswordWithTokenMigrated = false;
+ boolean mMemoryTaggingMigrated = false;
boolean mPoliciesMigratedPostUpdate = false;
@@ -416,6 +420,8 @@ class OwnersData {
if (Flags.unmanagedModeMigration()) {
out.attributeBoolean(null, ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED,
mRequiredPasswordComplexityMigrated);
+ }
+ if (Flags.suspendPackagesCoexistence()) {
out.attributeBoolean(null, ATTR_SUSPENDED_PACKAGES_MIGRATED,
mSuspendedPackagesMigrated);
@@ -424,6 +430,10 @@ class OwnersData {
out.attributeBoolean(null, ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED,
mResetPasswordWithTokenMigrated);
}
+ if (Flags.setMtePolicyCoexistence()) {
+ out.attributeBoolean(null, ATTR_MEMORY_TAGGING_MIGRATED,
+ mMemoryTaggingMigrated);
+ }
out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
}
@@ -491,13 +501,15 @@ class OwnersData {
mRequiredPasswordComplexityMigrated = Flags.unmanagedModeMigration()
&& parser.getAttributeBoolean(null,
ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED, false);
- mSuspendedPackagesMigrated = Flags.unmanagedModeMigration()
+ mSuspendedPackagesMigrated = Flags.suspendPackagesCoexistence()
&& parser.getAttributeBoolean(null,
ATTR_SUSPENDED_PACKAGES_MIGRATED, false);
mResetPasswordWithTokenMigrated = Flags.resetPasswordWithTokenCoexistence()
&& parser.getAttributeBoolean(null,
ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED, false);
-
+ mMemoryTaggingMigrated = Flags.setMtePolicyCoexistence()
+ && parser.getAttributeBoolean(null,
+ ATTR_MEMORY_TAGGING_MIGRATED, false);
break;
default:
Slog.e(TAG, "Unexpected tag: " + tag);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index f271162bbfa4..a5aeaace94bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import static com.android.server.devicepolicy.DevicePolicyEngine.DEVICE_LOCK_CONTROLLER_ROLE;
+import static com.android.server.devicepolicy.DevicePolicyEngine.SYSTEM_SUPERVISION_ROLE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -53,6 +54,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
final class PolicyDefinition<V> {
@@ -93,14 +95,18 @@ final class PolicyDefinition<V> {
private static final MostRestrictive<Boolean> TRUE_MORE_RESTRICTIVE = new MostRestrictive<>(
List.of(new BooleanPolicyValue(true), new BooleanPolicyValue(false)));
- static PolicyDefinition<Boolean> AUTO_TIMEZONE = new PolicyDefinition<>(
+ static PolicyDefinition<Integer> AUTO_TIME_ZONE = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY),
- // auto timezone is disabled by default, hence enabling it is more restrictive.
- TRUE_MORE_RESTRICTIVE,
+ // Auto time zone is enabled by default. Enabled state has higher priority given it
+ // means the time will be more precise and other applications can rely on that for
+ // their purposes.
+ new TopPriority<>(List.of(
+ EnforcingAdmin.getRoleAuthorityOf(SYSTEM_SUPERVISION_ROLE),
+ EnforcingAdmin.getRoleAuthorityOf(DEVICE_LOCK_CONTROLLER_ROLE),
+ EnforcingAdmin.DPC_AUTHORITY)),
POLICY_FLAG_GLOBAL_ONLY_POLICY,
- (Boolean value, Context context, Integer userId, PolicyKey policyKey) ->
- PolicyEnforcerCallbacks.setAutoTimezoneEnabled(value, context),
- new BooleanPolicySerializer());
+ PolicyEnforcerCallbacks::setAutoTimeZonePolicy,
+ new IntegerPolicySerializer());
static final PolicyDefinition<Integer> GENERIC_PERMISSION_GRANT =
new PolicyDefinition<>(
@@ -336,12 +342,19 @@ final class PolicyDefinition<V> {
PolicyEnforcerCallbacks::noOp,
new PackageSetPolicySerializer());
+ static PolicyDefinition<Integer> MEMORY_TAGGING = new PolicyDefinition<>(
+ new NoArgsPolicyKey(
+ DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY),
+ new TopPriority<>(List.of(EnforcingAdmin.DPC_AUTHORITY)),
+ PolicyEnforcerCallbacks::setMtePolicy,
+ new IntegerPolicySerializer());
+
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
// TODO(b/277218360): Revisit policies that should be marked as global-only.
static {
- POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIME_ZONE);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
GENERIC_PERMISSION_GRANT);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.SECURITY_LOGGING_POLICY,
@@ -382,6 +395,8 @@ final class PolicyDefinition<V> {
PASSWORD_COMPLEXITY);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PACKAGES_SUSPENDED_POLICY,
PACKAGES_SUSPENDED);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY,
+ MEMORY_TAGGING);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
@@ -504,7 +519,8 @@ final class PolicyDefinition<V> {
private final int mPolicyFlags;
// A function that accepts policy to apply, context, userId, callback arguments, and returns
// true if the policy has been enforced successfully.
- private final QuadFunction<V, Context, Integer, PolicyKey, Boolean> mPolicyEnforcerCallback;
+ private final QuadFunction<V, Context, Integer, PolicyKey, CompletableFuture<Boolean>>
+ mPolicyEnforcerCallback;
private final PolicySerializer<V> mPolicySerializer;
private PolicyDefinition<V> createPolicyDefinition(PolicyKey key) {
@@ -574,7 +590,7 @@ final class PolicyDefinition<V> {
return mResolutionMechanism.resolve(adminsPolicy);
}
- boolean enforcePolicy(@Nullable V value, Context context, int userId) {
+ CompletableFuture<Boolean> enforcePolicy(@Nullable V value, Context context, int userId) {
return mPolicyEnforcerCallback.apply(value, context, userId, mPolicyKey);
}
@@ -592,7 +608,6 @@ final class PolicyDefinition<V> {
POLICY_DEFINITIONS.put(key.getIdentifier(), definition);
}
-
/**
* Callers must ensure that {@code policyType} have implemented an appropriate
* {@link Object#equals} implementation.
@@ -600,7 +615,8 @@ final class PolicyDefinition<V> {
private PolicyDefinition(
@NonNull PolicyKey key,
ResolutionMechanism<V> resolutionMechanism,
- QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback,
+ QuadFunction<V, Context, Integer, PolicyKey, CompletableFuture<Boolean>>
+ policyEnforcerCallback,
PolicySerializer<V> policySerializer) {
this(key, resolutionMechanism, POLICY_FLAG_NONE, policyEnforcerCallback, policySerializer);
}
@@ -610,10 +626,11 @@ final class PolicyDefinition<V> {
* {@link Object#equals} and {@link Object#hashCode()} implementation.
*/
private PolicyDefinition(
- @NonNull PolicyKey policyKey,
+ @NonNull PolicyKey policyKey,
ResolutionMechanism<V> resolutionMechanism,
int policyFlags,
- QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback,
+ QuadFunction<V, Context, Integer, PolicyKey, CompletableFuture<Boolean>>
+ policyEnforcerCallback,
PolicySerializer<V> policySerializer) {
Objects.requireNonNull(policyKey);
mPolicyKey = policyKey;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 4d9abf1d6be0..40d8dae41dcf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -47,6 +47,7 @@ import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.permission.AdminPermissionControlParams;
import android.permission.PermissionControllerManager;
@@ -55,6 +56,7 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.IWindowManager;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
@@ -65,6 +67,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -73,33 +76,40 @@ final class PolicyEnforcerCallbacks {
private static final String LOG_TAG = "PolicyEnforcerCallbacks";
- static <T> boolean noOp(T value, Context context, Integer userId, PolicyKey policyKey) {
- return true;
+ static <T> CompletableFuture<Boolean> noOp(T value, Context context, Integer userId,
+ PolicyKey policyKey) {
+ return AndroidFuture.completedFuture(true);
}
- static boolean setAutoTimezoneEnabled(@Nullable Boolean enabled, @NonNull Context context) {
+ static CompletableFuture<Boolean> setAutoTimeZonePolicy(
+ @Nullable Integer policy, @NonNull Context context, int userId,
+ @NonNull PolicyKey policyKey) {
if (!Flags.setAutoTimeZoneEnabledCoexistence()) {
- Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off.");
- return true;
+ Slogf.w(LOG_TAG, "Trying to enforce setAutoTimeZonePolicy while flag is off.");
+ return AndroidFuture.completedFuture(true);
}
return Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(context);
-
- int value = enabled != null && enabled ? 1 : 0;
- return Settings.Global.putInt(
+ if (policy != null &&
+ policy == DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY) {
+ return AndroidFuture.completedFuture(false);
+ }
+ int enabled = policy != null &&
+ policy == DevicePolicyManager.AUTO_TIME_ZONE_ENABLED ? 1 : 0;
+ return AndroidFuture.completedFuture(Settings.Global.putInt(
context.getContentResolver(), Settings.Global.AUTO_TIME_ZONE,
- value);
+ enabled));
});
}
- static boolean setPermissionGrantState(
+ static CompletableFuture<Boolean> setPermissionGrantState(
@Nullable Integer grantState, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
if (!Flags.setPermissionGrantStateCoexistence()) {
Slogf.w(LOG_TAG, "Trying to enforce setPermissionGrantState while flag is off.");
- return true;
+ return AndroidFuture.completedFuture(true);
}
- return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
+ return Binder.withCleanCallingIdentity(() -> {
if (!(policyKey instanceof PackagePermissionPolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
+ "PermissionGrantStatePolicyKey, passed in policyKey is: " + policyKey);
@@ -125,12 +135,13 @@ final class PolicyEnforcerCallbacks {
.setRuntimePermissionGrantStateByDeviceAdmin(context.getPackageName(),
permissionParams, context.getMainExecutor(), callback::trigger);
try {
- return callback.await(20_000, TimeUnit.MILLISECONDS);
+ return AndroidFuture.completedFuture(
+ callback.await(20_000, TimeUnit.MILLISECONDS));
} catch (Exception e) {
// TODO: add logging
- return false;
+ return AndroidFuture.completedFuture(false);
}
- }));
+ });
}
@NonNull
@@ -149,23 +160,23 @@ final class PolicyEnforcerCallbacks {
}
}
- static boolean enforceSecurityLogging(
+ static CompletableFuture<Boolean> enforceSecurityLogging(
@Nullable Boolean value, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
dpmi.enforceSecurityLoggingPolicy(Boolean.TRUE.equals(value));
- return true;
+ return AndroidFuture.completedFuture(true);
}
- static boolean enforceAuditLogging(
+ static CompletableFuture<Boolean> enforceAuditLogging(
@Nullable Boolean value, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
dpmi.enforceAuditLoggingPolicy(Boolean.TRUE.equals(value));
- return true;
+ return AndroidFuture.completedFuture(true);
}
- static boolean setLockTask(
+ static CompletableFuture<Boolean> setLockTask(
@Nullable LockTaskPolicy policy, @NonNull Context context, int userId) {
List<String> packages = Collections.emptyList();
int flags = LockTaskPolicy.DEFAULT_LOCK_TASK_FLAG;
@@ -175,7 +186,7 @@ final class PolicyEnforcerCallbacks {
}
DevicePolicyManagerService.updateLockTaskPackagesLocked(context, packages, userId);
DevicePolicyManagerService.updateLockTaskFeaturesLocked(flags, userId);
- return true;
+ return AndroidFuture.completedFuture(true);
}
@@ -187,8 +198,8 @@ final class PolicyEnforcerCallbacks {
* rely on the POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED flag so DPE only invokes this callback
* when the policy is set, and not during system boot or other situations.
*/
- static boolean setApplicationRestrictions(Bundle bundle, Context context, Integer userId,
- PolicyKey policyKey) {
+ static CompletableFuture<Boolean> setApplicationRestrictions(Bundle bundle, Context context,
+ Integer userId, PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
PackagePolicyKey key = (PackagePolicyKey) policyKey;
String packageName = key.getPackageName();
@@ -198,12 +209,13 @@ final class PolicyEnforcerCallbacks {
changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
context.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
});
- return true;
+ return AndroidFuture.completedFuture(true);
}
private static class BlockingCallback {
private final CountDownLatch mLatch = new CountDownLatch(1);
private final AtomicReference<Boolean> mValue = new AtomicReference<>();
+
public void trigger(Boolean value) {
mValue.set(value);
mLatch.countDown();
@@ -220,7 +232,7 @@ final class PolicyEnforcerCallbacks {
// TODO: when a local policy exists for a user, this callback will be invoked for this user
// individually as well as for USER_ALL. This can be optimized by separating local and global
// enforcement in the policy engine.
- static boolean setUserControlDisabledPackages(
+ static CompletableFuture<Boolean> setUserControlDisabledPackages(
@Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
PackageManagerInternal pmi =
@@ -246,7 +258,7 @@ final class PolicyEnforcerCallbacks {
}
}
});
- return true;
+ return AndroidFuture.completedFuture(true);
}
/** Handles USER_ALL expanding it into the list of all intact users. */
@@ -271,7 +283,7 @@ final class PolicyEnforcerCallbacks {
}
}
- static boolean addPersistentPreferredActivity(
+ static CompletableFuture<Boolean> addPersistentPreferredActivity(
@Nullable ComponentName preferredActivity, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
@@ -297,13 +309,13 @@ final class PolicyEnforcerCallbacks {
Slog.wtf(LOG_TAG, "Error adding/removing persistent preferred activity", re);
}
});
- return true;
+ return AndroidFuture.completedFuture(true);
}
- static boolean setUninstallBlocked(
+ static CompletableFuture<Boolean> setUninstallBlocked(
@Nullable Boolean uninstallBlocked, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
- return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
+ return Binder.withCleanCallingIdentity(() -> {
if (!(policyKey instanceof PackagePolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
+ "PackagePolicyKey, passed in policyKey is: " + policyKey);
@@ -314,14 +326,14 @@ final class PolicyEnforcerCallbacks {
packageName,
uninstallBlocked != null && uninstallBlocked,
userId);
- return true;
- }));
+ return AndroidFuture.completedFuture(true);
+ });
}
- static boolean setUserRestriction(
+ static CompletableFuture<Boolean> setUserRestriction(
@Nullable Boolean enabled, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
- return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
+ return Binder.withCleanCallingIdentity(() -> {
if (!(policyKey instanceof UserRestrictionPolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
+ "UserRestrictionPolicyKey, passed in policyKey is: " + policyKey);
@@ -331,14 +343,14 @@ final class PolicyEnforcerCallbacks {
UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
userManager.setUserRestriction(
userId, parsedKey.getRestriction(), enabled != null && enabled);
- return true;
- }));
+ return AndroidFuture.completedFuture(true);
+ });
}
- static boolean setApplicationHidden(
+ static CompletableFuture<Boolean> setApplicationHidden(
@Nullable Boolean hide, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
- return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
+ return Binder.withCleanCallingIdentity(() -> {
if (!(policyKey instanceof PackagePolicyKey)) {
throw new IllegalArgumentException("policyKey is not of type "
+ "PackagePolicyKey, passed in policyKey is: " + policyKey);
@@ -346,12 +358,13 @@ final class PolicyEnforcerCallbacks {
PackagePolicyKey parsedKey = (PackagePolicyKey) policyKey;
String packageName = Objects.requireNonNull(parsedKey.getPackageName());
IPackageManager packageManager = AppGlobals.getPackageManager();
- return packageManager.setApplicationHiddenSettingAsUser(
- packageName, hide != null && hide, userId);
- }));
+ return AndroidFuture.completedFuture(
+ packageManager.setApplicationHiddenSettingAsUser(
+ packageName, hide != null && hide, userId));
+ });
}
- static boolean setScreenCaptureDisabled(
+ static CompletableFuture<Boolean> setScreenCaptureDisabled(
@Nullable Boolean disabled, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
@@ -363,10 +376,10 @@ final class PolicyEnforcerCallbacks {
updateScreenCaptureDisabled();
}
});
- return true;
+ return AndroidFuture.completedFuture(true);
}
- static boolean setContentProtectionPolicy(
+ static CompletableFuture<Boolean> setContentProtectionPolicy(
@Nullable Integer value,
@NonNull Context context,
@UserIdInt Integer userId,
@@ -378,7 +391,7 @@ final class PolicyEnforcerCallbacks {
cacheImpl.setContentProtectionPolicy(userId, value);
}
});
- return true;
+ return AndroidFuture.completedFuture(true);
}
private static void updateScreenCaptureDisabled() {
@@ -393,7 +406,7 @@ final class PolicyEnforcerCallbacks {
});
}
- static boolean setPersonalAppsSuspended(
+ static CompletableFuture<Boolean> setPersonalAppsSuspended(
@Nullable Boolean suspended, @NonNull Context context, int userId,
@NonNull PolicyKey policyKey) {
Binder.withCleanCallingIdentity(() -> {
@@ -404,7 +417,7 @@ final class PolicyEnforcerCallbacks {
.unsuspendAdminSuspendedPackages(userId);
}
});
- return true;
+ return AndroidFuture.completedFuture(true);
}
private static void suspendPersonalAppsInPackageManager(Context context, int userId) {
@@ -418,13 +431,53 @@ final class PolicyEnforcerCallbacks {
}
}
- static boolean setUsbDataSignalingEnabled(@Nullable Boolean value, @NonNull Context context) {
+ static CompletableFuture<Boolean> setUsbDataSignalingEnabled(@Nullable Boolean value,
+ @NonNull Context context) {
return Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(context);
boolean enabled = value == null || value;
DevicePolicyManagerService.updateUsbDataSignal(context, enabled);
- return true;
+ return AndroidFuture.completedFuture(true);
});
}
+
+ static CompletableFuture<Boolean> setMtePolicy(
+ @Nullable Integer mtePolicy, @NonNull Context context, int userId,
+ @NonNull PolicyKey policyKey) {
+ if (mtePolicy == null) {
+ mtePolicy = DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
+ }
+ final Set<Integer> allowedModes =
+ Set.of(
+ DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY,
+ DevicePolicyManager.MTE_DISABLED,
+ DevicePolicyManager.MTE_ENABLED);
+ if (!allowedModes.contains(mtePolicy)) {
+ Slog.wtf(LOG_TAG, "MTE policy is not a known one: " + mtePolicy);
+ return AndroidFuture.completedFuture(false);
+ }
+
+ final String mteDpmSystemProperty =
+ "ro.arm64.memtag.bootctl_device_policy_manager";
+ final String mteSettingsSystemProperty =
+ "ro.arm64.memtag.bootctl_settings_toggle";
+ final String mteControlProperty = "arm64.memtag.bootctl";
+
+ final boolean isAvailable = SystemProperties.getBoolean(mteDpmSystemProperty,
+ SystemProperties.getBoolean(mteSettingsSystemProperty, false));
+ if (!isAvailable) {
+ return AndroidFuture.completedFuture(false);
+ }
+
+ if (mtePolicy == DevicePolicyManager.MTE_ENABLED) {
+ SystemProperties.set(mteControlProperty, "memtag");
+ } else if (mtePolicy == DevicePolicyManager.MTE_DISABLED) {
+ SystemProperties.set(mteControlProperty, "memtag-off");
+ } else if (mtePolicy == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+ SystemProperties.set(mteControlProperty, "default");
+ }
+
+ return AndroidFuture.completedFuture(true);
+ }
}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
index cc5573bb01d8..f34ec72d7e27 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
@@ -19,6 +19,7 @@ package com.android.server.policy;
import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
@@ -71,6 +72,7 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements
private static final int DEVICE_STATE_OPENED = 2;
private static final int DEVICE_STATE_REAR_DISPLAY = 3;
private static final int DEVICE_STATE_CONCURRENT_INNER_DEFAULT = 4;
+ private static final int DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT = 5;
private static final int TENT_MODE_SWITCH_ANGLE_DEGREES = 90;
private static final int TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES = 125;
private static final int MIN_CLOSED_ANGLE_DEGREES = 0;
@@ -130,14 +132,17 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements
return hingeAngle >= MAX_CLOSED_ANGLE_DEGREES
&& hingeAngle <= TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES;
}),
- createConfig(getOpenedDeviceState(), /* activeStatePredicate= */
- ALLOWED),
- createConfig(getRearDisplayDeviceState(), /* activeStatePredicate= */
- NOT_ALLOWED),
- createConfig(getDualDisplayDeviceState(), /* activeStatePredicate= */
- NOT_ALLOWED, /* availabilityPredicate= */
- provider -> !mIsDualDisplayBlockingEnabled
- || provider.hasNoConnectedExternalDisplay())};
+ createConfig(getOpenedDeviceState(),
+ /* activeStatePredicate= */ ALLOWED),
+ createConfig(getRearDisplayDeviceState(),
+ /* activeStatePredicate= */ NOT_ALLOWED),
+ createConfig(getDualDisplayDeviceState(),
+ /* activeStatePredicate= */ NOT_ALLOWED,
+ /* availabilityPredicate= */ provider -> !mIsDualDisplayBlockingEnabled
+ || provider.hasNoConnectedExternalDisplay()),
+ createConfig(getRearDisplayOuterDefaultState(),
+ /* activeStatePredicate= */ NOT_ALLOWED)
+ };
}
private DeviceStatePredicateWrapper createClosedConfiguration(
@@ -266,4 +271,24 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements
.setSystemProperties(systemProperties)
.build());
}
+
+ /**
+ * Returns the {link DeviceState.Configuration} that represents the new rear display state
+ * where the inner display is also enabled, showing a system affordance to exit the state.
+ */
+ @NonNull
+ private DeviceState getRearDisplayOuterDefaultState() {
+ Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties = new HashSet<>(
+ List.of(PROPERTY_EMULATED_ONLY,
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST,
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT));
+
+ return new DeviceState(new DeviceState.Configuration.Builder(
+ DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT,
+ "REAR_DISPLAY_OUTER_DEFAULT")
+ .setSystemProperties(systemProperties)
+ .build());
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3805c02d1bb9..19b03437292f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -119,7 +119,6 @@ import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accounts.AccountManagerService;
-import com.android.server.adaptiveauth.AdaptiveAuthService;
import com.android.server.adb.AdbService;
import com.android.server.alarm.AlarmManagerService;
import com.android.server.am.ActivityManagerService;
@@ -205,6 +204,7 @@ import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
+import com.android.server.os.instrumentation.DynamicInstrumentationManagerService;
import com.android.server.pdb.PersistentDataBlockService;
import com.android.server.people.PeopleService;
import com.android.server.permission.access.AccessCheckingService;
@@ -249,6 +249,7 @@ import com.android.server.security.AttestationVerificationManagerService;
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
+import com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService;
import com.android.server.security.advancedprotection.AdvancedProtectionService;
import com.android.server.security.rkp.RemoteProvisioningService;
import com.android.server.selinux.SelinuxAuditLogsService;
@@ -1620,7 +1621,8 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
t.traceEnd();
- if (!isWatch && android.app.supervision.flags.Flags.supervisionApi()) {
+ if (android.app.supervision.flags.Flags.supervisionApi()
+ && (!isWatch || android.app.supervision.flags.Flags.supervisionApiOnWear())) {
t.traceBegin("StartSupervisionService");
mSystemServiceManager.startService(SupervisionService.Lifecycle.class);
t.traceEnd();
@@ -2650,8 +2652,8 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
if (android.adaptiveauth.Flags.enableAdaptiveAuth()) {
- t.traceBegin("StartAdaptiveAuthService");
- mSystemServiceManager.startService(AdaptiveAuthService.class);
+ t.traceBegin("StartAdaptiveAuthenticationService");
+ mSystemServiceManager.startService(AdaptiveAuthenticationService.class);
t.traceEnd();
}
@@ -2890,6 +2892,13 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(TracingServiceProxy.class);
t.traceEnd();
+ // UprobeStats DynamicInstrumentationManager
+ if (com.android.art.flags.Flags.executableMethodFileOffsets()) {
+ t.traceBegin("StartDynamicInstrumentationManager");
+ mSystemServiceManager.startService(DynamicInstrumentationManagerService.class);
+ t.traceEnd();
+ }
+
// It is now time to start up the app processes...
t.traceBegin("MakeLockSettingsServiceReady");
diff --git a/services/proguard.flags b/services/proguard.flags
index cdd41abf6c7c..977bd19a7236 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -82,7 +82,7 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbAlsaJackDetector { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbAlsaMidiDevice { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorController$OnVibrationCompleteListener { *; }
--keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorManagerService$OnSyncedVibrationCompleteListener { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorManagerService$VibratorManagerNativeCallbacks { *; }
-keepclasseswithmembers,allowoptimization,allowaccessmodification class com.android.server.** {
*** *FromNative(...);
}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java b/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
index fead05bc7e49..5df9dd521092 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
@@ -35,6 +35,14 @@ public abstract class SupervisionManagerInternal {
public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);
/**
+ * Set whether supervision is enabled for the specified user.
+ *
+ * @param userId The user to set the supervision state for
+ * @param enabled Whether or not the user should be supervised
+ */
+ public abstract void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled);
+
+ /**
* Sets whether the supervision lock screen should be shown for the specified user
*
* @param userId The user set the superivision state for
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 4c515c173c8d..67e254782f6d 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -19,7 +19,9 @@ package com.android.server.supervision;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
@@ -28,19 +30,19 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.SparseArray;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
import com.android.server.pm.UserManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-/**
- * Service for handling system supervision.
- */
+/** Service for handling system supervision. */
public class SupervisionService extends ISupervisionManager.Stub {
private static final String LOG_TAG = "SupervisionService";
@@ -52,14 +54,25 @@ public class SupervisionService extends ISupervisionManager.Stub {
@GuardedBy("getLockObject()")
private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
+ private final DevicePolicyManagerInternal mDpmInternal;
private final UserManagerInternal mUserManagerInternal;
public SupervisionService(Context context) {
mContext = context.createAttributionContext(LOG_TAG);
+ mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
}
+ void syncStateWithDevicePolicyManager(TargetUser user) {
+ if (user.isPreCreated()) return;
+
+ // Ensure that supervision is enabled when supervision app is the profile owner.
+ if (android.app.admin.flags.Flags.enableSupervisionServiceSync() && isProfileOwner(user)) {
+ setSupervisionEnabledForUser(user.getUserIdentifier(), true);
+ }
+ }
+
@Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
synchronized (getLockObject()) {
@@ -74,14 +87,15 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Nullable FileDescriptor err,
@NonNull String[] args,
@Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ @NonNull ResultReceiver resultReceiver)
+ throws RemoteException {
new SupervisionServiceShellCommand(this)
.exec(this, in, out, err, args, callback, resultReceiver);
}
@Override
- protected void dump(@NonNull FileDescriptor fd,
- @NonNull PrintWriter printWriter, @Nullable String[] args) {
+ protected void dump(
+ @NonNull FileDescriptor fd, @NonNull PrintWriter printWriter, @Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, printWriter)) return;
try (var pw = new IndentingPrintWriter(printWriter, " ")) {
@@ -120,6 +134,17 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
+ /** Returns whether the supervision app has profile owner status. */
+ private boolean isProfileOwner(TargetUser user) {
+ ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(user.getUserIdentifier());
+ if (profileOwner == null) {
+ return false;
+ }
+
+ String configPackage = mContext.getResources().getString(R.string.config_systemSupervision);
+ return profileOwner.getPackageName().equals(configPackage);
+ }
+
public static class Lifecycle extends SystemService {
private final SupervisionService mSupervisionService;
@@ -133,13 +158,24 @@ public class SupervisionService extends ISupervisionManager.Stub {
publishLocalService(SupervisionManagerInternal.class, mSupervisionService.mInternal);
publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
}
+
+ @Override
+ public void onUserStarting(@NonNull TargetUser user) {
+ mSupervisionService.syncStateWithDevicePolicyManager(user);
+ }
}
- final SupervisionManagerInternal mInternal = new SupervisionManagerInternal() {
+ final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
+
+ private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
+ @Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
- synchronized (getLockObject()) {
- return getUserDataLocked(userId).supervisionEnabled;
- }
+ return SupervisionService.this.isSupervisionEnabledForUser(userId);
+ }
+
+ @Override
+ public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+ SupervisionService.this.setSupervisionEnabledForUser(userId, enabled);
}
@Override
@@ -151,7 +187,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
data.supervisionLockScreenOptions = options;
}
}
- };
+ }
private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
@Override
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp b/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp
new file mode 100644
index 000000000000..2c2e5fdb68d9
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_system_performance",
+}
+
+android_test {
+ name: "DynamicInstrumentationManagerServiceTests",
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "hamcrest-library",
+ "platform-test-annotations",
+ "services.core",
+ "testables",
+ "truth",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: [
+ "device-tests",
+ ],
+}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/AndroidManifest.xml b/services/tests/DynamicInstrumentationManagerServiceTests/AndroidManifest.xml
new file mode 100644
index 000000000000..4913d706a72d
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.os.instrumentation" >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.os.instrumentation"
+ android:label="DynamicInstrumentationmanagerService Unit Tests"/>
+</manifest> \ No newline at end of file
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING b/services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING
new file mode 100644
index 000000000000..33defed0eae6
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "DynamicInstrumentationManagerServiceTests"
+ }
+ ]
+}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java
new file mode 100644
index 000000000000..04073fab2059
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+public abstract class TestAbstractClass {
+ abstract void abstractMethod();
+
+ void concreteMethod() {
+ }
+}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java
new file mode 100644
index 000000000000..2c25e7a52f73
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+public class TestAbstractClassImpl extends TestAbstractClass {
+ @Override
+ void abstractMethod() {
+ }
+}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java
new file mode 100644
index 000000000000..085f5953f0e5
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+public class TestClass {
+ void primitiveParams(boolean a, boolean[] b, byte c, byte[] d, char e, char[] f, short g,
+ short[] h, int i, int[] j, long k, long[] l, float m, float[] n, double o, double[] p) {
+ }
+
+ void classParams(String a, String[] b) {
+ }
+
+ private void privateMethod() {
+ }
+
+ /**
+ * docs!
+ */
+ public void publicMethod() {
+ }
+
+ private static class InnerClass {
+ private void innerMethod(InnerClass arg, InnerClass[] argArray) {
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterface.java
index 60cfc4876414..7af4f254ab1c 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-package com.android.server.integrity.serializer;
-
-import android.annotation.NonNull;
+package com.android.server;
/**
- * Thrown when rule serialization fails.
+ * docs!
*/
-public class RuleSerializeException extends Exception {
- public RuleSerializeException(@NonNull String message) {
- super(message);
- }
+public interface TestInterface {
+ /**
+ * docs!
+ */
+ void interfaceMethod();
- public RuleSerializeException(@NonNull String message, @NonNull Throwable cause) {
- super(message, cause);
+ /**
+ * docs!
+ */
+ default void defaultMethod() {
}
}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java
new file mode 100644
index 000000000000..53aecbc08939
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+public class TestInterfaceImpl implements TestInterface {
+ @Override
+ public void interfaceMethod() {
+ }
+}
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
new file mode 100644
index 000000000000..5492ba6b9dd1
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os.instrumentation;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.os.instrumentation.MethodDescriptor;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.TestAbstractClass;
+import com.android.server.TestAbstractClassImpl;
+import com.android.server.TestClass;
+import com.android.server.TestInterface;
+import com.android.server.TestInterfaceImpl;
+
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Test class for
+ * {@link DynamicInstrumentationManagerService#parseMethodDescriptor(ClassLoader,
+ * MethodDescriptor)}.
+ * <p>
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:ParseMethodDescriptorTest
+ */
+@Presubmit
+@SmallTest
+public class ParseMethodDescriptorTest {
+ private static final String[] PRIMITIVE_PARAMS = new String[]{
+ "boolean", "boolean[]", "byte", "byte[]", "char", "char[]", "short", "short[]", "int",
+ "int[]", "long", "long[]", "float", "float[]", "double", "double[]"};
+ private static final String[] CLASS_PARAMS =
+ new String[]{"java.lang.String", "java.lang.String[]"};
+
+ @Test
+ public void primitiveParams() {
+ assertNotNull(parseMethodDescriptor(TestClass.class.getName(), "primitiveParams",
+ PRIMITIVE_PARAMS));
+ }
+
+ @Test
+ public void classParams() {
+ assertNotNull(
+ parseMethodDescriptor(TestClass.class.getName(), "classParams", CLASS_PARAMS));
+ }
+
+ @Test
+ public void publicMethod() {
+ assertNotNull(
+ parseMethodDescriptor(TestClass.class.getName(), "publicMethod"));
+ }
+
+ @Test
+ public void privateMethod() {
+ assertNotNull(
+ parseMethodDescriptor(TestClass.class.getName(), "privateMethod"));
+ }
+
+ @Test
+ public void innerClass() {
+ assertNotNull(
+ parseMethodDescriptor(TestClass.class.getName() + "$InnerClass", "innerMethod",
+ new String[]{TestClass.class.getName() + "$InnerClass",
+ TestClass.class.getName() + "$InnerClass[]"}));
+ }
+
+ @Test
+ public void interface_concreteMethod() {
+ assertNotNull(
+ parseMethodDescriptor(TestInterfaceImpl.class.getName(), "interfaceMethod"));
+ }
+
+ @Test
+ public void interface_defaultMethod() {
+ assertNotNull(
+ parseMethodDescriptor(TestInterface.class.getName(), "defaultMethod"));
+ }
+
+ @Test
+ public void abstractClassImpl_abstractMethod() {
+ assertNotNull(
+ parseMethodDescriptor(TestAbstractClassImpl.class.getName(), "abstractMethod"));
+ }
+
+ @Test
+ public void abstractClass_concreteMethod() {
+ assertNotNull(
+ parseMethodDescriptor(TestAbstractClass.class.getName(), "concreteMethod"));
+ }
+
+ @Test
+ public void notFound_illegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () -> parseMethodDescriptor("foo", "bar"));
+ assertThrows(IllegalArgumentException.class,
+ () -> parseMethodDescriptor(TestClass.class.getName(), "bar"));
+ assertThrows(IllegalArgumentException.class,
+ () -> parseMethodDescriptor(TestClass.class.getName(), "primitiveParams",
+ new String[]{"int"}));
+ }
+
+ private Method parseMethodDescriptor(String fqcn, String methodName) {
+ return DynamicInstrumentationManagerService.parseMethodDescriptor(
+ getClass().getClassLoader(),
+ getMethodDescriptor(fqcn, methodName, new String[]{}));
+ }
+
+ private Method parseMethodDescriptor(String fqcn, String methodName, String[] fqParameters) {
+ return DynamicInstrumentationManagerService.parseMethodDescriptor(
+ getClass().getClassLoader(),
+ getMethodDescriptor(fqcn, methodName, fqParameters));
+ }
+
+ private MethodDescriptor getMethodDescriptor(String fqcn, String methodName,
+ String[] fqParameters) {
+ MethodDescriptor methodDescriptor = new MethodDescriptor();
+ methodDescriptor.fullyQualifiedClassName = fqcn;
+ methodDescriptor.methodName = methodName;
+ methodDescriptor.fullyQualifiedParameters = fqParameters;
+ return methodDescriptor;
+ }
+
+
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index a804f24acc8f..c30ab738b098 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -101,13 +101,13 @@ public final class InputMethodSubtypeSwitchingControllerTest {
TEST_SETTING_ACTIVITY_NAME, subtypes, TEST_IS_DEFAULT_RES_ID,
TEST_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, TEST_IS_VR_IME);
if (subtypes == null) {
- items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
- NOT_A_SUBTYPE_INDEX, null, SYSTEM_LOCALE));
+ items.add(new ImeSubtypeListItem(imeName, null /* subtypeName */, null /* layoutName */,
+ imi, NOT_A_SUBTYPE_INDEX, null, SYSTEM_LOCALE));
} else {
for (int i = 0; i < subtypes.size(); ++i) {
final String subtypeLocale = subtypeLocales.get(i);
- items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
- SYSTEM_LOCALE));
+ items.add(new ImeSubtypeListItem(imeName, subtypeLocale, null /* layoutName */,
+ imi, i, subtypeLocale, SYSTEM_LOCALE));
}
}
}
@@ -138,8 +138,8 @@ public final class InputMethodSubtypeSwitchingControllerTest {
final InputMethodInfo imi = new InputMethodInfo(ri, TEST_IS_AUX_IME,
TEST_SETTING_ACTIVITY_NAME, subtypes, TEST_IS_DEFAULT_RES_ID,
TEST_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */, TEST_IS_VR_IME);
- return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
- SYSTEM_LOCALE);
+ return new ImeSubtypeListItem(imeName, subtypeName, null /* layoutName */, imi,
+ subtypeIndex, subtypeLocale, SYSTEM_LOCALE);
}
@NonNull
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
new file mode 100644
index 000000000000..1be5cef28244
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.Flags.FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeNonSdkSandbox;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.PackageStateInternal;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@AppModeFull
+@AppModeNonSdkSandbox
+@RunWith(AndroidJUnit4.class)
+public class BroadcastHelperTest {
+ private static final String TAG = "BroadcastHelperTest";
+ private static final String PACKAGE_CHANGED_TEST_PACKAGE_NAME = "testpackagename";
+ private static final String PACKAGE_CHANGED_TEST_MAIN_ACTIVITY =
+ PACKAGE_CHANGED_TEST_PACKAGE_NAME + ".MainActivity";
+ private static final String PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED =
+ "android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED";
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Mock
+ ActivityManagerInternal mMockActivityManagerInternal;
+ @Mock
+ AndroidPackageInternal mMockAndroidPackageInternal;
+ @Mock
+ Computer mMockSnapshot;
+ @Mock
+ Handler mMockHandler;
+ @Mock
+ PackageManagerServiceInjector mMockPackageManagerServiceInjector;
+ @Mock
+ PackageMonitorCallbackHelper mMockPackageMonitorCallbackHelper;
+ @Mock
+ PackageStateInternal mMockPackageStateInternal;
+ @Mock
+ ParsedActivity mMockParsedActivity;
+ @Mock
+ UserManagerInternal mMockUserManagerInternal;
+
+ private Context mContext;
+ private BroadcastHelper mBroadcastHelper;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ when(mMockHandler.sendMessageAtTime(any(Message.class), anyLong())).thenAnswer(
+ i -> {
+ ((Message) i.getArguments()[0]).getCallback().run();
+ return true;
+ });
+ when(mMockPackageManagerServiceInjector.getActivityManagerInternal()).thenReturn(
+ mMockActivityManagerInternal);
+ when(mMockPackageManagerServiceInjector.getContext()).thenReturn(mContext);
+ when(mMockPackageManagerServiceInjector.getHandler()).thenReturn(mMockHandler);
+ when(mMockPackageManagerServiceInjector.getPackageMonitorCallbackHelper()).thenReturn(
+ mMockPackageMonitorCallbackHelper);
+ when(mMockPackageManagerServiceInjector.getUserManagerInternal()).thenReturn(
+ mMockUserManagerInternal);
+
+ mBroadcastHelper = new BroadcastHelper(mMockPackageManagerServiceInjector);
+ }
+
+ @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES)
+ @Test
+ public void changeNonExportedComponent_sendPackageChangedBroadcastToSystem_withPermission()
+ throws Exception {
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockActivityManagerInternal).broadcastIntentWithCallback(
+ captor.capture(), eq(null),
+ eq(new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED}),
+ anyInt(), eq(null), eq(null), eq(null));
+ Intent intent = captor.getValue();
+ assertNotNull(intent);
+ assertThat(intent.getPackage()).isEqualTo("android");
+ }
+
+ @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES)
+ @Test
+ public void changeNonExportedComponent_sendPackageChangedBroadcastToApplicationItself()
+ throws Exception {
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
+ eq(null), anyInt(), eq(null), eq(null), eq(null));
+ Intent intent = captor.getValue();
+ assertNotNull(intent);
+ assertThat(intent.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void changeExportedComponent_sendPackageChangedBroadcastToAll() throws Exception {
+ changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */);
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
+ eq(null), anyInt(), eq(null), eq(null), eq(null));
+ Intent intent = captor.getValue();
+ assertNotNull(intent);
+ assertNull(intent.getPackage());
+ }
+
+ private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent) {
+ when(mMockSnapshot.getPackageStateInternal(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
+ anyInt())).thenReturn(mMockPackageStateInternal);
+ when(mMockSnapshot.isInstantAppInternal(any(), anyInt(), anyInt())).thenReturn(false);
+ when(mMockSnapshot.getVisibilityAllowLists(any(), any())).thenReturn(null);
+ when(mMockPackageStateInternal.getPkg()).thenReturn(mMockAndroidPackageInternal);
+
+ when(mMockParsedActivity.getClassName()).thenReturn(
+ PACKAGE_CHANGED_TEST_MAIN_ACTIVITY);
+ when(mMockParsedActivity.isExported()).thenReturn(changeExportedComponent);
+ ArrayList<ParsedActivity> parsedActivities = new ArrayList<>();
+ parsedActivities.add(mMockParsedActivity);
+
+ when(mMockAndroidPackageInternal.getReceivers()).thenReturn(new ArrayList<>());
+ when(mMockAndroidPackageInternal.getProviders()).thenReturn(new ArrayList<>());
+ when(mMockAndroidPackageInternal.getServices()).thenReturn(new ArrayList<>());
+ when(mMockAndroidPackageInternal.getActivities()).thenReturn(parsedActivities);
+
+ doNothing().when(mMockPackageMonitorCallbackHelper).notifyPackageChanged(any(),
+ anyBoolean(), any(), anyInt(), any(), any(), any(), any(), any());
+ when(mMockActivityManagerInternal.broadcastIntentWithCallback(any(), any(), any(), anyInt(),
+ any(), any(), any())).thenReturn(ActivityManager.BROADCAST_SUCCESS);
+
+ ArrayList<String> componentNames = new ArrayList<>();
+ componentNames.add(PACKAGE_CHANGED_TEST_MAIN_ACTIVITY);
+
+ mBroadcastHelper.sendPackageChangedBroadcast(mMockSnapshot,
+ PACKAGE_CHANGED_TEST_PACKAGE_NAME, true /* dontKillApp */, componentNames,
+ UserHandle.USER_SYSTEM, "test" /* reason */);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
index 09d0e4a82f7f..5a59c57ddf28 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
@@ -201,7 +201,8 @@ class PackageInstallerSessionTest {
/* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
/* VerifierController */ mock(VerifierController::class.java),
/* initialVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
- /* currentVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
+ /* currentVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
+ /* installDependencyHelper */ null
)
}
@@ -256,7 +257,8 @@ class PackageInstallerSessionTest {
mTmpDir,
mock(PackageSessionProvider::class.java),
mock(SilentUpdatePolicy::class.java),
- mock(VerifierController::class.java)
+ mock(VerifierController::class.java),
+ mock(InstallDependencyHelper::class.java)
)
ret.add(session)
} catch (e: Exception) {
diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml
index 37a34eeb9724..205ff058275a 100644
--- a/services/tests/displayservicetests/AndroidManifest.xml
+++ b/services/tests/displayservicetests/AndroidManifest.xml
@@ -29,7 +29,6 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.MANAGE_USB" />
- <uses-permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE" />
<!-- Permissions needed for DisplayTransformManagerTest -->
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
index 06f1b27410ff..a8708f9f9e6e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
@@ -223,7 +223,8 @@ public class BrightnessSynchronizerTest {
mIntRangeUserPerceptionEnabled);
mSynchronizer.startSynchronizing();
verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(),
- isA(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ isA(Handler.class), eq(0L),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
mDisplayListener = mDisplayListenerCaptor.getValue();
verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false),
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index b917af4d796e..80e5ee39c13d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -19,13 +19,16 @@ package com.android.server.display;
import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
+import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
import static android.Manifest.permission.MANAGE_DISPLAYS;
+import static android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
@@ -96,6 +99,7 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
+import android.hardware.display.DisplayTopology;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -111,11 +115,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.PermissionEnforcer;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserManager;
+import android.os.test.FakePermissionEnforcer;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -203,11 +209,13 @@ public class DisplayManagerServiceTest {
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
private static final String PACKAGE_NAME = "com.android.frameworks.displayservicetests";
- private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ private static final long STANDARD_DISPLAY_EVENTS =
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
private static final long STANDARD_AND_CONNECTION_DISPLAY_EVENTS =
- STANDARD_DISPLAY_EVENTS | DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED;
+ STANDARD_DISPLAY_EVENTS
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED;
private static final String EVENT_DISPLAY_ADDED = "EVENT_DISPLAY_ADDED";
private static final String EVENT_DISPLAY_REMOVED = "EVENT_DISPLAY_REMOVED";
@@ -249,6 +257,8 @@ public class DisplayManagerServiceTest {
private int[] mAllowedHdrOutputTypes;
+ private final FakePermissionEnforcer mPermissionEnforcer = new FakePermissionEnforcer();
+
private final DisplayManagerService.Injector mShortMockedInjector =
new DisplayManagerService.Injector() {
@Override
@@ -426,6 +436,13 @@ public class DisplayManagerServiceTest {
when(mContext.getResources()).thenReturn(mResources);
mUserManager = Mockito.spy(mContext.getSystemService(UserManager.class));
+ mPermissionEnforcer.grant(CONTROL_DISPLAY_BRIGHTNESS);
+ mPermissionEnforcer.grant(MODIFY_USER_PREFERRED_DISPLAY_MODE);
+ doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName(
+ eq(PermissionEnforcer.class));
+ doReturn(mPermissionEnforcer).when(mContext).getSystemService(
+ eq(Context.PERMISSION_ENFORCER_SERVICE));
+
VirtualDeviceManager vdm = new VirtualDeviceManager(mIVirtualDeviceManager, mContext);
when(mContext.getSystemService(VirtualDeviceManager.class)).thenReturn(vdm);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
@@ -2379,7 +2396,7 @@ public class DisplayManagerServiceTest {
// register display listener callback
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
long allEventsExceptDisplayAdded = STANDARD_DISPLAY_EVENTS
- & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED;
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED;
displayManagerBinderService.registerCallbackWithEventMask(callback,
allEventsExceptDisplayAdded);
@@ -2450,7 +2467,7 @@ public class DisplayManagerServiceTest {
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
long allEventsExceptDisplayRemoved = STANDARD_DISPLAY_EVENTS
- & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
displayManagerBinderService.registerCallbackWithEventMask(callback,
allEventsExceptDisplayRemoved);
@@ -3665,6 +3682,87 @@ public class DisplayManagerServiceTest {
verify(mMockVirtualDisplayAdapter).releaseVirtualDisplayLocked(binder, callingUid);
}
+ @Test
+ public void testGetDisplayTopology() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 1);
+ manageDisplaysPermission(/* granted= */ true);
+ when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ DisplayTopology topology = displayManagerBinderService.getDisplayTopology();
+ assertNotNull(topology);
+ DisplayTopology.TreeNode display = topology.getRoot();
+ assertNotNull(display);
+ assertEquals(Display.DEFAULT_DISPLAY, display.getDisplayId());
+ }
+
+ @Test
+ public void testGetDisplayTopology_NullIfFlagDisabled() {
+ manageDisplaysPermission(/* granted= */ true);
+ when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(false);
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ DisplayTopology topology = displayManagerBinderService.getDisplayTopology();
+ assertNull(topology);
+ }
+
+ @Test
+ public void testGetDisplayTopology_withoutPermission_shouldThrowException() {
+ when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ assertThrows(SecurityException.class, displayManagerBinderService::getDisplayTopology);
+ }
+
+ @Test
+ public void testSetDisplayTopology() {
+ manageDisplaysPermission(/* granted= */ true);
+ when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ displayManagerBinderService.setDisplayTopology(new DisplayTopology());
+ }
+
+ @Test
+ public void testSetDisplayTopology_withoutPermission_shouldThrowException() {
+ when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true);
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ assertThrows(SecurityException.class,
+ () -> displayManagerBinderService.setDisplayTopology(new DisplayTopology()));
+ }
+
private void initDisplayPowerController(DisplayManagerInternal localService) {
localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
@Override
@@ -3848,6 +3946,10 @@ public class DisplayManagerServiceTest {
DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked());
displayDeviceInfo.modeId = modeId;
+ if (modeId > 0 && modeId <= displayDeviceInfo.supportedModes.length) {
+ displayDeviceInfo.renderFrameRate =
+ displayDeviceInfo.supportedModes[modeId - 1].getRefreshRate();
+ }
updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo);
}
@@ -4015,9 +4117,11 @@ public class DisplayManagerServiceTest {
private void manageDisplaysPermission(boolean granted) {
if (granted) {
doNothing().when(mContext).enforceCallingOrSelfPermission(eq(MANAGE_DISPLAYS), any());
+ mPermissionEnforcer.grant(MANAGE_DISPLAYS);
} else {
doThrow(new SecurityException("MANAGE_DISPLAYS permission denied")).when(mContext)
.enforceCallingOrSelfPermission(eq(MANAGE_DISPLAYS), any());
+ mPermissionEnforcer.revoke(MANAGE_DISPLAYS);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 27fd1e6187bb..e64d9855bc54 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -139,6 +139,7 @@ public final class DisplayPowerControllerTest {
private TestLooper mTestLooper;
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
+ private DisplayBrightnessState mDisplayBrightnessState;
private Sensor mProxSensor;
@Mock
@@ -187,6 +188,7 @@ public final class DisplayPowerControllerTest {
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mHandler = new Handler(mTestLooper.getLooper());
+ mDisplayBrightnessState = DisplayBrightnessState.builder().build();
// Set some settings to minimize unexpected events and have a consistent starting state
Settings.System.putInt(mContext.getContentResolver(),
@@ -1453,10 +1455,11 @@ public final class DisplayPowerControllerTest {
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
- when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
- invocation -> DisplayBrightnessState.builder()
- .setIsSlowChange(invocation.getArgument(2))
- .setBrightness(invocation.getArgument(1))
+ when(mHolder.clamperController.clamp(any(), any(), anyFloat(),
+ anyBoolean(), anyInt())).thenAnswer(
+ invocation -> DisplayBrightnessState.Builder.from(mDisplayBrightnessState)
+ .setIsSlowChange(invocation.getArgument(3))
+ .setBrightness(invocation.getArgument(2))
.setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
.setCustomAnimationRate(transitionRate).build());
@@ -1477,10 +1480,11 @@ public final class DisplayPowerControllerTest {
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
- when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
- invocation -> DisplayBrightnessState.builder()
- .setIsSlowChange(invocation.getArgument(2))
- .setBrightness(invocation.getArgument(1))
+ when(mHolder.clamperController.clamp(any(), any(), anyFloat(),
+ anyBoolean(), anyInt())).thenAnswer(
+ invocation -> DisplayBrightnessState.Builder.from(mDisplayBrightnessState)
+ .setIsSlowChange(invocation.getArgument(3))
+ .setBrightness(invocation.getArgument(2))
.setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
.setCustomAnimationRate(transitionRate).build());
@@ -2574,10 +2578,11 @@ public final class DisplayPowerControllerTest {
BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
- when(clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
- invocation -> DisplayBrightnessState.builder()
- .setIsSlowChange(invocation.getArgument(2))
- .setBrightness(invocation.getArgument(1))
+ when(clamperController.clamp(any(), any(), anyFloat(), anyBoolean(),
+ anyInt())).thenAnswer(
+ invocation -> DisplayBrightnessState.Builder.from(mDisplayBrightnessState)
+ .setIsSlowChange(invocation.getArgument(3))
+ .setBrightness(invocation.getArgument(2))
.setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
.setCustomAnimationRate(-1).build());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 85e73561cf59..a2d2a81b20b4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -16,6 +16,7 @@
package com.android.server.display
+import android.hardware.display.DisplayTopology
import android.util.DisplayMetrics
import android.view.Display
import android.view.DisplayInfo
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
deleted file mode 100644
index cd8c26d0d337..000000000000
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display
-
-import android.view.Display
-import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM
-import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP
-import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-class DisplayTopologyTest {
- private val topology = DisplayTopology()
-
- @Test
- fun addOneDisplay() {
- val displayId = 1
- val width = 800f
- val height = 600f
-
- topology.addDisplay(displayId, width, height)
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId)
-
- val display = topology.mRoot!!
- assertThat(display.mDisplayId).isEqualTo(displayId)
- assertThat(display.mWidth).isEqualTo(width)
- assertThat(display.mHeight).isEqualTo(height)
- assertThat(display.mChildren).isEmpty()
- }
-
- @Test
- fun addTwoDisplays() {
- val displayId1 = 1
- val width1 = 800f
- val height1 = 600f
-
- val displayId2 = 2
- val width2 = 1000f
- val height2 = 1500f
-
- topology.addDisplay(displayId1, width1, height1)
- topology.addDisplay(displayId2, width2, height2)
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
-
- val display1 = topology.mRoot!!
- assertThat(display1.mDisplayId).isEqualTo(displayId1)
- assertThat(display1.mWidth).isEqualTo(width1)
- assertThat(display1.mHeight).isEqualTo(height1)
- assertThat(display1.mChildren).hasSize(1)
-
- val display2 = display1.mChildren[0]
- assertThat(display2.mDisplayId).isEqualTo(displayId2)
- assertThat(display2.mWidth).isEqualTo(width2)
- assertThat(display2.mHeight).isEqualTo(height2)
- assertThat(display2.mChildren).isEmpty()
- assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
- assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
- }
-
- @Test
- fun addManyDisplays() {
- val displayId1 = 1
- val width1 = 800f
- val height1 = 600f
-
- val displayId2 = 2
- val width2 = 1000f
- val height2 = 1500f
-
- topology.addDisplay(displayId1, width1, height1)
- topology.addDisplay(displayId2, width2, height2)
-
- val noOfDisplays = 30
- for (i in 3..noOfDisplays) {
- topology.addDisplay(/* displayId= */ i, width1, height1)
- }
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
-
- val display1 = topology.mRoot!!
- assertThat(display1.mDisplayId).isEqualTo(displayId1)
- assertThat(display1.mWidth).isEqualTo(width1)
- assertThat(display1.mHeight).isEqualTo(height1)
- assertThat(display1.mChildren).hasSize(1)
-
- val display2 = display1.mChildren[0]
- assertThat(display2.mDisplayId).isEqualTo(displayId2)
- assertThat(display2.mWidth).isEqualTo(width2)
- assertThat(display2.mHeight).isEqualTo(height2)
- assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
- assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
-
- var display = display2
- for (i in 3..noOfDisplays) {
- display = display.mChildren[0]
- assertThat(display.mDisplayId).isEqualTo(i)
- assertThat(display.mWidth).isEqualTo(width1)
- assertThat(display.mHeight).isEqualTo(height1)
- // The last display should have no children
- assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(display.mOffset).isEqualTo(0)
- }
- }
-
- @Test
- fun removeDisplays() {
- val displayId1 = 1
- val width1 = 800f
- val height1 = 600f
-
- val displayId2 = 2
- val width2 = 1000f
- val height2 = 1500f
-
- topology.addDisplay(displayId1, width1, height1)
- topology.addDisplay(displayId2, width2, height2)
-
- val noOfDisplays = 30
- for (i in 3..noOfDisplays) {
- topology.addDisplay(/* displayId= */ i, width1, height1)
- }
-
- var removedDisplays = arrayOf(20)
- topology.removeDisplay(20)
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
-
- var display1 = topology.mRoot!!
- assertThat(display1.mDisplayId).isEqualTo(displayId1)
- assertThat(display1.mWidth).isEqualTo(width1)
- assertThat(display1.mHeight).isEqualTo(height1)
- assertThat(display1.mChildren).hasSize(1)
-
- var display2 = display1.mChildren[0]
- assertThat(display2.mDisplayId).isEqualTo(displayId2)
- assertThat(display2.mWidth).isEqualTo(width2)
- assertThat(display2.mHeight).isEqualTo(height2)
- assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
- assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
-
- var display = display2
- for (i in 3..noOfDisplays) {
- if (i in removedDisplays) {
- continue
- }
- display = display.mChildren[0]
- assertThat(display.mDisplayId).isEqualTo(i)
- assertThat(display.mWidth).isEqualTo(width1)
- assertThat(display.mHeight).isEqualTo(height1)
- // The last display should have no children
- assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(display.mOffset).isEqualTo(0)
- }
-
- topology.removeDisplay(22)
- removedDisplays += 22
- topology.removeDisplay(23)
- removedDisplays += 23
- topology.removeDisplay(25)
- removedDisplays += 25
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
-
- display1 = topology.mRoot!!
- assertThat(display1.mDisplayId).isEqualTo(displayId1)
- assertThat(display1.mWidth).isEqualTo(width1)
- assertThat(display1.mHeight).isEqualTo(height1)
- assertThat(display1.mChildren).hasSize(1)
-
- display2 = display1.mChildren[0]
- assertThat(display2.mDisplayId).isEqualTo(displayId2)
- assertThat(display2.mWidth).isEqualTo(width2)
- assertThat(display2.mHeight).isEqualTo(height2)
- assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
- assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
-
- display = display2
- for (i in 3..noOfDisplays) {
- if (i in removedDisplays) {
- continue
- }
- display = display.mChildren[0]
- assertThat(display.mDisplayId).isEqualTo(i)
- assertThat(display.mWidth).isEqualTo(width1)
- assertThat(display.mHeight).isEqualTo(height1)
- // The last display should have no children
- assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(display.mOffset).isEqualTo(0)
- }
- }
-
- @Test
- fun removeAllDisplays() {
- val displayId = 1
- val width = 800f
- val height = 600f
-
- topology.addDisplay(displayId, width, height)
- topology.removeDisplay(displayId)
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
- assertThat(topology.mRoot).isNull()
- }
-
- @Test
- fun removeDisplayThatDoesNotExist() {
- val displayId = 1
- val width = 800f
- val height = 600f
-
- topology.addDisplay(displayId, width, height)
- topology.removeDisplay(3)
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId)
-
- val display = topology.mRoot!!
- assertThat(display.mDisplayId).isEqualTo(displayId)
- assertThat(display.mWidth).isEqualTo(width)
- assertThat(display.mHeight).isEqualTo(height)
- assertThat(display.mChildren).isEmpty()
- }
-
- @Test
- fun removePrimaryDisplay() {
- val displayId1 = 1
- val displayId2 = 2
- val width = 800f
- val height = 600f
-
- topology.addDisplay(displayId1, width, height)
- topology.addDisplay(displayId2, width, height)
- topology.mPrimaryDisplayId = displayId2
- topology.removeDisplay(displayId2)
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
- val display = topology.mRoot!!
- assertThat(display.mDisplayId).isEqualTo(displayId1)
- assertThat(display.mWidth).isEqualTo(width)
- assertThat(display.mHeight).isEqualTo(height)
- assertThat(display.mChildren).isEmpty()
- }
-
- @Test
- fun normalization_noOverlaps_leavesTopologyUnchanged() {
- val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
- /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
- topology.mRoot = display1
-
- val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
- /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
- display1.mChildren.add(display2)
-
- val primaryDisplayId = 3
- val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
- /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
- display1.mChildren.add(display3)
- topology.mPrimaryDisplayId = primaryDisplayId
-
- val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
- /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
- display2.mChildren.add(display4)
-
- topology.normalize()
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
-
- val actualDisplay1 = topology.mRoot!!
- assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
- assertThat(actualDisplay1.mWidth).isEqualTo(200f)
- assertThat(actualDisplay1.mHeight).isEqualTo(600f)
- assertThat(actualDisplay1.mChildren).hasSize(2)
-
- val actualDisplay2 = actualDisplay1.mChildren[0]
- assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
- assertThat(actualDisplay2.mWidth).isEqualTo(600f)
- assertThat(actualDisplay2.mHeight).isEqualTo(200f)
- assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay2.mOffset).isEqualTo(0f)
- assertThat(actualDisplay2.mChildren).hasSize(1)
-
- val actualDisplay3 = actualDisplay1.mChildren[1]
- assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
- assertThat(actualDisplay3.mWidth).isEqualTo(600f)
- assertThat(actualDisplay3.mHeight).isEqualTo(200f)
- assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay3.mOffset).isEqualTo(400f)
- assertThat(actualDisplay3.mChildren).isEmpty()
-
- val actualDisplay4 = actualDisplay2.mChildren[0]
- assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
- assertThat(actualDisplay4.mWidth).isEqualTo(200f)
- assertThat(actualDisplay4.mHeight).isEqualTo(600f)
- assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay4.mOffset).isEqualTo(0f)
- assertThat(actualDisplay4.mChildren).isEmpty()
- }
-
- @Test
- fun normalization_moveDisplayWithoutReparenting() {
- val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
- /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
- topology.mRoot = display1
-
- val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
- /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
- display1.mChildren.add(display2)
-
- val primaryDisplayId = 3
- val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
- /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
- display1.mChildren.add(display3)
- topology.mPrimaryDisplayId = primaryDisplayId
-
- val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
- /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
- display2.mChildren.add(display4)
-
- // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent.
- topology.normalize()
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
-
- val actualDisplay1 = topology.mRoot!!
- assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
- assertThat(actualDisplay1.mWidth).isEqualTo(200f)
- assertThat(actualDisplay1.mHeight).isEqualTo(600f)
- assertThat(actualDisplay1.mChildren).hasSize(1)
-
- val actualDisplay2 = actualDisplay1.mChildren[0]
- assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
- assertThat(actualDisplay2.mWidth).isEqualTo(200f)
- assertThat(actualDisplay2.mHeight).isEqualTo(600f)
- assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay2.mOffset).isEqualTo(0f)
- assertThat(actualDisplay2.mChildren).hasSize(2)
-
- val actualDisplay3 = actualDisplay2.mChildren[1]
- assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
- assertThat(actualDisplay3.mWidth).isEqualTo(600f)
- assertThat(actualDisplay3.mHeight).isEqualTo(200f)
- assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay3.mOffset).isEqualTo(10f)
- assertThat(actualDisplay3.mChildren).isEmpty()
-
- val actualDisplay4 = actualDisplay2.mChildren[0]
- assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
- assertThat(actualDisplay4.mWidth).isEqualTo(200f)
- assertThat(actualDisplay4.mHeight).isEqualTo(600f)
- assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay4.mOffset).isEqualTo(210f)
- assertThat(actualDisplay4.mChildren).isEmpty()
- }
-
- @Test
- fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() {
- val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
- /* height= */ 50f, /* position= */ null, /* offset= */ 0f)
- topology.mRoot = display1
-
- val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
- /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
- display1.mChildren.add(display2)
-
- val primaryDisplayId = 3
- val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
- /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
- display1.mChildren.add(display3)
- topology.mPrimaryDisplayId = primaryDisplayId
-
- // Display 3 gets moved and its left side is still on the same line as the right side
- // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2
- // becomes its new parent.
- topology.normalize()
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
-
- val actualDisplay1 = topology.mRoot!!
- assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
- assertThat(actualDisplay1.mWidth).isEqualTo(200f)
- assertThat(actualDisplay1.mHeight).isEqualTo(50f)
- assertThat(actualDisplay1.mChildren).hasSize(1)
-
- val actualDisplay2 = actualDisplay1.mChildren[0]
- assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
- assertThat(actualDisplay2.mWidth).isEqualTo(600f)
- assertThat(actualDisplay2.mHeight).isEqualTo(200f)
- assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay2.mOffset).isEqualTo(0f)
- assertThat(actualDisplay2.mChildren).hasSize(1)
-
- val actualDisplay3 = actualDisplay2.mChildren[0]
- assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
- assertThat(actualDisplay3.mWidth).isEqualTo(600f)
- assertThat(actualDisplay3.mHeight).isEqualTo(200f)
- assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_BOTTOM)
- assertThat(actualDisplay3.mOffset).isEqualTo(0f)
- assertThat(actualDisplay3.mChildren).isEmpty()
- }
-
- @Test
- fun normalization_moveAndReparentDisplay() {
- val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
- /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
- topology.mRoot = display1
-
- val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
- /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
- display1.mChildren.add(display2)
-
- val primaryDisplayId = 3
- val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
- /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
- display1.mChildren.add(display3)
- topology.mPrimaryDisplayId = primaryDisplayId
-
- val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
- /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
- display2.mChildren.add(display4)
-
- topology.normalize()
-
- assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
-
- val actualDisplay1 = topology.mRoot!!
- assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
- assertThat(actualDisplay1.mWidth).isEqualTo(200f)
- assertThat(actualDisplay1.mHeight).isEqualTo(600f)
- assertThat(actualDisplay1.mChildren).hasSize(1)
-
- val actualDisplay2 = actualDisplay1.mChildren[0]
- assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
- assertThat(actualDisplay2.mWidth).isEqualTo(200f)
- assertThat(actualDisplay2.mHeight).isEqualTo(600f)
- assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay2.mOffset).isEqualTo(0f)
- assertThat(actualDisplay2.mChildren).hasSize(1)
-
- val actualDisplay3 = actualDisplay2.mChildren[0]
- assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
- assertThat(actualDisplay3.mWidth).isEqualTo(600f)
- assertThat(actualDisplay3.mHeight).isEqualTo(200f)
- assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay3.mOffset).isEqualTo(400f)
- assertThat(actualDisplay3.mChildren).hasSize(1)
-
- val actualDisplay4 = actualDisplay3.mChildren[0]
- assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
- assertThat(actualDisplay4.mWidth).isEqualTo(200f)
- assertThat(actualDisplay4.mHeight).isEqualTo(600f)
- assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
- assertThat(actualDisplay4.mOffset).isEqualTo(-400f)
- assertThat(actualDisplay4.mChildren).isEmpty()
- }
-} \ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 3ac7fb0dbe53..e0b0fec380dd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -335,6 +335,10 @@ public class VirtualDisplayAdapterTest {
@Override
public void onStopped() {
}
+
+ @Override
+ public void onRequestedBrightnessChanged(float brightness) {
+ }
};
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index da79f301ee3c..2aafdfa8a4d3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -98,6 +98,7 @@ public class BrightnessClamperControllerTest {
@Mock
private DeviceConfig.Properties mMockProperties;
private BrightnessClamperController mClamperController;
+ private DisplayBrightnessState mDisplayBrightnessState;
private TestInjector mTestInjector;
@Before
@@ -109,6 +110,7 @@ public class BrightnessClamperControllerTest {
when(mMockDisplayDeviceData.getAmbientLightSensor()).thenReturn(mMockSensorData);
mClamperController = createBrightnessClamperController();
+ mDisplayBrightnessState = DisplayBrightnessState.builder().build();
}
@Test
@@ -192,7 +194,8 @@ public class BrightnessClamperControllerTest {
public void testClamp_AppliesModifier() {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
- mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, initialBrightness,
+ initialSlowChange, STATE_ON);
verify(mMockModifier).apply(eq(mMockRequest), any());
verify(mMockDisplayListenerModifier).apply(eq(mMockRequest), any());
@@ -204,7 +207,8 @@ public class BrightnessClamperControllerTest {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
- mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, initialBrightness,
+ initialSlowChange, STATE_ON);
verify(mMockLightSensorController).restart();
}
@@ -214,7 +218,8 @@ public class BrightnessClamperControllerTest {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
clearInvocations(mMockLightSensorController);
- mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_OFF);
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, initialBrightness,
+ initialSlowChange, STATE_OFF);
verify(mMockLightSensorController).stop();
}
@@ -232,8 +237,8 @@ public class BrightnessClamperControllerTest {
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
- DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange, STATE_ON);
+ DisplayBrightnessState state = mClamperController.clamp(mDisplayBrightnessState,
+ mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(PowerManager.BRIGHTNESS_MAX, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -256,8 +261,8 @@ public class BrightnessClamperControllerTest {
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
- DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange, STATE_ON);
+ DisplayBrightnessState state = mClamperController.clamp(mDisplayBrightnessState,
+ mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -280,8 +285,8 @@ public class BrightnessClamperControllerTest {
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
- DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange, STATE_ON);
+ DisplayBrightnessState state = mClamperController.clamp(mDisplayBrightnessState,
+ mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -304,11 +309,11 @@ public class BrightnessClamperControllerTest {
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
// first call of clamp method
- mClamperController.clamp(mMockRequest, initialBrightness,
+ mClamperController.clamp(mDisplayBrightnessState, mMockRequest, initialBrightness,
initialSlowChange, STATE_ON);
// immediately second call of clamp method
- DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange, STATE_ON);
+ DisplayBrightnessState state = mClamperController.clamp(mDisplayBrightnessState,
+ mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -319,6 +324,22 @@ public class BrightnessClamperControllerTest {
}
@Test
+ public void testClamp_activeClamperApplied_confirmBrightnessOverrideStateReturned() {
+ float initialBrightness = 0.8f;
+ boolean initialSlowChange = false;
+ mTestInjector.mCapturedChangeListener.onChanged();
+ mTestHandler.flush();
+
+ mDisplayBrightnessState = DisplayBrightnessState.builder().setBrightnessReason(
+ BrightnessReason.REASON_OVERRIDE).build();
+
+ DisplayBrightnessState state = mClamperController.clamp(mDisplayBrightnessState,
+ mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
+
+ assertEquals(BrightnessReason.REASON_OVERRIDE, state.getBrightnessReason().getReason());
+ }
+
+ @Test
public void testAmbientLuxChanges() {
mTestInjector.mCapturedLightSensorListener.onAmbientLuxChange(50);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
index 3c77ec925078..3aef6aa2ee3f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
@@ -97,11 +97,14 @@ class BrightnessObserverTest {
private fun setUpLowBrightnessZone() {
whenever(mockInjector.getBrightnessInfo(Display.DEFAULT_DISPLAY)).thenReturn(
- BrightnessInfo(/* brightness = */ 0.05f, /* adjustedBrightness = */ 0.05f,
- /* brightnessMinimum = */ 0.0f, /* brightnessMaximum = */ 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
- /* highBrightnessTransitionPoint = */ 1.0f,
- BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE))
+ BrightnessInfo(/* brightness = */ 0.05f, /* adjustedBrightness = */ 0.05f,
+ /* brightnessMinimum = */ 0.0f, /* brightnessMaximum = */ 1.0f,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ /* highBrightnessTransitionPoint = */ 1.0f,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
+ false /* isBrightnessOverrideByWindow */
+ )
+ )
whenever(mockDeviceConfig.highDisplayBrightnessThresholds).thenReturn(floatArrayOf())
whenever(mockDeviceConfig.highAmbientBrightnessThresholds).thenReturn(floatArrayOf())
whenever(mockDeviceConfig.lowDisplayBrightnessThresholds).thenReturn(floatArrayOf(0.1f))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 58f0ab4411bc..4e0bab8bf4bd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -1225,8 +1225,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
setBrightness(10, 10, displayListener);
@@ -1256,8 +1256,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
setBrightness(10, 10, displayListener);
@@ -1291,8 +1291,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
setBrightness(10, 10, displayListener);
@@ -1325,8 +1325,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
ArgumentCaptor<SensorEventListener> sensorListenerCaptor =
@@ -1404,8 +1404,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
ArgumentCaptor<SensorEventListener> sensorListenerCaptor =
@@ -1464,8 +1464,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
ArgumentCaptor<SensorEventListener> listenerCaptor =
@@ -1630,8 +1630,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
// Get the sensor listener so that we can give it new light sensor events
@@ -1730,8 +1730,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener displayListener = displayListenerCaptor.getValue();
// Get the sensor listener so that we can give it new light sensor events
@@ -2877,8 +2877,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<DisplayListener> captor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener listener = captor.getValue();
// Specify Limitation
@@ -3000,8 +3000,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<DisplayListener> captor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener listener = captor.getValue();
final int initialRefreshRate = 60;
@@ -3075,8 +3075,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<DisplayListener> captor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener listener = captor.getValue();
// Specify Limitation for different display
@@ -3115,8 +3115,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<DisplayListener> captor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener listener = captor.getValue();
// Specify Limitation
@@ -3200,8 +3200,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener listener = captor.getValue();
// Specify Sunlight limitations
@@ -3239,8 +3239,8 @@ public class DisplayModeDirectorTest {
ArgumentCaptor<DisplayListener> captor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
- eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED),
+ eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS));
DisplayListener listener = captor.getValue();
// Specify Limitation for different display
@@ -3786,8 +3786,9 @@ public class DisplayModeDirectorTest {
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
- BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT,
- BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE));
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
+ false /* isBrightnessOverrideByWindow */));
listener.onDisplayChanged(DISPLAY_ID);
}
@@ -3897,7 +3898,12 @@ public class DisplayModeDirectorTest {
public void registerDisplayListener(DisplayListener listener, Handler handler) {}
@Override
- public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
+ public void registerDisplayListener(DisplayListener listener, Handler handler,
+ long flags) {}
+
+ @Override
+ public void registerDisplayListener(DisplayListener listener, Handler handler, long flag,
+ long privateFlag) {}
@Override
public Display getDisplay(int displayId) {
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index c81d6be43223..95acd75f32cc 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
java_defaults {
- name: "FrameworkMockingServicesTests-jni-defaults",
+ name: "FrameworksMockingServicesTests-jni-defaults",
jni_libs: [
"libmockingservicestestjni",
],
@@ -30,7 +30,7 @@ package {
android_test {
name: "FrameworksMockingServicesTests",
defaults: [
- "FrameworkMockingServicesTests-jni-defaults",
+ "FrameworksMockingServicesTests-jni-defaults",
"modules-utils-testable-device-config-defaults",
],
@@ -77,7 +77,10 @@ android_test {
"flag-junit",
"am_flags_lib",
"device_policy_aconfig_flags_lib",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ "true": ["service-crashrecovery.impl"],
+ default: [],
+ }),
libs: [
"android.test.mock.stubs.system",
@@ -106,6 +109,10 @@ android_test {
optimize: {
enabled: false,
},
+
+ data: [
+ ":HelloWorldUsingSdk1And2",
+ ],
}
java_library {
@@ -122,22 +129,6 @@ java_library {
],
}
-android_ravenwood_test {
- name: "FrameworksMockingServicesTestsRavenwood",
- libs: [
- "android.test.mock.stubs.system",
- ],
- static_libs: [
- "androidx.annotation_annotation",
- "androidx.test.rules",
- "services.core",
- ],
- srcs: [
- "src/com/android/server/am/BroadcastRecordTest.java",
- ],
- auto_gen_config: true,
-}
-
test_module_config {
name: "FrameworksMockingServicesTests_blob",
base: "FrameworksMockingServicesTests",
diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml
index 7782d570856f..2b90119145bd 100644
--- a/services/tests/mockingservicestests/AndroidTest.xml
+++ b/services/tests/mockingservicestests/AndroidTest.xml
@@ -23,6 +23,12 @@
<option name="test-file-name" value="FrameworksMockingServicesTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true"/>
+ <option name="push-file" key="HelloWorldUsingSdk1And2.apk"
+ value="/data/local/tmp/tests/smockingservicestest/pm/HelloWorldUsingSdk1And2.apk"/>
+ </target_preparer>
+
<option name="test-tag" value="FrameworksMockingServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java
new file mode 100644
index 000000000000..5e2f80bf8311
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.hardware.health.HealthInfo;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.R;
+import com.android.internal.app.IBatteryStats;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.flags.Flags;
+import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryServiceTest {
+
+ private static final int CURRENT_BATTERY_VOLTAGE = 3000;
+ private static final int VOLTAGE_LESS_THEN_ONE_PERCENT = 3029;
+ private static final int VOLTAGE_MORE_THEN_ONE_PERCENT = 3030;
+ private static final int CURRENT_BATTERY_TEMP = 300;
+ private static final int TEMP_LESS_THEN_ONE_DEGREE_CELSIUS = 305;
+ private static final int TEMP_MORE_THEN_ONE_DEGREE_CELSIUS = 310;
+ private static final int CURRENT_BATTERY_HEALTH = 2;
+ private static final int UPDATED_BATTERY_HEALTH = 3;
+ private static final int CURRENT_CHARGE_COUNTER = 4680000;
+ private static final int UPDATED_CHARGE_COUNTER = 4218000;
+ private static final int HANDLER_IDLE_TIME_MS = 5000;
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .mockStatic(SystemProperties.class)
+ .mockStatic(ActivityManager.class)
+ .mockStatic(BatteryStatsService.class)
+ .build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Mock
+ private Context mContextMock;
+ @Mock
+ private LightsManager mLightsManagerMock;
+ @Mock
+ private ActivityManagerInternal mActivityManagerInternalMock;
+ @Mock
+ private IBatteryStats mIBatteryStatsMock;
+
+ private BatteryService mBatteryService;
+ private String mSystemUiPackage;
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mSystemUiPackage = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getResources().getString(R.string.config_systemUi);
+
+ when(mLightsManagerMock.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
+ when(mActivityManagerInternalMock.isSystemReady()).thenReturn(true);
+ when(mContextMock.getResources()).thenReturn(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getResources());
+ ExtendedMockito.when(BatteryStatsService.getService()).thenReturn(mIBatteryStatsMock);
+
+ doNothing().when(mIBatteryStatsMock).setBatteryState(anyInt(), anyInt(), anyInt(), anyInt(),
+ anyInt(), anyInt(), anyInt(), anyInt(), anyLong());
+ doNothing().when(() -> SystemProperties.set(anyString(), anyString()));
+ doNothing().when(() -> ActivityManager.broadcastStickyIntent(any(),
+ eq(new String[]{mSystemUiPackage}), eq(AppOpsManager.OP_NONE),
+ eq(BatteryService.BATTERY_CHANGED_OPTIONS), eq(UserHandle.USER_ALL)));
+
+ addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+ addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+
+ createBatteryService();
+ }
+
+ @Test
+ public void createBatteryService_withNullLooper_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new BatteryService(mContextMock));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyVoltageUpdated_lessThenOnePercent_broadcastNotSent() {
+ mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(0);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyVoltageUpdated_beforeTwentySeconds_broadcastNotSent() {
+ mBatteryService.update(
+ createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(0);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyVoltageUpdated_broadcastSent() {
+ mBatteryService.mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime() - 20000;
+ mBatteryService.update(createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(1);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyTempUpdated_lessThenOneDegreeCelsius_broadcastNotSent() {
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS,
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(0);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void tempUpdated_broadcastSent() {
+ long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime;
+ mBatteryService.update(
+ createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, TEMP_MORE_THEN_ONE_DEGREE_CELSIUS,
+ UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime);
+ verifyNumberOfTimesBroadcastSent(1);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void batteryHealthUpdated_voltageAndTempConst_broadcastSent() {
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER,
+ UPDATED_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(1);
+
+ // updating counter just after the health update does not triggers broadcast.
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ UPDATED_CHARGE_COUNTER,
+ UPDATED_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(1);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void voltageUpdated_lessThanOnePercent_flagDisabled_broadcastSent() {
+ mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(1);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyChargeCounterUpdated_broadcastNotSent() {
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ UPDATED_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(0);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void chargeCounterUpdated_tempUpdatedLessThanOneDegree_broadcastNotSent() {
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS,
+ UPDATED_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(0);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST)
+ public void onlyChargeCounterUpdated_broadcastSent() {
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ UPDATED_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+
+ verifyNumberOfTimesBroadcastSent(1);
+ }
+
+ private HealthInfo createHealthInfo(
+ int batteryVoltage,
+ int batteryTemperature,
+ int batteryChargeCounter,
+ int batteryHealth) {
+ HealthInfo h = new HealthInfo();
+ h.batteryVoltageMillivolts = batteryVoltage;
+ h.batteryTemperatureTenthsCelsius = batteryTemperature;
+ h.batteryChargeCounterUah = batteryChargeCounter;
+ h.batteryStatus = 5;
+ h.batteryHealth = batteryHealth;
+ h.batteryPresent = true;
+ h.batteryLevel = 100;
+ h.maxChargingCurrentMicroamps = 298125;
+ h.batteryCurrentAverageMicroamps = -2812;
+ h.batteryCurrentMicroamps = 298125;
+ h.maxChargingVoltageMicrovolts = 3000;
+ h.batteryCycleCount = 50;
+ h.chargingState = 4;
+ h.batteryCapacityLevel = 100;
+ return h;
+ }
+
+ // Creates a new battery service objects and sets the initial values.
+ private void createBatteryService() throws InterruptedException {
+ final HandlerThread handlerThread = new HandlerThread("BatteryServiceTest");
+ handlerThread.start();
+
+ mBatteryService = new BatteryService(mContextMock, handlerThread.getLooper());
+
+ // trigger the update to set the initial values.
+ mBatteryService.update(
+ createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP,
+ CURRENT_CHARGE_COUNTER,
+ CURRENT_BATTERY_HEALTH));
+
+ waitForHandlerToExecute();
+ }
+
+ private void waitForHandlerToExecute() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mBatteryService.getHandlerForTest().post(latch::countDown);
+ boolean isExecutionComplete = false;
+
+ try {
+ isExecutionComplete = latch.await(HANDLER_IDLE_TIME_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("Handler interrupted before executing the message " + e);
+ }
+
+ assertTrue("Timed out while waiting for Handler to execute.", isExecutionComplete);
+ }
+
+ private void verifyNumberOfTimesBroadcastSent(int numberOfTimes) {
+ // Increase the numberOfTimes by 1 as one broadcast was sent initially during the test
+ // setUp.
+ verify(() -> ActivityManager.broadcastStickyIntent(any(),
+ eq(new String[]{mSystemUiPackage}), eq(AppOpsManager.OP_NONE),
+ eq(BatteryService.BATTERY_CHANGED_OPTIONS), eq(UserHandle.USER_ALL)),
+ times(++numberOfTimes));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index cbc8538cf9fb..37d1c30f76f5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -15,7 +15,12 @@
*/
package com.android.server;
+import static android.os.PowerExemptionManager.REASON_OTHER;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
+
import static androidx.test.InstrumentationRegistry.getContext;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -31,6 +36,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
+import static com.android.server.DeviceIdleController.MSG_TEMP_APP_WHITELIST_TIMEOUT;
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
import static com.android.server.DeviceIdleController.STATE_IDLE;
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -41,6 +47,7 @@ import static com.android.server.DeviceIdleController.STATE_QUICK_DOZE_DELAY;
import static com.android.server.DeviceIdleController.STATE_SENSING;
import static com.android.server.DeviceIdleController.lightStateToString;
import static com.android.server.DeviceIdleController.stateToString;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -83,6 +90,8 @@ import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.SystemClock;
import android.os.WearModeManagerInternal;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -90,12 +99,16 @@ import android.telephony.emergency.EmergencyNumber;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.am.BatteryStatsService;
import com.android.server.deviceidle.ConstraintController;
+import com.android.server.deviceidle.Flags;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -115,6 +128,9 @@ import java.util.concurrent.Executor;
@SuppressWarnings("GuardedBy")
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT);
+
private DeviceIdleController mDeviceIdleController;
private DeviceIdleController.MyHandler mHandler;
private AnyMotionDetectorForTest mAnyMotionDetector;
@@ -157,7 +173,8 @@ public class DeviceIdleControllerTest {
LocationManager locationManager;
ConstraintController constraintController;
// Freeze time for testing.
- long nowElapsed;
+ volatile long nowElapsed;
+ volatile long nowUptime;
boolean useMotionSensor = true;
boolean isLocationPrefetchEnabled = true;
@@ -193,6 +210,11 @@ public class DeviceIdleControllerTest {
}
@Override
+ long getUptimeMillis() {
+ return nowUptime;
+ }
+
+ @Override
LocationManager getLocationManager() {
return locationManager;
}
@@ -314,11 +336,13 @@ public class DeviceIdleControllerTest {
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
+ .mockStatic(BatteryStatsService.class)
.spyStatic(DeviceConfig.class)
.spyStatic(LocalServices.class)
.startMocking();
spyOn(getContext());
doReturn(null).when(getContext()).registerReceiver(any(), any());
+ doReturn(mock(IBatteryStats.class)).when(() -> BatteryStatsService.getService());
doReturn(mock(ActivityManagerInternal.class))
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(ActivityTaskManagerInternal.class))
@@ -401,6 +425,46 @@ public class DeviceIdleControllerTest {
}
@Test
+ @EnableFlags(Flags.FLAG_USE_CPU_TIME_FOR_TEMP_ALLOWLIST)
+ public void testTempAllowlistCountsUptime() {
+ doNothing().when(getContext()).sendBroadcastAsUser(any(), any(), any(), any());
+ final int testUid = 12345;
+ final long durationMs = 4300;
+ final long startTime = 100; // Arbitrary starting point in time.
+ mInjector.nowUptime = mInjector.nowElapsed = startTime;
+
+ mDeviceIdleController.addPowerSaveTempWhitelistAppDirectInternal(0, testUid, durationMs,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, true, REASON_OTHER, "test");
+
+ assertEquals(startTime + durationMs,
+ mDeviceIdleController.mTempWhitelistAppIdEndTimes.get(testUid).first.value);
+
+ final InOrder inorder = inOrder(mHandler);
+ // mHandler is already stubbed to do nothing on handleMessage.
+ inorder.verify(mHandler).sendMessageDelayed(
+ argThat(m -> m.what == MSG_TEMP_APP_WHITELIST_TIMEOUT && m.arg1 == testUid),
+ eq(durationMs));
+
+ mInjector.nowElapsed += durationMs;
+ mInjector.nowUptime += 2;
+ // Elapsed time moved past the expiration but not uptime. The check should be rescheduled.
+ mDeviceIdleController.checkTempAppWhitelistTimeout(testUid);
+ inorder.verify(mHandler).sendMessageDelayed(
+ argThat(m -> m.what == MSG_TEMP_APP_WHITELIST_TIMEOUT && m.arg1 == testUid),
+ eq(durationMs - 2));
+ assertEquals(startTime + durationMs,
+ mDeviceIdleController.mTempWhitelistAppIdEndTimes.get(testUid).first.value);
+
+ mInjector.nowUptime += durationMs;
+ // Uptime moved past the expiration time. Uid should be removed from the temp allowlist.
+ mDeviceIdleController.checkTempAppWhitelistTimeout(testUid);
+ inorder.verify(mHandler, never()).sendMessageDelayed(
+ argThat(m -> m.what == MSG_TEMP_APP_WHITELIST_TIMEOUT && m.arg1 == testUid),
+ anyLong());
+ assertFalse(mDeviceIdleController.mTempWhitelistAppIdEndTimes.contains(testUid));
+ }
+
+ @Test
public void testUpdateInteractivityLocked() {
doReturn(false).when(mPowerManager).isInteractive();
mDeviceIdleController.updateInteractivityLocked();
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index dc5cb8d6bdf5..0c9f70ca96b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -1,6 +1,6 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
-per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
+per-file *DeviceIdleController* = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
per-file SensitiveContentProtectionManagerService* = file:/core/java/android/permission/OWNERS
per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS
per-file *Storage* = file:/core/java/android/os/storage/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index f2acbc31b008..f40d8038da3b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -45,13 +45,11 @@ import android.crashrecovery.flags.Flags;
import android.os.RecoverySystem;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.util.ArraySet;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
@@ -74,11 +72,9 @@ import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -250,37 +246,6 @@ public class RescuePartyTest {
}
@Test
- @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
- Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
- public void testBootLoop() {
- // this is old test where the flag needs to be disabled
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
- HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
-
- noteBoot(1);
-
- // Record DeviceConfig accesses
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
-
- final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
-
- noteBoot(2);
- noteBoot(3);
-
- noteBoot(4);
- assertTrue(RescueParty.isRebootPropertySet());
-
- setCrashRecoveryPropAttemptingReboot(false);
- noteBoot(5);
- assertTrue(RescueParty.isFactoryResetPropertySet());
- }
- @Test
@EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testBootLoopNoFlags() {
// this is old test where the flag needs to be disabled
@@ -293,61 +258,6 @@ public class RescuePartyTest {
}
@Test
- @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testBootLoopRecoverability() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
- HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
-
- // Record DeviceConfig accesses
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
-
- final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
-
-
- noteBoot(1);
-
- noteBoot(2);
- assertTrue(RescueParty.isRebootPropertySet());
-
- noteBoot(3);
-
- verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
-
- noteBoot(4);
- verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_CHANGES);
-
- noteBoot(5);
- verifyOnlySettingsReset(Settings.RESET_MODE_TRUSTED_DEFAULTS);
-
- setCrashRecoveryPropAttemptingReboot(false);
- noteBoot(6);
- assertTrue(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
- @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
- Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
- public void testPersistentAppCrash() {
- // this is old test where the flag needs to be disabled
- noteAppCrash(1, true);
- noteAppCrash(2, true);
- noteAppCrash(3, true);
-
- noteAppCrash(4, true);
- assertTrue(RescueParty.isRebootPropertySet());
-
- setCrashRecoveryPropAttemptingReboot(false);
- noteAppCrash(5, true);
- assertTrue(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
@EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testPersistentAppCrashNoFlags() {
// this is old test where the flag needs to be disabled
@@ -360,98 +270,6 @@ public class RescuePartyTest {
}
@Test
- @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testPersistentAppCrashRecoverability() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
- HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
-
- // Record DeviceConfig accesses
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(PERSISTENT_PACKAGE, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
-
- final String[] expectedResetNamespaces = new String[]{NAMESPACE1};
- final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
-
- noteAppCrash(1, true);
-
- noteAppCrash(2, true);
-
- noteAppCrash(3, true);
- assertTrue(RescueParty.isRebootPropertySet());
-
- noteAppCrash(4, true);
- verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
-
- noteAppCrash(5, true);
- verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_CHANGES);
-
- noteAppCrash(6, true);
- verifyOnlySettingsReset(Settings.RESET_MODE_TRUSTED_DEFAULTS);
-
- setCrashRecoveryPropAttemptingReboot(false);
- noteAppCrash(7, true);
- assertTrue(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
- @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
- Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
- public void testNonPersistentApp() {
- // this is old test where the flag needs to be disabled
- noteAppCrash(1, false);
- noteAppCrash(2, false);
- noteAppCrash(3, false);
- assertFalse(RescueParty.isRebootPropertySet());
-
- noteAppCrash(5, false);
- assertFalse(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
- @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testNonPersistentAppOnlyPerformsFlagResetsRecoverabilityDetection() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
- HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
-
- // Record DeviceConfig accesses
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(NON_PERSISTENT_PACKAGE, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
-
- final String[] expectedResetNamespaces = new String[]{NAMESPACE1};
- final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
-
- noteAppCrash(1, false);
-
- noteAppCrash(2, false);
-
- noteAppCrash(3, false);
- assertFalse(RescueParty.isRebootPropertySet());
-
- noteAppCrash(4, false);
- verifyNoSettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
- noteAppCrash(5, false);
- verifyNoSettingsReset(Settings.RESET_MODE_UNTRUSTED_CHANGES);
- noteAppCrash(6, false);
- verifyNoSettingsReset(Settings.RESET_MODE_TRUSTED_DEFAULTS);
-
- setCrashRecoveryPropAttemptingReboot(false);
- noteAppCrash(7, false);
- assertFalse(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
public void testIsRecoveryTriggeredReboot() {
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot(i + 1);
@@ -522,6 +340,7 @@ public class RescuePartyTest {
@Test
public void testNotThrottlingAfterTimeoutOnAppCrash() {
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(
@@ -534,30 +353,15 @@ public class RescuePartyTest {
}
@Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testNativeRescuePartyResets() {
- doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());
- doReturn(FAKE_RESET_NATIVE_NAMESPACES).when(
- () -> SettingsToPropertiesMapper.getResetNativeCategories());
-
- RescueParty.onSettingsProviderPublished(mMockContext);
-
- verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
- FAKE_NATIVE_NAMESPACE1));
- verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
- FAKE_NATIVE_NAMESPACE2));
- }
-
- @Test
public void testExplicitlyEnablingAndDisablingRescue() {
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
- assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
- assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1));
+ assertTrue(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1));
}
@Test
@@ -565,8 +369,8 @@ public class RescuePartyTest {
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
- assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
// Restore the property value initialized in SetUp()
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
@@ -587,75 +391,6 @@ public class RescuePartyTest {
}
@Test
- @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
- Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
- public void testHealthCheckLevels() {
- // this is old test where the flag needs to be disabled
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-
- // Ensure that no action is taken for cases where the failure reason is unknown
- assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
-
- // Ensure the correct user impact is returned for each mitigation count.
- assertEquals(observer.onHealthCheckFailed(null,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
-
- assertEquals(observer.onHealthCheckFailed(null,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
-
- assertEquals(observer.onHealthCheckFailed(null,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
-
- assertEquals(observer.onHealthCheckFailed(null,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
- public void testHealthCheckLevelsRecoverabilityDetection() {
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-
- // Ensure that no action is taken for cases where the failure reason is unknown
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_UNKNOWN, 1),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
-
- // Ensure the correct user impact is returned for each mitigation count.
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
-
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
-
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
-
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
-
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
-
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 6),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
-
- assertEquals(observer.onHealthCheckFailed(sFailingPackage,
- PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 7),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
- }
- @Test
@EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testHealthCheckLevelsNoFlags() {
// this is old test where the flag needs to be disabled
@@ -674,36 +409,6 @@ public class RescuePartyTest {
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
}
- @Test
- @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
- Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
- public void testBootLoopLevels() {
- // this is old test where the flag needs to be disabled
-
-
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-
- assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
- assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
- assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
- assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
- assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
- assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
- public void testBootLoopLevelsRecoverabilityDetection() {
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-
- assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
- assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
- assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_LEVEL_71);
- assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_LEVEL_75);
- assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_LEVEL_80);
- assertEquals(observer.onBootLoop(6), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
- }
@Test
@EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
@@ -714,129 +419,6 @@ public class RescuePartyTest {
assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
}
- @Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testResetDeviceConfigForPackagesOnlyRuntimeMap() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
-
- // Record DeviceConfig accesses
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
- // Fake DeviceConfig value changes
- monitorCallback.onNamespaceUpdate(NAMESPACE1);
- monitorCallback.onNamespaceUpdate(NAMESPACE2);
- monitorCallback.onNamespaceUpdate(NAMESPACE3);
-
- doReturn("").when(() -> DeviceConfig.getString(
- eq(RescueParty.NAMESPACE_CONFIGURATION),
- eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG),
- eq("")));
-
- RescueParty.resetDeviceConfigForPackages(Arrays.asList(new String[]{CALLING_PACKAGE1}));
- ArraySet<String> expectedNamespacesWiped = new ArraySet<String>(
- Arrays.asList(new String[]{NAMESPACE1, NAMESPACE2}));
- assertEquals(mNamespacesWiped, expectedNamespacesWiped);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testResetDeviceConfigForPackagesOnlyPresetMap() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
-
- String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + ","
- + NAMESPACE2 + ":" + CALLING_PACKAGE2 + ","
- + NAMESPACE3 + ":" + CALLING_PACKAGE1;
- doReturn(presetMapping).when(() -> DeviceConfig.getString(
- eq(RescueParty.NAMESPACE_CONFIGURATION),
- eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG),
- eq("")));
-
- RescueParty.resetDeviceConfigForPackages(Arrays.asList(new String[]{CALLING_PACKAGE1}));
- ArraySet<String> expectedNamespacesWiped = new ArraySet<String>(
- Arrays.asList(new String[]{NAMESPACE1, NAMESPACE3}));
- assertEquals(mNamespacesWiped, expectedNamespacesWiped);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testResetDeviceConfigForPackagesBothMaps() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
-
- // Record DeviceConfig accesses
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE3, NAMESPACE4);
- // Fake DeviceConfig value changes
- monitorCallback.onNamespaceUpdate(NAMESPACE1);
- monitorCallback.onNamespaceUpdate(NAMESPACE2);
- monitorCallback.onNamespaceUpdate(NAMESPACE3);
- monitorCallback.onNamespaceUpdate(NAMESPACE4);
-
- String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + ","
- + NAMESPACE2 + ":" + CALLING_PACKAGE2 + ","
- + NAMESPACE4 + ":" + CALLING_PACKAGE3;
- doReturn(presetMapping).when(() -> DeviceConfig.getString(
- eq(RescueParty.NAMESPACE_CONFIGURATION),
- eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG),
- eq("")));
-
- RescueParty.resetDeviceConfigForPackages(
- Arrays.asList(new String[]{CALLING_PACKAGE1, CALLING_PACKAGE2}));
- ArraySet<String> expectedNamespacesWiped = new ArraySet<String>(
- Arrays.asList(new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3}));
- assertEquals(mNamespacesWiped, expectedNamespacesWiped);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
- public void testResetDeviceConfigNoExceptionWhenFlagMalformed() {
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
-
- // Record DeviceConfig accesses
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE3, NAMESPACE4);
- // Fake DeviceConfig value changes
- monitorCallback.onNamespaceUpdate(NAMESPACE1);
- monitorCallback.onNamespaceUpdate(NAMESPACE2);
- monitorCallback.onNamespaceUpdate(NAMESPACE3);
- monitorCallback.onNamespaceUpdate(NAMESPACE4);
-
- String invalidPresetMapping = NAMESPACE2 + ":" + CALLING_PACKAGE2 + ","
- + NAMESPACE1 + "." + CALLING_PACKAGE2;
- doReturn(invalidPresetMapping).when(() -> DeviceConfig.getString(
- eq(RescueParty.NAMESPACE_CONFIGURATION),
- eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG),
- eq("")));
-
- RescueParty.resetDeviceConfigForPackages(
- Arrays.asList(new String[]{CALLING_PACKAGE1, CALLING_PACKAGE2}));
- ArraySet<String> expectedNamespacesWiped = new ArraySet<String>(
- Arrays.asList(new String[]{NAMESPACE1, NAMESPACE3}));
- assertEquals(mNamespacesWiped, expectedNamespacesWiped);
- }
private void verifySettingsResets(int resetMode, String[] resetNamespaces,
HashMap<String, Integer> configResetVerifiedTimesMap) {
@@ -858,13 +440,14 @@ public class RescuePartyTest {
}
private void noteBoot(int mitigationCount) {
- RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount);
+ RescuePartyObserver.getInstance(mMockContext).onExecuteBootLoopMitigation(mitigationCount);
}
private void noteAppCrash(int mitigationCount, boolean isPersistent) {
String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE;
- RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
- packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
+ RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ new VersionedPackage(packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH,
+ mitigationCount);
}
// Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2a825f35bf62..dcbc23410fdb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -1308,6 +1308,8 @@ public class ActivityManagerServiceTest {
Intent intent = new Intent();
Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
intent.putExtra("EXTRA_INTENT0", extraIntent);
+ Intent nestedIntent = new Intent("NESTED_INTENT_ACTION");
+ extraIntent.putExtra("NESTED_INTENT", nestedIntent);
intent.collectExtraIntentKeys();
mAms.addCreatorToken(intent, TEST_PACKAGE);
@@ -1317,6 +1319,11 @@ public class ActivityManagerServiceTest {
assertThat(token).isNotNull();
assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
+
+ token = (ActivityManagerService.IntentCreatorToken) nestedIntent.getCreatorToken();
+ assertThat(token).isNotNull();
+ assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+ assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
}
@Test
@@ -1349,6 +1356,8 @@ public class ActivityManagerServiceTest {
Intent intent = new Intent();
Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
intent.putExtra("EXTRA_INTENT", extraIntent);
+ Intent nestedIntent = new Intent("NESTED_INTENT_ACTION");
+ extraIntent.putExtra("NESTED_INTENT", nestedIntent);
intent.collectExtraIntentKeys();
@@ -1374,9 +1383,12 @@ public class ActivityManagerServiceTest {
extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class);
extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class);
extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class);
+ nestedIntent = extraIntent.getParcelableExtra("NESTED_INTENT", Intent.class);
assertThat(extraIntent.getExtendedFlags()
& Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
+ assertThat(nestedIntent.getExtendedFlags()
+ & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
// sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set.
assertThat(extraIntent2.getExtendedFlags()
& Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index d602660597ff..a1a8b0ec7d2f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -41,6 +41,7 @@ import android.os.TestLooperManager;
import android.os.UserHandle;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.SparseArray;
@@ -97,6 +98,9 @@ public abstract class BaseBroadcastQueueTest {
.spyStatic(ProcessList.class)
.build();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -112,6 +116,8 @@ public abstract class BaseBroadcastQueueTest {
AlarmManagerInternal mAlarmManagerInt;
@Mock
ProcessList mProcessList;
+ @Mock
+ PlatformCompat mPlatformCompat;
@Mock
AppStartInfoTracker mAppStartInfoTracker;
@@ -178,6 +184,11 @@ public abstract class BaseBroadcastQueueTest {
doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker();
+
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt());
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
}
public void tearDown() throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 100b54897573..1481085c5f71 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
@@ -49,7 +50,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -65,7 +65,6 @@ import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.BundleMerger;
@@ -73,6 +72,8 @@ import android.os.DropBoxManager;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -182,10 +183,6 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
return mock(Intent.class);
}
- private static ResolveInfo makeMockManifestReceiver() {
- return mock(ResolveInfo.class);
- }
-
private static BroadcastFilter makeMockRegisteredReceiver() {
return mock(BroadcastFilter.class);
}
@@ -214,7 +211,8 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false,
null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
- BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
+ BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN,
+ mPlatformCompat);
}
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
@@ -646,7 +644,8 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
@Test
public void testRunnableAt_Cached_Manifest() {
doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
- List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST);
+ List.of(makeManifestReceiver(PACKAGE_RED, CLASS_RED)), null, false),
+ REASON_CONTAINS_MANIFEST);
}
@Test
@@ -679,6 +678,19 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM);
}
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testRunnableAt_Cached_Prioritized_NonDeferrable_flagDisabled() {
+ final List receivers = List.of(
+ withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
+ final BroadcastOptions options = BroadcastOptions.makeBasic()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ receivers, null, false), REASON_CONTAINS_PRIORITIZED);
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testRunnableAt_Cached_Prioritized_NonDeferrable() {
final List receivers = List.of(
@@ -687,6 +699,32 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
final BroadcastOptions options = BroadcastOptions.makeBasic()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ receivers, null, false), REASON_CONTAINS_MANIFEST);
+ }
+
+ @Test
+ public void testRunnableAt_Cached_Ordered_NonDeferrable() {
+ final List receivers = List.of(
+ withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
+ final BroadcastOptions options = BroadcastOptions.makeBasic()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ receivers, mock(IIntentReceiver.class), true), REASON_CONTAINS_ORDERED);
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testRunnableAt_Cached_Prioritized_NonDeferrable_changeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(getUidForPackage(PACKAGE_GREEN)));
+ final List receivers = List.of(
+ withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
+ final BroadcastOptions options = BroadcastOptions.makeBasic()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
+ doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
receivers, null, false), REASON_CONTAINS_PRIORITIZED);
}
@@ -1136,6 +1174,63 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
verifyPendingRecords(blueQueue, List.of(screenOn));
}
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testDeliveryGroupPolicy_prioritized_diffReceivers_flagDisabled() {
+ final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
+ final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF);
+ final BroadcastOptions screenOnOffOptions = BroadcastOptions.makeBasic()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeliveryGroupMatchingKey("screenOnOff", Intent.ACTION_SCREEN_ON);
+
+ final Object greenReceiver = withPriority(
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10);
+ final Object redReceiver = withPriority(
+ makeManifestReceiver(PACKAGE_RED, CLASS_RED), 5);
+ final Object blueReceiver = withPriority(
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0);
+
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), false));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), false));
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ final BroadcastProcessQueue blueQueue = mImpl.getProcessQueue(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE));
+ verifyPendingRecords(greenQueue, List.of(screenOff));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOff));
+
+ assertTrue(greenQueue.isEmpty());
+ assertTrue(redQueue.isEmpty());
+ assertTrue(blueQueue.isEmpty());
+
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), false));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), false));
+ verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOff, screenOn));
+
+ final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), false);
+ screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED,
+ "testDeliveryGroupPolicy_prioritized_diffReceivers_flagDisabled");
+ mImpl.enqueueBroadcastLocked(screenOffRecord);
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), false));
+ verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOn));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("GuardedBy")
@Test
public void testDeliveryGroupPolicy_prioritized_diffReceivers() {
final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
@@ -1173,6 +1268,65 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
List.of(greenReceiver, redReceiver, blueReceiver), false));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
List.of(greenReceiver, blueReceiver), false));
+ verifyPendingRecords(greenQueue, List.of(screenOn));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOn));
+
+ final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), false);
+ screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED,
+ "testDeliveryGroupPolicy_prioritized_diffReceivers");
+ mImpl.enqueueBroadcastLocked(screenOffRecord);
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), false));
+ verifyPendingRecords(greenQueue, List.of(screenOn));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOn));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testDeliveryGroupPolicy_prioritized_diffReceivers_changeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(getUidForPackage(PACKAGE_GREEN)));
+
+ final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
+ final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF);
+ final BroadcastOptions screenOnOffOptions = BroadcastOptions.makeBasic()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeliveryGroupMatchingKey("screenOnOff", Intent.ACTION_SCREEN_ON);
+
+ final Object greenReceiver = withPriority(
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10);
+ final Object redReceiver = withPriority(
+ makeManifestReceiver(PACKAGE_RED, CLASS_RED), 5);
+ final Object blueReceiver = withPriority(
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0);
+
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), false));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), false));
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ final BroadcastProcessQueue blueQueue = mImpl.getProcessQueue(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE));
+ verifyPendingRecords(greenQueue, List.of(screenOff));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOff));
+
+ assertTrue(greenQueue.isEmpty());
+ assertTrue(redQueue.isEmpty());
+ assertTrue(blueQueue.isEmpty());
+
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), false));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), false));
verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
verifyPendingRecords(redQueue, List.of(screenOff));
verifyPendingRecords(blueQueue, List.of(screenOff, screenOn));
@@ -1569,8 +1723,9 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
verifyPendingRecords(redQueue, List.of(userPresent, timeTick));
}
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
- public void testDeliveryDeferredForCached() throws Exception {
+ public void testDeliveryDeferredForCached_flagDisabled() throws Exception {
final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
@@ -1664,8 +1819,217 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
}, false /* andRemove */);
}
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testDeliveryDeferredForCached_changeIdDisabled() throws Exception {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(getUidForPackage(PACKAGE_GREEN)));
+
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testDeliveryDeferredForCached_withInfiniteDeferred_flagDisabled() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastOptions optionsPackageChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ optionsPackageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
- public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception {
+ public void testDeliveryDeferredForCached_withInfiniteDeferred_changeIdDisabled()
+ throws Exception {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE),
+ eq(getUidForPackage(PACKAGE_GREEN)));
final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 3aaf2e5c61a6..9d92d5fe4f60 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -21,6 +21,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
import static com.android.server.am.BroadcastProcessQueue.reasonToString;
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
@@ -45,7 +46,6 @@ import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -77,6 +77,8 @@ import android.os.IBinder;
import android.os.PowerExemptionManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArrayMap;
import android.util.Log;
@@ -446,7 +448,8 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
callerApp.getPid(), callerApp.info.uid, false, null, null, null, null,
AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo,
Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId,
- BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
+ BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN,
+ mPlatformCompat);
}
private void assertHealth() {
@@ -1495,7 +1498,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(),
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null,
Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM,
- backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN);
+ backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN, mPlatformCompat);
enqueueBroadcast(r);
waitForIdle();
@@ -1550,8 +1553,10 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
/**
* Verify that when dispatching we respect tranches of priority.
*/
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("DistinctVarargsChecker")
@Test
- public void testPriority() throws Exception {
+ public void testPriority_flagDisabled() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
@@ -1594,6 +1599,106 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
}
/**
+ * Verify that when dispatching we respect tranches of priority.
+ */
+ @SuppressWarnings("DistinctVarargsChecker")
+ @Test
+ public void testOrdered_withPriorities() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
+
+ // Enqueue a normal broadcast that will go to several processes, and
+ // then enqueue a foreground broadcast that risks reordering
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ final IIntentReceiver orderedResultTo = mock(IIntentReceiver.class);
+ enqueueBroadcast(makeOrderedBroadcastRecord(timezone, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp, 10),
+ makeRegisteredReceiver(receiverGreenApp, 10),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
+ makeRegisteredReceiver(receiverYellowApp, -10)),
+ orderedResultTo, null));
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp))));
+ waitForIdle();
+
+ // Ignore the final foreground broadcast
+ mScheduledBroadcasts.remove(makeScheduledBroadcast(receiverBlueApp, airplane));
+ assertEquals(6, mScheduledBroadcasts.size());
+
+ // We're only concerned about enforcing ordering between tranches;
+ // within a tranche we're okay with reordering
+ assertEquals(
+ Set.of(makeScheduledBroadcast(receiverBlueApp, timezone),
+ makeScheduledBroadcast(receiverGreenApp, timezone)),
+ Set.of(mScheduledBroadcasts.remove(0),
+ mScheduledBroadcasts.remove(0)));
+ assertEquals(
+ Set.of(makeScheduledBroadcast(receiverBlueApp, timezone),
+ makeScheduledBroadcast(receiverYellowApp, timezone)),
+ Set.of(mScheduledBroadcasts.remove(0),
+ mScheduledBroadcasts.remove(0)));
+ assertEquals(
+ Set.of(makeScheduledBroadcast(receiverYellowApp, timezone)),
+ Set.of(mScheduledBroadcasts.remove(0)));
+ }
+
+ /**
+ * Verify that when dispatching we respect tranches of priority.
+ */
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @SuppressWarnings("DistinctVarargsChecker")
+ @Test
+ public void testPriority_changeIdDisabled() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
+
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
+
+ // Enqueue a normal broadcast that will go to several processes, and
+ // then enqueue a foreground broadcast that risks reordering
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp, 10),
+ makeRegisteredReceiver(receiverGreenApp, 10),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
+ makeRegisteredReceiver(receiverYellowApp, -10))));
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp))));
+ waitForIdle();
+
+ // Ignore the final foreground broadcast
+ mScheduledBroadcasts.remove(makeScheduledBroadcast(receiverBlueApp, airplane));
+ assertEquals(5, mScheduledBroadcasts.size());
+
+ // We're only concerned about enforcing ordering between tranches;
+ // within a tranche we're okay with reordering
+ assertEquals(
+ Set.of(makeScheduledBroadcast(receiverBlueApp, timezone),
+ makeScheduledBroadcast(receiverGreenApp, timezone)),
+ Set.of(mScheduledBroadcasts.remove(0),
+ mScheduledBroadcasts.remove(0)));
+ assertEquals(
+ Set.of(makeScheduledBroadcast(receiverBlueApp, timezone),
+ makeScheduledBroadcast(receiverYellowApp, timezone)),
+ Set.of(mScheduledBroadcasts.remove(0),
+ mScheduledBroadcasts.remove(0)));
+ assertEquals(
+ Set.of(makeScheduledBroadcast(receiverYellowApp, timezone)),
+ Set.of(mScheduledBroadcasts.remove(0)));
+ }
+
+ /**
* Verify prioritized receivers work as expected with deferrable broadcast - broadcast to
* app in cached state should be deferred and the rest should be delivered as per the priority
* order.
@@ -2305,8 +2410,35 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
.isLessThan(getReceiverScheduledTime(timeTickRecord, receiverBlue));
}
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testPrioritizedBroadcastDelivery_uidForeground_flagDisabled() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
+ ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
+ waitForIdle();
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+
+ final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10);
+ final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5);
+ final BroadcastRecord prioritizedRecord = makeBroadcastRecord(timeTick, callerApp,
+ List.of(receiverBlue, receiverGreen));
+
+ enqueueBroadcast(prioritizedRecord);
+
+ waitForIdle();
+ // Verify that uid foreground-ness does not impact that delivery of prioritized broadcast.
+ // That is, broadcast to receiverBlueApp gets scheduled before the one to receiverGreenApp.
+ assertThat(getReceiverScheduledTime(prioritizedRecord, receiverGreen))
+ .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue));
+ }
+
@Test
- public void testPrioritizedBroadcastDelivery_uidForeground() throws Exception {
+ public void testOrderedBroadcastDelivery_uidForeground() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
@@ -2319,6 +2451,37 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10);
final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5);
+ final IIntentReceiver resultTo = mock(IIntentReceiver.class);
+ final BroadcastRecord prioritizedRecord = makeOrderedBroadcastRecord(timeTick, callerApp,
+ List.of(receiverBlue, receiverGreen), resultTo, null);
+
+ enqueueBroadcast(prioritizedRecord);
+
+ waitForIdle();
+ // Verify that uid foreground-ness does not impact that delivery of prioritized broadcast.
+ // That is, broadcast to receiverBlueApp gets scheduled before the one to receiverGreenApp.
+ assertThat(getReceiverScheduledTime(prioritizedRecord, receiverGreen))
+ .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testPrioritizedBroadcastDelivery_uidForeground_changeIdDisabled() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid));
+
+ mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
+ ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
+ waitForIdle();
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+
+ final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10);
+ final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5);
final BroadcastRecord prioritizedRecord = makeBroadcastRecord(timeTick, callerApp,
List.of(receiverBlue, receiverGreen));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index 8cd0da721364..a424bfdb8df4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -18,6 +18,8 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.am.BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE;
import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED;
import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING;
@@ -33,6 +35,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
@@ -46,11 +50,17 @@ import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.SubscriptionManager;
import androidx.test.filters.SmallTest;
+import com.android.server.compat.PlatformCompat;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -73,6 +83,9 @@ import java.util.function.BiFunction;
public class BroadcastRecordTest {
private static final String TAG = "BroadcastRecordTest";
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final int USER0 = UserHandle.USER_SYSTEM;
private static final String PACKAGE1 = "pkg1";
private static final String PACKAGE2 = "pkg2";
@@ -89,10 +102,14 @@ public class BroadcastRecordTest {
@Mock BroadcastQueue mQueue;
@Mock ProcessRecord mProcess;
+ @Mock PlatformCompat mPlatformCompat;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt());
}
@Test
@@ -108,13 +125,13 @@ public class BroadcastRecordTest {
assertArrayEquals(new int[] {-1},
calculateBlockedUntilBeyondCount(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), 0)), false));
+ createResolveInfo(PACKAGE1, getAppId(1), 0)), false, mPlatformCompat));
assertArrayEquals(new int[] {-1},
calculateBlockedUntilBeyondCount(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), -10)), false));
+ createResolveInfo(PACKAGE1, getAppId(1), -10)), false, mPlatformCompat));
assertArrayEquals(new int[] {-1},
calculateBlockedUntilBeyondCount(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), 10)), false));
+ createResolveInfo(PACKAGE1, getAppId(1), 10)), false, mPlatformCompat));
}
@Test
@@ -128,18 +145,19 @@ public class BroadcastRecordTest {
createResolveInfo(PACKAGE2, getAppId(2), 10),
createResolveInfo(PACKAGE3, getAppId(3), 10))));
- assertArrayEquals(new int[] {-1,-1,-1},
+ assertArrayEquals(new int[] {-1, -1, -1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 0),
createResolveInfo(PACKAGE2, getAppId(2), 0),
- createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
- assertArrayEquals(new int[] {-1,-1,-1},
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {-1, -1, -1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 10),
- createResolveInfo(PACKAGE3, getAppId(3), 10)), false));
+ createResolveInfo(PACKAGE3, getAppId(3), 10)), false, mPlatformCompat));
}
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testIsPrioritized_Yes() {
assertTrue(isPrioritized(List.of(
@@ -151,18 +169,203 @@ public class BroadcastRecordTest {
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), 0))));
- assertArrayEquals(new int[] {0,1,2},
+ assertArrayEquals(new int[] {0, 1, 2},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 2, 3, 3},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(2), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testIsPrioritized_withDifferentPriorities() {
+ assertFalse(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10))));
+ assertFalse(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0))));
+
+ assertArrayEquals(new int[] {-1, -1, -1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {-1, -1, -1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {-1, -1, -1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {-1, -1, -1, -1, -1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(2), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testIsPrioritized_withDifferentPriorities_withFirstUidChangeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10))));
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0))));
+
+ assertArrayEquals(new int[] {0, 1, 1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
- createResolveInfo(PACKAGE3, getAppId(3), -10)), false));
- assertArrayEquals(new int[] {0,0,2,3,3},
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 1, 1, 1},
calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 20),
createResolveInfo(PACKAGE2, getAppId(2), 20),
createResolveInfo(PACKAGE3, getAppId(3), 10),
createResolveInfo(PACKAGE3, getAppId(3), 0),
- createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testIsPrioritized_withDifferentPriorities_withLastUidChangeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
+
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10))));
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0))));
+
+ assertArrayEquals(new int[] {0, 0, 2},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 2, 3, 3},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(2), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testIsPrioritized_withDifferentPriorities_withUidChangeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10))));
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0))));
+
+ assertArrayEquals(new int[] {0, 1, 2},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 1, 0},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 2, 2, 2},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(2), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(4), 0)), false, mPlatformCompat));
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testIsPrioritized_withDifferentPriorities_withMultipleUidChangeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10))));
+ assertTrue(isPrioritized(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0))));
+
+ assertArrayEquals(new int[] {0, 1, 2},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 1, 1},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE2, getAppId(2), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 2, 2, 2},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(2), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(4), 0)), false, mPlatformCompat));
+ assertArrayEquals(new int[] {0, 0, 1, 1, 3},
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(3), 20),
+ createResolveInfo(PACKAGE3, getAppId(3), 10),
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(2), 0)), false, mPlatformCompat));
}
@Test
@@ -246,6 +449,77 @@ public class BroadcastRecordTest {
assertTerminalDeferredBeyond(r, 3, 0, 3);
}
+ @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testSetDeliveryState_DeferUntilActive_flagDisabled() {
+ final BroadcastRecord r = createBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
+ createResolveInfoWithPriority(10),
+ createResolveInfoWithPriority(10),
+ createResolveInfoWithPriority(10),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(-10),
+ createResolveInfoWithPriority(-10),
+ createResolveInfoWithPriority(-10)));
+ assertBlocked(r, false, false, false, true, true, true, true, true, true);
+ assertTerminalDeferredBeyond(r, 0, 0, 0);
+
+ r.setDeliveryState(0, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(1, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(2, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(3, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(4, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(5, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(6, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(7, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(8, DELIVERY_DEFERRED, TAG);
+
+ // Verify deferred counts ratchet up, but we're not "beyond" the first
+ // still-pending receiver
+ assertBlocked(r, false, false, false, true, true, true, true, true, true);
+ assertTerminalDeferredBeyond(r, 0, 6, 0);
+
+ // We're still not "beyond" the first still-pending receiver, even when
+ // we finish a receiver later in the first tranche
+ r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, true, true, true, true, true, true);
+ assertTerminalDeferredBeyond(r, 1, 6, 0);
+
+ // Completing that last item in first tranche means we now unblock the
+ // second tranche, and since it's entirely deferred, the third traunche
+ // is unblocked too
+ r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 6, 7);
+
+ // Moving a deferred item in an earlier tranche back to being pending
+ // doesn't change the fact that we've already moved beyond it
+ r.setDeliveryState(1, DELIVERY_PENDING, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 5, 7);
+ r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 3, 5, 7);
+
+ // Completing middle pending item is enough to fast-forward to end
+ r.setDeliveryState(7, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 4, 5, 9);
+
+ // Moving everyone else directly into a finished state updates all the
+ // terminal counters
+ r.setDeliveryState(3, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(4, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(5, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(6, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(8, DELIVERY_SKIPPED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 9, 0, 9);
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
@Test
public void testSetDeliveryState_DeferUntilActive() {
final BroadcastRecord r = createBroadcastRecord(
@@ -259,6 +533,78 @@ public class BroadcastRecordTest {
createResolveInfoWithPriority(-10),
createResolveInfoWithPriority(-10),
createResolveInfoWithPriority(-10)));
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 0, 0, 0);
+
+ r.setDeliveryState(0, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(1, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(2, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(3, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(4, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(5, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(6, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(7, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(8, DELIVERY_DEFERRED, TAG);
+
+ // Verify deferred counts ratchet up, but we're not "beyond" the first
+ // still-pending receiver
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 0, 6, 0);
+
+ // We're still not "beyond" the first still-pending receiver, even when
+ // we finish a receiver later in the first tranche
+ r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 1, 6, 0);
+
+ // Completing that last item in first tranche means we now unblock the
+ // second tranche, and since it's entirely deferred, the third traunche
+ // is unblocked too
+ r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 6, 7);
+
+ // Moving a deferred item in an earlier tranche back to being pending
+ // doesn't change the fact that we've already moved beyond it
+ r.setDeliveryState(1, DELIVERY_PENDING, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 5, 7);
+ r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 3, 5, 7);
+
+ // Completing middle pending item is enough to fast-forward to end
+ r.setDeliveryState(7, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 4, 5, 9);
+
+ // Moving everyone else directly into a finished state updates all the
+ // terminal counters
+ r.setDeliveryState(3, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(4, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(5, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(6, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(8, DELIVERY_SKIPPED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 9, 0, 9);
+ }
+
+ @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE)
+ @Test
+ public void testSetDeliveryState_DeferUntilActive_changeIdDisabled() {
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ final BroadcastRecord r = createBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
+ createResolveInfo(PACKAGE1, getAppId(1), 0),
+ createResolveInfo(PACKAGE1, getAppId(1), 0),
+ createResolveInfo(PACKAGE1, getAppId(1), 0),
+ createResolveInfo(PACKAGE1, getAppId(1), -10),
+ createResolveInfo(PACKAGE1, getAppId(1), -10),
+ createResolveInfo(PACKAGE1, getAppId(1), -10)));
assertBlocked(r, false, false, false, true, true, true, true, true, true);
assertTerminalDeferredBeyond(r, 0, 0, 0);
@@ -602,6 +948,66 @@ public class BroadcastRecordTest {
assertTrue(record3.matchesDeliveryGroup(record1));
}
+ @Test
+ public void testCalculateChangeStateForReceivers() {
+ assertArrayEquals(new boolean[] {true, true, true}, calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+ assertArrayEquals(new boolean[] {true, true, true, true}, calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1)));
+ assertArrayEquals(new boolean[] {false, true, true}, calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+ assertArrayEquals(new boolean[] {false, true, false, true}, calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE2, getAppId(1)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2)));
+ assertArrayEquals(new boolean[] {false, false, true}, calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+ assertArrayEquals(new boolean[] {false, true, false, false, false, true},
+ calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE3, getAppId(3)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE2, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+
+ doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging(
+ eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3)));
+ assertArrayEquals(new boolean[] {false, false, false}, calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+ assertArrayEquals(new boolean[] {false, false, false, false, false, false},
+ calculateChangeState(
+ List.of(createResolveInfo(PACKAGE1, getAppId(1)),
+ createResolveInfo(PACKAGE3, getAppId(3)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE2, getAppId(1)),
+ createResolveInfo(PACKAGE2, getAppId(2)),
+ createResolveInfo(PACKAGE3, getAppId(3)))));
+ }
+
+ private boolean[] calculateChangeState(List<Object> receivers) {
+ return BroadcastRecord.calculateChangeStateForReceivers(receivers,
+ CHANGE_LIMIT_PRIORITY_SCOPE, mPlatformCompat);
+ }
+
private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
String packageName, int userId) {
record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */,
@@ -753,16 +1159,17 @@ public class BroadcastRecordTest {
BackgroundStartPrivileges.NONE,
false /* timeoutExempt */,
filterExtrasForReceiver,
- PROCESS_STATE_UNKNOWN);
+ PROCESS_STATE_UNKNOWN,
+ mPlatformCompat);
}
private static int getAppId(int i) {
return Process.FIRST_APPLICATION_UID + i;
}
- private static boolean isPrioritized(List<Object> receivers) {
+ private boolean isPrioritized(List<Object> receivers) {
return BroadcastRecord.isPrioritized(
- calculateBlockedUntilBeyondCount(receivers, false), false);
+ calculateBlockedUntilBeyondCount(receivers, false, mPlatformCompat), false);
}
private static void assertBlocked(BroadcastRecord r, boolean... blocked) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index b0053581963a..f82a86092064 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -60,6 +60,7 @@ import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_AP
import static com.android.server.am.ProcessList.PERSISTENT_PROC_ADJ;
import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
+import static com.android.server.am.ProcessList.PREVIOUS_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
import static com.android.server.am.ProcessList.SCHED_GROUP_DEFAULT;
import static com.android.server.am.ProcessList.SCHED_GROUP_FOREGROUND_WINDOW;
@@ -108,6 +109,7 @@ import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.server.LocalServices;
@@ -129,6 +131,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* Test class for {@link OomAdjuster}.
@@ -173,6 +176,7 @@ public class MockingOomAdjusterTests {
private ActiveUids mActiveUids;
private PackageManagerInternal mPackageManagerInternal;
private ActivityManagerService mService;
+ private TestCachedAppOptimizer mTestCachedAppOptimizer;
private OomAdjusterInjector mInjector = new OomAdjusterInjector();
private int mUiTierSize;
@@ -240,9 +244,11 @@ public class MockingOomAdjusterTests {
doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(),
anyBoolean());
mActiveUids = new ActiveUids(mService, false);
+ mTestCachedAppOptimizer = new TestCachedAppOptimizer(mService);
mProcessStateController = new ProcessStateController.Builder(mService,
mService.mProcessList, mActiveUids)
.useModernOomAdjuster(mService.mConstants.ENABLE_NEW_OOMADJ)
+ .setCachedAppOptimizer(mTestCachedAppOptimizer)
.setOomAdjusterInjector(mInjector)
.build();
mService.mProcessStateController = mProcessStateController;
@@ -899,8 +905,25 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoPending_PreviousApp() {
+ testUpdateOomAdj_PreviousApp(apps -> {
+ for (ProcessRecord app : apps) {
+ mProcessStateController.enqueueUpdateTarget(app);
+ }
+ mProcessStateController.runPendingUpdate(OOM_ADJ_REASON_NONE);
+ });
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoAll_PreviousApp() {
- final int numberOfApps = 15;
+ testUpdateOomAdj_PreviousApp(apps -> {
+ mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
+ });
+ }
+
+ private void testUpdateOomAdj_PreviousApp(Consumer<ProcessRecord[]> updater) {
+ final int numberOfApps = 105;
final ProcessRecord[] apps = new ProcessRecord[numberOfApps];
for (int i = 0; i < numberOfApps; i++) {
apps[i] = spy(makeDefaultProcessRecord(MOCKAPP_PID + i, MOCKAPP_UID + i,
@@ -911,10 +934,11 @@ public class MockingOomAdjusterTests {
}
setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
setProcessesToLru(apps);
- mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
-
+ updater.accept(apps);
for (int i = 0; i < numberOfApps; i++) {
- assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ final int mruIndex = numberOfApps - i - 1;
+ final int expectedAdj = Math.min(PREVIOUS_APP_ADJ + mruIndex, PREVIOUS_APP_MAX_ADJ);
+ assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
SCHED_GROUP_BACKGROUND, "previous");
}
@@ -3090,13 +3114,13 @@ public class MockingOomAdjusterTests {
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
assertEquals(true, app.getUidRecord().isSetAllowListed());
- assertEquals(true, app.mOptRecord.shouldNotFreeze());
- assertEquals(true, app2.mOptRecord.shouldNotFreeze());
+ assertFreezeState(app, false);
+ assertFreezeState(app2, false);
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
- assertEquals(false, app.mOptRecord.shouldNotFreeze());
- assertEquals(false, app2.mOptRecord.shouldNotFreeze());
+ assertFreezeState(app, true);
+ assertFreezeState(app2, true);
}
@SuppressWarnings("GuardedBy")
@@ -3118,25 +3142,25 @@ public class MockingOomAdjusterTests {
assertEquals(true, app.getUidRecord().isSetAllowListed());
assertEquals(true, app2.getUidRecord().isSetAllowListed());
- assertEquals(true, app.mOptRecord.shouldNotFreeze());
- assertEquals(true, app2.mOptRecord.shouldNotFreeze());
- assertEquals(true, app3.mOptRecord.shouldNotFreeze());
+ assertFreezeState(app, false);
+ assertFreezeState(app2, false);
+ assertFreezeState(app3, false);
// Remove app1 from allowlist.
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
assertEquals(true, app2.getUidRecord().isSetAllowListed());
- assertEquals(false, app.mOptRecord.shouldNotFreeze());
- assertEquals(true, app2.mOptRecord.shouldNotFreeze());
- assertEquals(true, app3.mOptRecord.shouldNotFreeze());
+ assertFreezeState(app, true);
+ assertFreezeState(app2, false);
+ assertFreezeState(app3, false);
// Now remove app2 from allowlist.
mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
assertEquals(false, app2.getUidRecord().isSetAllowListed());
- assertEquals(false, app.mOptRecord.shouldNotFreeze());
- assertEquals(false, app2.mOptRecord.shouldNotFreeze());
- assertEquals(false, app3.mOptRecord.shouldNotFreeze());
+ assertFreezeState(app, true);
+ assertFreezeState(app2, true);
+ assertFreezeState(app3, true);
}
@SuppressWarnings("GuardedBy")
@@ -3184,7 +3208,8 @@ public class MockingOomAdjusterTests {
setProcessesToLru(app1, app2);
mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
- assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY,
+ PREVIOUS_APP_ADJ + (Flags.oomadjusterPrevLaddering() ? 1 : 0),
SCHED_GROUP_BACKGROUND, "recent-provider");
assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
SCHED_GROUP_BACKGROUND, "recent-provider");
@@ -3349,6 +3374,14 @@ public class MockingOomAdjusterTests {
assertEquals(expectedCached, state.isCached());
}
+ @SuppressWarnings("GuardedBy")
+ private void assertFreezeState(ProcessRecord app, boolean expectedFreezeState) {
+ boolean actualFreezeState = mTestCachedAppOptimizer.mLastSetFreezeState.get(app.getPid(),
+ false);
+ assertEquals("Unexcepted freeze state for " + app.processName, expectedFreezeState,
+ actualFreezeState);
+ }
+
private class ProcessRecordBuilder {
@SuppressWarnings("UnusedVariable")
int mPid;
@@ -3492,6 +3525,39 @@ public class MockingOomAdjusterTests {
return app;
}
}
+ private static final class TestProcessDependencies
+ implements CachedAppOptimizer.ProcessDependencies {
+ @Override
+ public long[] getRss(int pid) {
+ return new long[]{/*totalRSS*/ 0, /*fileRSS*/ 0, /*anonRSS*/ 0, /*swap*/ 0};
+ }
+
+ @Override
+ public void performCompaction(CachedAppOptimizer.CompactProfile action, int pid) {}
+ }
+
+ private static class TestCachedAppOptimizer extends CachedAppOptimizer {
+ private SparseBooleanArray mLastSetFreezeState = new SparseBooleanArray();
+
+ TestCachedAppOptimizer(ActivityManagerService ams) {
+ super(ams, null, new TestProcessDependencies());
+ }
+
+ @Override
+ public boolean useFreezer() {
+ return true;
+ }
+
+ @Override
+ public void freezeAppAsyncLSP(ProcessRecord app) {
+ mLastSetFreezeState.put(app.getPid(), true);
+ }
+
+ @Override
+ public void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
+ mLastSetFreezeState.put(app.getPid(), false);
+ }
+ }
static class OomAdjusterInjector extends OomAdjuster.Injector {
// Jump ahead in time by this offset amount.
@@ -3503,7 +3569,6 @@ public class MockingOomAdjusterTests {
mLastSetOomAdj.clear();
}
-
void jumpUptimeAheadTo(long uptimeMillis) {
final long jumpMs = uptimeMillis - getUptimeMillis();
if (jumpMs <= 0) return;
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
index 197342874b2a..f7c2e8b72d6b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
@@ -21,6 +21,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static org.junit.Assert.assertEquals;
+import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.os.Handler;
@@ -63,6 +64,7 @@ public class AppOpsLegacyRestrictionsTest {
@Before
public void setUp() {
+ PropertyInvalidatedCache.disableForTestMode();
mSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
diff --git a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
index 7ac7aca3fd59..1f88c29e2abc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
@@ -36,7 +36,10 @@ android_test {
"services.core",
"truth",
"flag-junit",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ "true": ["service-crashrecovery.impl"],
+ default: [],
+ }),
libs: [
"android.test.mock.stubs.system",
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 4e1f741b1398..dd7ce21e3628 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -2351,6 +2351,7 @@ public class JobSchedulerServiceTest {
/** Tests that jobs are removed from the pending list if the user stops the app. */
@Test
+ @RequiresFlagsDisabled(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API)
public void testUserStopRemovesPending() {
spyOn(mService);
@@ -2402,6 +2403,60 @@ public class JobSchedulerServiceTest {
assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReason(job2b));
}
+ /** Tests that jobs are removed from the pending list if the user stops the app. */
+ @Test
+ @RequiresFlagsEnabled(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API)
+ public void testUserStopRemovesPending_withPendingJobReasonsApi() {
+ spyOn(mService);
+
+ JobStatus job1a = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(1), 1, "pkg1");
+ JobStatus job1b = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(2), 1, "pkg1");
+ JobStatus job2a = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(1), 2, "pkg2");
+ JobStatus job2b = createJobStatus("testUserStopRemovesPending",
+ createJobInfo(2), 2, "pkg2");
+ doReturn(1).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 0);
+ doReturn(11).when(mPackageManagerInternal).getPackageUid("pkg1", 0, 1);
+ doReturn(2).when(mPackageManagerInternal).getPackageUid("pkg2", 0, 0);
+
+ mService.getPendingJobQueue().clear();
+ mService.getPendingJobQueue().add(job1a);
+ mService.getPendingJobQueue().add(job1b);
+ mService.getPendingJobQueue().add(job2a);
+ mService.getPendingJobQueue().add(job2b);
+ mService.getJobStore().add(job1a);
+ mService.getJobStore().add(job1b);
+ mService.getJobStore().add(job2a);
+ mService.getJobStore().add(job2b);
+
+ mService.notePendingUserRequestedAppStopInternal("pkg1", 1, "test");
+ assertEquals(4, mService.getPendingJobQueue().size());
+ assertTrue(mService.getPendingJobQueue().contains(job1a));
+ assertTrue(mService.getPendingJobQueue().contains(job1b));
+ assertTrue(mService.getPendingJobQueue().contains(job2a));
+ assertTrue(mService.getPendingJobQueue().contains(job2b));
+
+ mService.notePendingUserRequestedAppStopInternal("pkg1", 0, "test");
+ assertEquals(2, mService.getPendingJobQueue().size());
+ assertFalse(mService.getPendingJobQueue().contains(job1a));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job1a)[0]);
+ assertFalse(mService.getPendingJobQueue().contains(job1b));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job1b)[0]);
+ assertTrue(mService.getPendingJobQueue().contains(job2a));
+ assertTrue(mService.getPendingJobQueue().contains(job2b));
+
+ mService.notePendingUserRequestedAppStopInternal("pkg2", 0, "test");
+ assertEquals(0, mService.getPendingJobQueue().size());
+ assertFalse(mService.getPendingJobQueue().contains(job1a));
+ assertFalse(mService.getPendingJobQueue().contains(job1b));
+ assertFalse(mService.getPendingJobQueue().contains(job2a));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job2a)[0]);
+ assertFalse(mService.getPendingJobQueue().contains(job2b));
+ assertEquals(JobScheduler.PENDING_JOB_REASON_USER, mService.getPendingJobReasons(job2b)[0]);
+ }
+
/**
* Unit tests {@link JobSchedulerService#checkIfRestricted(JobStatus)} with single {@link
* JobRestriction} registered.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
new file mode 100644
index 000000000000..f6c644e3d4d4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.ApkLite;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.FileUtils;
+import android.os.OutcomeReceiver;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@RunWith(JUnit4.class)
+@RequiresFlagsEnabled(FLAG_SDK_DEPENDENCY_INSTALLER)
+public class InstallDependencyHelperTest {
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+ @Rule public final CheckFlagsRule checkFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/smockingservicestest/pm/";
+ private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk";
+
+ @Mock private SharedLibrariesImpl mSharedLibraries;
+ private InstallDependencyHelper mInstallDependencyHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mInstallDependencyHelper = new InstallDependencyHelper(mSharedLibraries);
+ }
+
+ @Test
+ public void testResolveLibraryDependenciesIfNeeded_errorInSharedLibrariesImpl()
+ throws Exception {
+ doThrow(new PackageManagerException(new Exception("xyz")))
+ .when(mSharedLibraries).collectMissingSharedLibraryInfos(any());
+
+ PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
+ CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ callback.assertFailure();
+
+ assertThat(callback.error).hasMessageThat().contains("xyz");
+ }
+
+ @Test
+ public void testResolveLibraryDependenciesIfNeeded_failsToBind() throws Exception {
+ // Return a non-empty list as missing dependency
+ PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
+ List<SharedLibraryInfo> missingDependency = Collections.singletonList(
+ mock(SharedLibraryInfo.class));
+ when(mSharedLibraries.collectMissingSharedLibraryInfos(eq(pkg)))
+ .thenReturn(missingDependency);
+
+ CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ callback.assertFailure();
+
+ assertThat(callback.error).hasMessageThat().contains(
+ "Failed to bind to Dependency Installer");
+ }
+
+
+ @Test
+ public void testResolveLibraryDependenciesIfNeeded_allDependenciesInstalled() throws Exception {
+ // Return an empty list as missing dependency
+ PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
+ List<SharedLibraryInfo> missingDependency = Collections.emptyList();
+ when(mSharedLibraries.collectMissingSharedLibraryInfos(eq(pkg)))
+ .thenReturn(missingDependency);
+
+ CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ callback.assertSuccess();
+ }
+
+ private static class CallbackHelper implements OutcomeReceiver<Void, PackageManagerException> {
+ public PackageManagerException error;
+
+ private final CountDownLatch mWait = new CountDownLatch(1);
+ private final boolean mExpectSuccess;
+
+ CallbackHelper(boolean expectSuccess) {
+ mExpectSuccess = expectSuccess;
+ }
+
+ @Override
+ public void onResult(Void result) {
+ if (!mExpectSuccess) {
+ fail("Expected to fail");
+ }
+ mWait.countDown();
+ }
+
+ @Override
+ public void onError(@NonNull PackageManagerException e) {
+ if (mExpectSuccess) {
+ fail("Expected success but received: " + e);
+ }
+ error = e;
+ mWait.countDown();
+ }
+
+ void assertSuccess() throws Exception {
+ assertThat(mWait.await(1000, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(error).isNull();
+ }
+
+ void assertFailure() throws Exception {
+ assertThat(mWait.await(1000, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(error).isNotNull();
+ }
+
+ }
+
+ private PackageLite getPackageLite(String apkFileName) throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
+ ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+ ApkLite baseApk = result.getResult();
+
+ return new PackageLite(/*path=*/ null, baseApk.getPath(), baseApk,
+ /*splitNames=*/ null, /*isFeatureSplits=*/ null, /*usesSplitNames=*/ null,
+ /*configForSplit=*/ null, /*splitApkPaths=*/ null,
+ /*splitRevisionCodes=*/ null, baseApk.getTargetSdkVersion(),
+ /*requiredSplitTypes=*/ null, /*splitTypes=*/ null);
+ }
+
+ private File copyApkToTmpDir(String apkFileName) throws Exception {
+ File outFile = temporaryFolder.newFile(apkFileName);
+ String apkFilePath = PUSH_FILE_DIR + apkFileName;
+ File apkFile = new File(apkFilePath);
+ assertThat(apkFile.exists()).isTrue();
+ try (InputStream is = new FileInputStream(apkFile)) {
+ FileUtils.copyToFileOrThrow(is, outFile);
+ }
+ return outFile;
+ }
+
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 591e8df1725b..71c60ad02794 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -742,10 +742,11 @@ public class StagingManagerTest {
/* stagedSessionErrorMessage */ "no error",
/* preVerifiedDomains */ null,
/* verifierController */ null,
- /* initialVerificationPolicy */
+ /* initialVerificationPolicy */
PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
/* currentVerificationPolicy */
- PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED);
+ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
+ /* installDependencyHelper */ null);
StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
doReturn(packageName).when(stagedSession).getPackageName();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 1cba3c574543..8a10040f986f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -195,12 +195,12 @@ public final class UserManagerServiceTest {
doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
mockIsLowRamDevice(false);
- // Called when getting boot user. config_bootToHeadlessSystemUser is false by default.
+ // Called when getting boot user. config_bootToHeadlessSystemUser is 0 by default.
mSpyResources = spy(mSpiedContext.getResources());
when(mSpiedContext.getResources()).thenReturn(mSpyResources);
- doReturn(false)
+ doReturn(0)
.when(mSpyResources)
- .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
// Must construct UserManagerService in the UiThread
mTestDir = new File(mRealContext.getDataDir(), "umstest");
@@ -859,15 +859,50 @@ public final class UserManagerServiceTest {
}
@Test
- public void testGetBootUser_enableBootToHeadlessSystemUser() {
+ public void testGetBootUser_Headless_BootToSystemUserWhenDeviceIsProvisioned() {
setSystemUserHeadless(true);
- doReturn(true)
+ addUser(USER_ID);
+ addUser(OTHER_USER_ID);
+ mockProvisionedDevice(true);
+ doReturn(1)
.when(mSpyResources)
- .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
assertThat(mUms.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
}
+ @Test
+ public void testGetBootUser_Headless_BootToFirstSwitchableFullUserWhenDeviceNotProvisioned() {
+ setSystemUserHeadless(true);
+ addUser(USER_ID);
+ addUser(OTHER_USER_ID);
+ mockProvisionedDevice(false);
+ doReturn(1)
+ .when(mSpyResources)
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
+ // Even if the headless system user switchable flag is true, the boot user should be the
+ // first switchable full user.
+ doReturn(true)
+ .when(mSpyResources)
+ .getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser);
+
+ assertThat(mUms.getBootUser()).isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetBootUser_Headless_ThrowsIfBootFailsNoFullUserWhenDeviceNotProvisioned()
+ throws Exception {
+ setSystemUserHeadless(true);
+ removeNonSystemUsers();
+ mockProvisionedDevice(false);
+ doReturn(1)
+ .when(mSpyResources)
+ .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
+
+ assertThrows(ServiceSpecificException.class,
+ () -> mUms.getBootUser());
+ }
+
/**
* Returns true if the user's XML file has Default restrictions
* @param userId Id of the user.
@@ -935,6 +970,11 @@ public final class UserManagerServiceTest {
any(), eq(android.provider.Settings.Global.USER_SWITCHER_ENABLED), anyInt()));
}
+ private void mockProvisionedDevice(boolean isProvisionedDevice) {
+ doReturn(isProvisionedDevice ? 1 : 0).when(() -> Settings.Global.getInt(
+ any(), eq(android.provider.Settings.Global.DEVICE_PROVISIONED), anyInt()));
+ }
+
private void mockIsLowRamDevice(boolean isLowRamDevice) {
doReturn(isLowRamDevice).when(ActivityManager::isLowRamDeviceStatic);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index 677ecf47355d..2f23e02f5737 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -34,7 +34,10 @@ android_test {
"services.core",
"truth",
"flag-junit",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ "true": ["service-crashrecovery.impl"],
+ default: [],
+ }),
libs: [
"android.test.mock.stubs.system",
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index e0c7bfe91890..347dc81c6734 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -109,14 +109,11 @@ public class RollbackPackageHealthObserverTest {
private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG =
"persist.device_config.configuration.disable_high_impact_rollback";
- private SystemConfig mSysConfig;
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@Before
public void setup() {
- mSysConfig = new SystemConfigTestClass();
-
mSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
@@ -184,7 +181,7 @@ public class RollbackPackageHealthObserverTest {
@Test
public void testHealthCheckLevels() {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE);
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
@@ -228,14 +225,14 @@ public class RollbackPackageHealthObserverTest {
@Test
public void testIsPersistent() {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
assertTrue(observer.isPersistent());
}
@Test
public void testMayObservePackage_withoutAnyRollback() {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
assertFalse(observer.mayObservePackage(APP_A));
@@ -245,7 +242,7 @@ public class RollbackPackageHealthObserverTest {
public void testMayObservePackage_forPersistentApp()
throws PackageManager.NameNotFoundException {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ApplicationInfo info = new ApplicationInfo();
info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM;
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -260,7 +257,7 @@ public class RollbackPackageHealthObserverTest {
public void testMayObservePackage_forNonPersistentApp()
throws PackageManager.NameNotFoundException {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
@@ -286,7 +283,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_LOW);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -317,7 +314,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -348,7 +345,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -386,7 +383,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 222,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -419,7 +416,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_LOW);
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
// Make the rollbacks available
@@ -427,7 +424,7 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(secondFailedPackage,
+ observer.onExecuteHealthCheckMitigation(secondFailedPackage,
PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
@@ -461,7 +458,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 222,
PackageManager.ROLLBACK_USER_IMPACT_LOW);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -471,7 +468,8 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(appBFrom, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ observer.onExecuteHealthCheckMitigation(appBFrom, PackageWatchdog.FAILURE_REASON_APP_CRASH,
+ 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager).commitRollback(argument.capture(), any(), any());
@@ -506,7 +504,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_LOW);
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -516,7 +514,8 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ observer.onExecuteHealthCheckMitigation(failedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(2)).commitRollback(
@@ -552,7 +551,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -562,7 +561,8 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ observer.onExecuteHealthCheckMitigation(failedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(1)).commitRollback(
@@ -590,7 +590,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -599,7 +599,8 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ observer.onExecuteHealthCheckMitigation(failedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any());
@@ -621,7 +622,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_LOW);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
// Make the rollbacks available
@@ -646,7 +647,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
// Make the rollbacks available
@@ -672,7 +673,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
// Make the rollbacks available
@@ -701,7 +702,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
// Make the rollbacks available
@@ -737,7 +738,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 222,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
// Make the rollbacks available
@@ -776,7 +777,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 222,
PackageManager.ROLLBACK_USER_IMPACT_LOW);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -786,7 +787,7 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.executeBootLoopMitigation(1);
+ observer.onExecuteBootLoopMitigation(1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(2)).commitRollback(
@@ -821,7 +822,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 222,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -831,7 +832,7 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.executeBootLoopMitigation(1);
+ observer.onExecuteBootLoopMitigation(1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(1)).commitRollback(
@@ -857,7 +858,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -866,7 +867,7 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.executeBootLoopMitigation(1);
+ observer.onExecuteBootLoopMitigation(1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(1)).commitRollback(
@@ -902,7 +903,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -912,7 +913,8 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ observer.onExecuteHealthCheckMitigation(failedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(1)).commitRollback(
@@ -938,7 +940,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -947,7 +949,8 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ observer.onExecuteHealthCheckMitigation(failedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any());
@@ -980,7 +983,7 @@ public class RollbackPackageHealthObserverTest {
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -990,7 +993,7 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.executeBootLoopMitigation(1);
+ observer.onExecuteBootLoopMitigation(1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, times(1)).commitRollback(
@@ -1026,7 +1029,7 @@ public class RollbackPackageHealthObserverTest {
false, null, 111,
PackageManager.ROLLBACK_USER_IMPACT_HIGH);
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mMockContext));
ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -1036,7 +1039,7 @@ public class RollbackPackageHealthObserverTest {
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- observer.executeBootLoopMitigation(1);
+ observer.onExecuteBootLoopMitigation(1);
waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
verify(mRollbackManager, never()).commitRollback(
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index f03043ea0ae0..2f0633139331 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -12,6 +12,7 @@ android_test {
],
static_libs: [
+ "truth",
"flag-junit",
"frameworks-base-testutils",
"platform-compat-test-rules",
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 1c7fc63efd41..96741e0b1e87 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -19,13 +19,17 @@ package com.android.server.power;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -42,6 +46,7 @@ import android.hardware.SensorManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
import android.os.Handler;
import android.os.IWakeLockCallback;
import android.os.Looper;
@@ -50,6 +55,7 @@ import android.os.RemoteException;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.WorkSource;
+import android.os.WorkSource.WorkChain;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.testing.TestableContext;
@@ -67,12 +73,15 @@ import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.FrameworkStatsLogger.WakelockEventType;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -87,6 +96,14 @@ public class NotifierTest {
private static final int DISPLAY_PORT = 0xFF;
private static final long DISPLAY_MODEL = 0xEEEEEEEEL;
+ private static final int UID = 1234;
+ private static final int OWNER_UID = 1235;
+ private static final int WORK_SOURCE_UID_1 = 2345;
+ private static final int WORK_SOURCE_UID_2 = 2346;
+ private static final int OWNER_WORK_SOURCE_UID_1 = 3456;
+ private static final int OWNER_WORK_SOURCE_UID_2 = 3457;
+ private static final int PID = 5678;
+
@Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@Mock private Notifier mNotifierMock;
@@ -110,13 +127,15 @@ public class NotifierTest {
@Mock private AppOpsManager mAppOpsManager;
+ @Mock private BatteryStatsInternal mBatteryStatsInternal;
+ @Mock private FrameworkStatsLogger mLogger;
+
private PowerManagerService mService;
private Context mContextSpy;
private Resources mResourcesSpy;
private TestLooper mTestLooper = new TestLooper();
private FakeExecutor mTestExecutor = new FakeExecutor();
private Notifier mNotifier;
-
private DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
@Before
@@ -411,6 +430,246 @@ public class NotifierTest {
}
@Test
+ public void testOnWakeLockReleased_FrameworkStatsLogged_NoChains() {
+ when(mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()).thenReturn(true);
+ createNotifier();
+
+ clearInvocations(mLogger, mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ when(mBatteryStatsInternal.getOwnerUid(UID)).thenReturn(OWNER_UID);
+ when(mBatteryStatsInternal.getOwnerUid(WORK_SOURCE_UID_1))
+ .thenReturn(OWNER_WORK_SOURCE_UID_1);
+
+ mNotifier.onWakeLockAcquired(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "wakelockTag",
+ "my.package.name",
+ UID,
+ PID,
+ /* workSource= */ null,
+ /* historyTag= */ null,
+ /* callback= */ null);
+
+ WorkSource ws = new WorkSource(WORK_SOURCE_UID_1);
+
+ mNotifier.onWakeLockChanging(
+ /* existing WakeLock params */
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "wakelockTag",
+ "my.package.name",
+ UID,
+ PID,
+ /* workSource= */ null,
+ /* historyTag= */ null,
+ /* callback= */ null,
+ /* updated WakeLock params */
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "wakelockTag",
+ "my.package.name",
+ UID,
+ PID,
+ ws,
+ /* historyTag= */ null,
+ /* callback= */ null);
+
+ mNotifier.onWakeLockReleased(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "wakelockTag",
+ "my.package.name",
+ UID,
+ PID,
+ ws,
+ /* historyTag= */ null,
+ /* callback= */ null);
+
+ verify(mBatteryStatsInternal, atLeast(1)).getOwnerUid(eq(UID));
+ verify(mBatteryStatsInternal, atLeast(1)).getOwnerUid(eq(WORK_SOURCE_UID_1));
+
+ // ACQUIRE before RELEASE
+ InOrder inOrder1 = inOrder(mLogger);
+ inOrder1.verify(mLogger)
+ .wakelockStateChanged(
+ eq(OWNER_UID),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ inOrder1.verify(mLogger)
+ .wakelockStateChanged(
+ eq(OWNER_UID),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+
+ InOrder inOrder2 = inOrder(mLogger);
+ inOrder2.verify(mLogger)
+ .wakelockStateChanged(
+ eq(OWNER_WORK_SOURCE_UID_1),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ inOrder2.verify(mLogger)
+ .wakelockStateChanged(
+ eq(OWNER_WORK_SOURCE_UID_1),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ }
+
+ @Test
+ public void testOnWakeLockReleased_FrameworkStatsLogged_MultipleWorkSourceUids() {
+ // UIDs stored directly in WorkSource
+ WorkSource ws = new WorkSource(WORK_SOURCE_UID_1);
+ ws.add(WORK_SOURCE_UID_2);
+ testWorkSource(ws);
+
+ InOrder inOrder = inOrder(mLogger);
+ ArgumentCaptor<Integer> captorInt = ArgumentCaptor.forClass(int.class);
+
+ // ACQUIRE
+ inOrder.verify(mLogger, times(2))
+ .wakelockStateChanged(
+ /* uid= */ captorInt.capture(),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ assertThat(captorInt.getAllValues())
+ .containsExactly(OWNER_WORK_SOURCE_UID_1, OWNER_WORK_SOURCE_UID_2);
+
+ // RELEASE
+ captorInt = ArgumentCaptor.forClass(int.class);
+ inOrder.verify(mLogger, times(2))
+ .wakelockStateChanged(
+ /* uid= */ captorInt.capture(),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ assertThat(captorInt.getAllValues())
+ .containsExactly(OWNER_WORK_SOURCE_UID_1, OWNER_WORK_SOURCE_UID_2);
+ }
+
+ @Test
+ public void testOnWakeLockReleased_FrameworkStatsLogged_OneChain() {
+ // UIDs stored in a WorkChain of the WorkSource
+ WorkSource ws = new WorkSource();
+ WorkChain wc = ws.createWorkChain();
+ wc.addNode(WORK_SOURCE_UID_1, "tag1");
+ wc.addNode(WORK_SOURCE_UID_2, "tag2");
+ testWorkSource(ws);
+
+ WorkChain expectedWorkChain = new WorkChain();
+ expectedWorkChain.addNode(OWNER_WORK_SOURCE_UID_1, "tag1");
+ expectedWorkChain.addNode(OWNER_WORK_SOURCE_UID_2, "tag2");
+
+ InOrder inOrder = inOrder(mLogger);
+
+ // ACQUIRE
+ inOrder.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ // RELEASE
+ inOrder.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ }
+
+ @Test
+ public void testOnWakeLockReleased_FrameworkStatsLogged_OneUid_OneChain() {
+ WorkSource ws = new WorkSource(WORK_SOURCE_UID_1);
+ WorkChain wc = ws.createWorkChain();
+ wc.addNode(WORK_SOURCE_UID_2, "someTag");
+ testWorkSource(ws);
+
+ WorkChain expectedWorkChain = new WorkChain();
+ expectedWorkChain.addNode(OWNER_WORK_SOURCE_UID_2, "someTag");
+
+ InOrder inOrder1 = inOrder(mLogger);
+ InOrder inOrder2 = inOrder(mLogger);
+
+ // ACQUIRE
+ inOrder1.verify(mLogger)
+ .wakelockStateChanged(
+ eq(OWNER_WORK_SOURCE_UID_1),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ inOrder2.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ // RELEASE
+ inOrder1.verify(mLogger)
+ .wakelockStateChanged(
+ eq(OWNER_WORK_SOURCE_UID_1),
+ eq("wakelockTag"),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ inOrder2.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ }
+
+ @Test
+ public void testOnWakeLockReleased_FrameworkStatsLogged_TwoChains() {
+ // UIDs stored in a WorkChain of the WorkSource
+ WorkSource ws = new WorkSource();
+ WorkChain wc1 = ws.createWorkChain();
+ wc1.addNode(WORK_SOURCE_UID_1, "tag1");
+
+ WorkChain wc2 = ws.createWorkChain();
+ wc2.addNode(WORK_SOURCE_UID_2, "tag2");
+
+ testWorkSource(ws);
+
+ WorkChain expectedWorkChain1 = new WorkChain();
+ expectedWorkChain1.addNode(OWNER_WORK_SOURCE_UID_1, "tag1");
+
+ WorkChain expectedWorkChain2 = new WorkChain();
+ expectedWorkChain2.addNode(OWNER_WORK_SOURCE_UID_2, "tag2");
+
+ InOrder inOrder1 = inOrder(mLogger);
+ InOrder inOrder2 = inOrder(mLogger);
+
+ // ACQUIRE
+ inOrder1.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain1),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+ inOrder2.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain2),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.ACQUIRE));
+
+ // RELEASE
+ inOrder1.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain1),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ inOrder2.verify(mLogger)
+ .wakelockStateChanged(
+ eq("wakelockTag"),
+ eq(expectedWorkChain2),
+ eq(PowerManager.PARTIAL_WAKE_LOCK),
+ eq(WakelockEventType.RELEASE));
+ }
+
+ @Test
public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
@@ -708,22 +967,33 @@ public class NotifierTest {
}
private void createNotifier() {
- Notifier.Injector injector = new Notifier.Injector() {
- @Override
- public long currentTimeMillis() {
- return 1;
- }
-
- @Override
- public WakeLockLog getWakeLockLog(Context context) {
- return mWakeLockLog;
- }
-
- @Override
- public AppOpsManager getAppOpsManager(Context context) {
- return mAppOpsManager;
- }
- };
+ Notifier.Injector injector =
+ new Notifier.Injector() {
+ @Override
+ public long currentTimeMillis() {
+ return 1;
+ }
+
+ @Override
+ public WakeLockLog getWakeLockLog(Context context) {
+ return mWakeLockLog;
+ }
+
+ @Override
+ public AppOpsManager getAppOpsManager(Context context) {
+ return mAppOpsManager;
+ }
+
+ @Override
+ public FrameworkStatsLogger getFrameworkStatsLogger() {
+ return mLogger;
+ }
+
+ @Override
+ public BatteryStatsInternal getBatteryStatsInternal() {
+ return mBatteryStatsInternal;
+ }
+ };
mNotifier = new Notifier(
mTestLooper.getLooper(),
@@ -760,4 +1030,38 @@ public class NotifierTest {
}
}
+ private void testWorkSource(WorkSource ws) {
+ when(mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()).thenReturn(true);
+ createNotifier();
+ clearInvocations(
+ mBatteryStatsInternal, mLogger, mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ when(mBatteryStatsInternal.getOwnerUid(WORK_SOURCE_UID_1))
+ .thenReturn(OWNER_WORK_SOURCE_UID_1);
+ when(mBatteryStatsInternal.getOwnerUid(WORK_SOURCE_UID_2))
+ .thenReturn(OWNER_WORK_SOURCE_UID_2);
+
+ mNotifier.onWakeLockAcquired(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "wakelockTag",
+ "my.package.name",
+ UID,
+ PID,
+ ws,
+ /* historyTag= */ null,
+ /* callback= */ null);
+
+ mNotifier.onWakeLockReleased(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "wakelockTag",
+ "my.package.name",
+ UID,
+ PID,
+ ws,
+ /* historyTag= */ null,
+ /* callback= */ null);
+
+ verify(mBatteryStatsInternal, atLeast(1)).getOwnerUid(eq(WORK_SOURCE_UID_1));
+ verify(mBatteryStatsInternal, atLeast(1)).getOwnerUid(eq(WORK_SOURCE_UID_2));
+ }
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index 648da6530eb0..4e29e74651b6 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -49,17 +49,23 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
import android.os.PowerManager;
import android.os.PowerSaveState;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.Display;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.LatencyTracker;
+import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.power.feature.PowerManagerFlags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -73,6 +79,9 @@ import org.mockito.MockitoAnnotations;
public class PowerGroupTest {
private static final int GROUP_ID = 0;
+ private static final int NON_DEFAULT_GROUP_ID = 1;
+ private static final int NON_DEFAULT_DISPLAY_ID = 2;
+ private static final int VIRTUAL_DEVICE_ID = 3;
private static final int UID = 11;
private static final long TIMESTAMP_CREATE = 1;
private static final long TIMESTAMP1 = 999;
@@ -87,10 +96,16 @@ public class PowerGroupTest {
private static final LatencyTracker LATENCY_TRACKER = LatencyTracker.getInstance(
InstrumentationRegistry.getInstrumentation().getContext());
+ private static final long DEFAULT_TIMEOUT = 1234L;
+
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private PowerGroup mPowerGroup;
@Mock private PowerGroup.PowerGroupListener mWakefulnessCallbackMock;
@Mock private Notifier mNotifier;
@Mock private DisplayManagerInternal mDisplayManagerInternal;
+ @Mock private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
@Mock private PowerManagerFlags mFeatureFlags;
@@ -740,4 +755,111 @@ public class PowerGroupTest {
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
}
+
+ @Test
+ public void testTimeoutsOverride_defaultGroup_noOverride() {
+ assertThat(mPowerGroup.getScreenDimDurationOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(DEFAULT_TIMEOUT);
+ assertThat(mPowerGroup.getScreenOffTimeoutOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(DEFAULT_TIMEOUT);
+ }
+
+ @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @Test
+ public void testTimeoutsOverride_noVdm_noOverride() {
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class, null);
+
+ mPowerGroup = new PowerGroup(NON_DEFAULT_GROUP_ID, mWakefulnessCallbackMock, mNotifier,
+ mDisplayManagerInternal, WAKEFULNESS_AWAKE, /* ready= */ true,
+ /* supportsSandman= */ true, TIMESTAMP_CREATE, mFeatureFlags);
+
+ assertThat(mPowerGroup.getScreenDimDurationOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(DEFAULT_TIMEOUT);
+ assertThat(mPowerGroup.getScreenOffTimeoutOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(DEFAULT_TIMEOUT);
+ }
+
+ @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @Test
+ public void testTimeoutsOverride_notValidVirtualDeviceId_noOverride() {
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternal);
+
+ when(mDisplayManagerInternal.getDisplayIdsForGroup(NON_DEFAULT_GROUP_ID))
+ .thenReturn(new int[] {NON_DEFAULT_DISPLAY_ID});
+ when(mVirtualDeviceManagerInternal.getDeviceIdForDisplayId(NON_DEFAULT_DISPLAY_ID))
+ .thenReturn(Context.DEVICE_ID_DEFAULT);
+ when(mVirtualDeviceManagerInternal.isValidVirtualDeviceId(Context.DEVICE_ID_DEFAULT))
+ .thenReturn(false);
+
+ mPowerGroup = new PowerGroup(NON_DEFAULT_GROUP_ID, mWakefulnessCallbackMock, mNotifier,
+ mDisplayManagerInternal, WAKEFULNESS_AWAKE, /* ready= */ true,
+ /* supportsSandman= */ true, TIMESTAMP_CREATE, mFeatureFlags);
+
+ assertThat(mPowerGroup.getScreenDimDurationOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(DEFAULT_TIMEOUT);
+ assertThat(mPowerGroup.getScreenOffTimeoutOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(DEFAULT_TIMEOUT);
+ }
+
+ @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @Test
+ public void testTimeoutsOverride_validVirtualDeviceId_timeoutsAreOverridden() {
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternal);
+
+ final long dimDurationOverride = DEFAULT_TIMEOUT * 3;
+ final long screenOffTimeoutOverride = DEFAULT_TIMEOUT * 5;
+
+ when(mDisplayManagerInternal.getDisplayIdsForGroup(NON_DEFAULT_GROUP_ID))
+ .thenReturn(new int[] {NON_DEFAULT_DISPLAY_ID});
+ when(mVirtualDeviceManagerInternal.getDeviceIdForDisplayId(NON_DEFAULT_DISPLAY_ID))
+ .thenReturn(VIRTUAL_DEVICE_ID);
+ when(mVirtualDeviceManagerInternal.isValidVirtualDeviceId(VIRTUAL_DEVICE_ID))
+ .thenReturn(true);
+ when(mVirtualDeviceManagerInternal.getDimDurationMillisForDeviceId(VIRTUAL_DEVICE_ID))
+ .thenReturn(dimDurationOverride);
+ when(mVirtualDeviceManagerInternal.getScreenOffTimeoutMillisForDeviceId(VIRTUAL_DEVICE_ID))
+ .thenReturn(screenOffTimeoutOverride);
+
+ mPowerGroup = new PowerGroup(NON_DEFAULT_GROUP_ID, mWakefulnessCallbackMock, mNotifier,
+ mDisplayManagerInternal, WAKEFULNESS_AWAKE, /* ready= */ true,
+ /* supportsSandman= */ true, TIMESTAMP_CREATE, mFeatureFlags);
+
+ assertThat(mPowerGroup.getScreenDimDurationOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(dimDurationOverride);
+ assertThat(mPowerGroup.getScreenOffTimeoutOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(screenOffTimeoutOverride);
+ }
+
+ @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @Test
+ public void testTimeoutsOverrides_dimDurationIsCapped() {
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternal);
+
+ final long dimDurationOverride = DEFAULT_TIMEOUT * 5;
+ final long screenOffTimeoutOverride = DEFAULT_TIMEOUT * 3;
+
+ when(mDisplayManagerInternal.getDisplayIdsForGroup(NON_DEFAULT_GROUP_ID))
+ .thenReturn(new int[] {NON_DEFAULT_DISPLAY_ID});
+ when(mVirtualDeviceManagerInternal.getDeviceIdForDisplayId(NON_DEFAULT_DISPLAY_ID))
+ .thenReturn(VIRTUAL_DEVICE_ID);
+ when(mVirtualDeviceManagerInternal.isValidVirtualDeviceId(VIRTUAL_DEVICE_ID))
+ .thenReturn(true);
+ when(mVirtualDeviceManagerInternal.getDimDurationMillisForDeviceId(VIRTUAL_DEVICE_ID))
+ .thenReturn(dimDurationOverride);
+ when(mVirtualDeviceManagerInternal.getScreenOffTimeoutMillisForDeviceId(VIRTUAL_DEVICE_ID))
+ .thenReturn(screenOffTimeoutOverride);
+
+ mPowerGroup = new PowerGroup(NON_DEFAULT_GROUP_ID, mWakefulnessCallbackMock, mNotifier,
+ mDisplayManagerInternal, WAKEFULNESS_AWAKE, /* ready= */ true,
+ /* supportsSandman= */ true, TIMESTAMP_CREATE, mFeatureFlags);
+
+ assertThat(mPowerGroup.getScreenDimDurationOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(screenOffTimeoutOverride);
+ assertThat(mPowerGroup.getScreenOffTimeoutOverrideLocked(DEFAULT_TIMEOUT))
+ .isEqualTo(screenOffTimeoutOverride);
+ }
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index b10200daeeb4..359cf6376239 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -85,6 +85,7 @@ import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -102,6 +103,7 @@ import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.lights.LightsManager;
import com.android.server.policy.WindowManagerPolicy;
@@ -167,6 +169,7 @@ public class PowerManagerServiceTest {
@Mock private BatteryManagerInternal mBatteryManagerInternalMock;
@Mock private ActivityManagerInternal mActivityManagerInternalMock;
@Mock private AttentionManagerInternal mAttentionManagerInternalMock;
+ @Mock private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
@Mock private DreamManagerInternal mDreamManagerInternalMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@Mock private FoldGracePeriodProvider mFoldGracePeriodProvider;
@@ -246,6 +249,7 @@ public class PowerManagerServiceTest {
addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
+ addLocalServiceMock(VirtualDeviceManagerInternal.class, mVirtualDeviceManagerInternalMock);
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResourcesSpy = spy(mContextSpy.getResources());
@@ -1200,6 +1204,59 @@ public class PowerManagerServiceTest {
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
}
+ @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testNonDefaultDisplayGroupWithCustomTimeout_afterTimeout_goesToDozing() {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final int nonDefaultDisplayId = Display.DEFAULT_DISPLAY + 2;
+ final int virtualDeviceId = Context.DEVICE_ID_DEFAULT + 3;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = nonDefaultDisplayGroupId;
+ when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplayId)).thenReturn(info);
+ when(mDisplayManagerInternalMock.getDisplayIdsForGroup(nonDefaultDisplayGroupId))
+ .thenReturn(new int[] {nonDefaultDisplayId});
+ when(mVirtualDeviceManagerInternalMock.getDeviceIdForDisplayId(nonDefaultDisplayId))
+ .thenReturn(virtualDeviceId);
+ when(mVirtualDeviceManagerInternalMock.isValidVirtualDeviceId(virtualDeviceId))
+ .thenReturn(true);
+ when(mVirtualDeviceManagerInternalMock
+ .getScreenOffTimeoutMillisForDeviceId(virtualDeviceId))
+ .thenReturn(20000L);
+
+ setMinimumScreenOffTimeoutConfig(10000);
+ createService();
+ startSystem();
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP))
+ .isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId))
+ .isEqualTo(WAKEFULNESS_AWAKE);
+
+ // The default timeout is 10s, the custom group timeout is 20s.
+
+ advanceTime(15000);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP))
+ .isEqualTo(WAKEFULNESS_ASLEEP);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId))
+ .isEqualTo(WAKEFULNESS_AWAKE);
+
+ advanceTime(10000);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP))
+ .isEqualTo(WAKEFULNESS_ASLEEP);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId))
+ .isEqualTo(WAKEFULNESS_DOZING);
+ }
+
@SuppressWarnings("GuardedBy")
@Test
public void testAmbientSuppression_disablesDreamingAndWakesDevice() {
@@ -3348,7 +3405,8 @@ public class PowerManagerServiceTest {
null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY,
null /* callback */);
}
- assertThat(mService.getScreenOffTimeoutOverrideLocked(screenTimeout, screenDimTimeout))
+ assertThat(mService.getDefaultGroupScreenOffTimeoutOverrideLocked(screenTimeout,
+ screenDimTimeout))
.isEqualTo(expect[2]);
if (acquireWakeLock) {
mService.getBinderServiceInstance().releaseWakeLock(token, 0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 0a1fc3184fca..d83dc110800a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -67,6 +67,7 @@ import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelSingleUidTimeReader;
import com.android.internal.os.LongArrayMultiStateCounter;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.server.power.feature.flags.Flags;
@@ -120,6 +121,7 @@ public class BatteryStatsImplTest {
}});
private final MockClock mMockClock = new MockClock();
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(777666, mMockClock);
private MockBatteryStatsImpl mBatteryStatsImpl;
private Handler mHandler;
private PowerStatsStore mPowerStatsStore;
@@ -160,7 +162,7 @@ public class BatteryStatsImplTest {
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerAttributor,
mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore, 0,
- mMockClock);
+ mMockClock, mMonotonicClock);
}
@Test
@@ -438,10 +440,7 @@ public class BatteryStatsImplTest {
doAnswer(invocation -> {
LongArrayMultiStateCounter counter = invocation.getArgument(1);
long timestampMs = invocation.getArgument(2);
- LongArrayMultiStateCounter.LongArrayContainer container =
- new LongArrayMultiStateCounter.LongArrayContainer(NUM_CPU_FREQS);
- container.setValues(cpuTimes);
- counter.updateValues(container, timestampMs);
+ counter.updateValues(cpuTimes, timestampMs);
return null;
}).when(mKernelSingleUidTimeReader).addDelta(eq(testUid),
any(LongArrayMultiStateCounter.class), anyLong());
@@ -451,20 +450,13 @@ public class BatteryStatsImplTest {
doAnswer(invocation -> {
LongArrayMultiStateCounter counter = invocation.getArgument(1);
long timestampMs = invocation.getArgument(2);
- LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
- invocation.getArgument(3);
-
- LongArrayMultiStateCounter.LongArrayContainer container =
- new LongArrayMultiStateCounter.LongArrayContainer(NUM_CPU_FREQS);
- container.setValues(cpuTimes);
- counter.updateValues(container, timestampMs);
- if (deltaContainer != null) {
- deltaContainer.setValues(delta);
- }
+ long[] deltaOut = invocation.getArgument(3);
+ counter.updateValues(cpuTimes, timestampMs);
+ System.arraycopy(delta, 0, deltaOut, 0, delta.length);
return null;
}).when(mKernelSingleUidTimeReader).addDelta(eq(testUid),
any(LongArrayMultiStateCounter.class), anyLong(),
- any(LongArrayMultiStateCounter.LongArrayContainer.class));
+ any(long[].class));
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
index 813dd841b2b9..5d50e6c9f715 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
@@ -191,7 +191,7 @@ public class BatteryUsageStatsAtomTest {
"cpu",
1650.0f,
9300.0f,
- 8400L
+ 8300L
);
verify(statsLogger).buildStatsEvent(
1000L,
@@ -205,7 +205,7 @@ public class BatteryUsageStatsAtomTest {
"cpu",
1650.0f,
9400.0f,
- 0L
+ 8400L
);
verify(statsLogger).buildStatsEvent(
1000L,
@@ -502,17 +502,17 @@ public class BatteryUsageStatsAtomTest {
.setPackageWithHighestDrain("myPackage0")
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 2000)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 400)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 450)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 500)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 600)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 800);
final BatteryConsumer.Key keyFg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
@@ -524,14 +524,14 @@ public class BatteryUsageStatsAtomTest {
final BatteryConsumer.Key keyCached = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_CACHED);
- uidBuilder.setConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(keyFg, 8100)
- .setConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setUsageDurationMillis(keyBg, 8200)
- .setConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setUsageDurationMillis(keyFgs, 8300)
- .setConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setUsageDurationMillis(keyFgs, 8400);
+ uidBuilder.addConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .addUsageDurationMillis(keyFg, 8100)
+ .addConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addUsageDurationMillis(keyBg, 8200)
+ .addConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addUsageDurationMillis(keyFgs, 8300)
+ .addConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
+ .addUsageDurationMillis(keyCached, 8400);
final BatteryConsumer.Key keyCustomFg = uidBuilder.getKey(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
@@ -539,9 +539,9 @@ public class BatteryUsageStatsAtomTest {
final BatteryConsumer.Key keyCustomBg = uidBuilder.getKey(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
- uidBuilder.setConsumedPower(
+ uidBuilder.addConsumedPower(
keyCustomFg, 100, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
- uidBuilder.setConsumedPower(
+ uidBuilder.addConsumedPower(
keyCustomBg, 350, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
@@ -549,36 +549,36 @@ public class BatteryUsageStatsAtomTest {
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1234);
builder.getOrCreateUidBatteryConsumerBuilder(UID_2)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
766);
builder.getOrCreateUidBatteryConsumerBuilder(UID_3);
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(30000)
- .setConsumedPower(
+ .addConsumedPower(30000)
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 20100,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_AUDIO, 0,
BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 20300)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
// Not used; just to make sure extraneous data doesn't mess things up.
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 10100,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
return builder.build();
@@ -596,8 +596,8 @@ public class BatteryUsageStatsAtomTest {
BatteryConsumer.PROCESS_STATE_FOREGROUND, 1 * 60 * 1000)
.setTimeInProcessStateMs(
BatteryConsumer.PROCESS_STATE_BACKGROUND, 2 * 60 * 1000)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
}
// Add a UID with much larger battery footprint
@@ -605,16 +605,16 @@ public class BatteryUsageStatsAtomTest {
builder.getOrCreateUidBatteryConsumerBuilder(largeConsumerUid)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 10 * 60 * 1000)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 20 * 60 * 1000)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
// Add a UID with much larger usage duration
final int highUsageUid = 3002;
builder.getOrCreateUidBatteryConsumerBuilder(highUsageUid)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 60 * 60 * 1000)
.setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 120 * 60 * 1000)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
BatteryUsageStats batteryUsageStats = builder.build();
final byte[] bytes = batteryUsageStats.getStatsProto();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 8239e0955032..709f83ba907d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import com.android.server.power.stats.processor.MultiStatePowerAttributor;
@@ -80,6 +81,7 @@ public class BatteryUsageStatsProviderTest {
.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
private MockClock mMockClock = mStatsRule.getMockClock();
+ private MonotonicClock mMonotonicClock = new MonotonicClock(666777, mMockClock);
private Context mContext;
@Before
@@ -146,7 +148,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -273,7 +276,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
powerAttributor, mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
return provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
}
@@ -303,7 +307,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -396,7 +401,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
final BatteryUsageStats batteryUsageStats =
provider.getBatteryUsageStats(batteryStats,
@@ -487,7 +493,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock,
+ mMonotonicClock);
batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore,
/* accumulateBatteryUsageStats */ false);
@@ -590,7 +597,10 @@ public class BatteryUsageStatsProviderTest {
private void accumulateBatteryUsageStats(int accumulatedBatteryUsageStatsSpanSize,
int expectedNumberOfUpdates) throws Throwable {
- BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl batteryStats = spy(mStatsRule.getBatteryStats());
+ // Note - these two are in microseconds
+ when(batteryStats.computeBatteryTimeRemaining(anyLong())).thenReturn(111_000L);
+ when(batteryStats.computeChargeTimeRemaining(anyLong())).thenReturn(777_000L);
batteryStats.forceRecordAllHistory();
setTime(5 * MINUTE_IN_MS);
@@ -623,7 +633,7 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
powerAttributor, mStatsRule.getPowerProfile(),
mStatsRule.getCpuScalingPolicies(), powerStatsStore,
- accumulatedBatteryUsageStatsSpanSize, mMockClock);
+ accumulatedBatteryUsageStatsSpanSize, mMockClock, mMonotonicClock);
provider.accumulateBatteryUsageStatsAsync(batteryStats, mStatsRule.getHandler());
@@ -677,9 +687,14 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().accumulated().build());
+
assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
assertThat(stats.getStatsEndTimestamp()).isEqualTo(115 * MINUTE_IN_MS);
+ assertThat(stats.getBatteryTimeRemainingMs()).isEqualTo(111);
+ assertThat(stats.getChargeTimeRemainingMs()).isEqualTo(777);
+ assertThat(stats.getBatteryCapacity()).isEqualTo(4000); // from PowerProfile
+
// Total: 10 + 20 + 30 = 60
assertThat(stats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
@@ -729,7 +744,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock,
+ mMonotonicClock);
PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
doAnswer(invocation -> {
@@ -796,7 +812,8 @@ public class BatteryUsageStatsProviderTest {
BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
- mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock);
+ mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock,
+ mMonotonicClock);
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(0, 3000)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 1b6b8c49461e..9771da590e37 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -119,7 +119,7 @@ public class BatteryUsageStatsTest {
BatteryStatsImpl.Uid mockUid = mock(BatteryStatsImpl.Uid.class);
when(mockUid.getUid()).thenReturn(i);
builder.getOrCreateUidBatteryConsumerBuilder(mockUid)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100);
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100);
}
BatteryUsageStats outBatteryUsageStats = builder.build();
@@ -355,13 +355,13 @@ public class BatteryUsageStatsTest {
if (includeUserBatteryConsumer) {
builder.getOrCreateUserBatteryConsumerBuilder(USER_ID)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 10)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 30)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
}
return builder;
@@ -422,15 +422,15 @@ public class BatteryUsageStatsTest {
.setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, timeInProcessStateBackground)
.setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
timeInProcessStateForegroundService)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
if (builder.isProcessStateDataNeeded()) {
final BatteryConsumer.Key cpuFgKey = builder.isScreenStateDataNeeded()
@@ -461,21 +461,21 @@ public class BatteryUsageStatsTest {
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
BatteryConsumer.PROCESS_STATE_BACKGROUND);
uidBuilder
- .setConsumedPower(cpuFgKey, cpuPowerForeground,
+ .addConsumedPower(cpuFgKey, cpuPowerForeground,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuFgKey, cpuDurationForeground)
- .setConsumedPower(cpuBgKey, cpuPowerBackground,
+ .addUsageDurationMillis(cpuFgKey, cpuDurationForeground)
+ .addConsumedPower(cpuBgKey, cpuPowerBackground,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuBgKey, cpuDurationBackground)
- .setConsumedPower(cpuFgsKey, cpuPowerFgs,
+ .addUsageDurationMillis(cpuBgKey, cpuDurationBackground)
+ .addConsumedPower(cpuFgsKey, cpuPowerFgs,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuFgsKey, cpuDurationFgs)
- .setConsumedPower(cachedKey, cpuPowerCached,
+ .addUsageDurationMillis(cpuFgsKey, cpuDurationFgs)
+ .addConsumedPower(cachedKey, cpuPowerCached,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cachedKey, cpuDurationCached)
- .setConsumedPower(customBgKey, customComponentPower,
+ .addUsageDurationMillis(cachedKey, cpuDurationCached)
+ .addConsumedPower(customBgKey, customComponentPower,
BatteryConsumer.POWER_MODEL_UNDEFINED)
- .setUsageDurationMillis(customBgKey, customComponentDuration);
+ .addUsageDurationMillis(customBgKey, customComponentDuration);
}
}
@@ -486,15 +486,15 @@ public class BatteryUsageStatsTest {
long cpuDurationChgScrOn, double cpuPowerChgScrOff, long cpuDurationChgScrOff) {
final AggregateBatteryConsumer.Builder aggBuilder =
builder.getAggregateBatteryConsumerBuilder(scope)
- .setConsumedPower(consumedPower)
- .setConsumedPower(
+ .addConsumedPower(consumedPower)
+ .addConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
- .setConsumedPower(
+ .addConsumedPower(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
customComponentPower)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
- .setUsageDurationMillis(
+ .addUsageDurationMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
customComponentDuration);
if (builder.isPowerStateDataNeeded() || builder.isScreenStateDataNeeded()) {
@@ -519,18 +519,18 @@ public class BatteryUsageStatsTest {
BatteryConsumer.SCREEN_STATE_OTHER,
BatteryConsumer.POWER_STATE_OTHER);
aggBuilder
- .setConsumedPower(cpuBatScrOn, cpuPowerBatScrOn,
+ .addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn)
- .setConsumedPower(cpuBatScrOff, cpuPowerBatScrOff,
+ .addUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn)
+ .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff)
- .setConsumedPower(cpuChgScrOn, cpuPowerChgScrOn,
+ .addUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff)
+ .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn)
- .setConsumedPower(cpuChgScrOff, cpuPowerChgScrOff,
+ .addUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn)
+ .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff,
BatteryConsumer.POWER_MODEL_POWER_PROFILE)
- .setUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff);
+ .addUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff);
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
index 71a65c85d9e1..4cea72835fba 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
@@ -394,10 +394,7 @@ public class CpuPowerCalculatorTest {
doAnswer(invocation -> {
LongArrayMultiStateCounter counter = invocation.getArgument(1);
long timestampMs = invocation.getArgument(2);
- LongArrayMultiStateCounter.LongArrayContainer container =
- new LongArrayMultiStateCounter.LongArrayContainer(NUM_CPU_FREQS);
- container.setValues(cpuTimes);
- counter.updateValues(container, timestampMs);
+ counter.updateValues(cpuTimes, timestampMs);
return null;
}).when(mMockKernelSingleUidTimeReader).addDelta(eq(uid),
any(LongArrayMultiStateCounter.class), anyLong());
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 359755a7ad08..f1656678d3bd 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -96,7 +96,10 @@ android_test {
"CtsVirtualDeviceCommonLib",
"com_android_server_accessibility_flags_lib",
"locksettings_flags_lib",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ "true": ["service-crashrecovery.impl"],
+ default: [],
+ }),
libs: [
"android.hardware.power-V1-java",
@@ -157,21 +160,49 @@ android_test {
resource_zips: [":FrameworksServicesTests_apks_as_resources"],
}
-android_ravenwood_test {
- name: "FrameworksServicesTestsRavenwood",
+java_defaults {
+ name: "FrameworksServicesTestsRavenwood-defaults",
libs: [
"android.test.mock.stubs.system",
],
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
- "services.core",
"flag-junit",
],
+ auto_gen_config: true,
+}
+
+// Unit tests for UriGrantManager, running on ravenwood.
+// Note UriGrantManager does not support Ravenwood (yet). We're just running the original
+// unit tests as is on Ravenwood. So here, we use the original "services.core", because
+// "services.core.ravenwood" doesn't have the target code.
+// (Compare to FrameworksServicesTestsRavenwood_Compat, which does support Ravenwood.)
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood_Uri",
+ defaults: ["FrameworksServicesTestsRavenwood-defaults"],
+ team: "trendy_team_ravenwood",
+ static_libs: [
+ "services.core",
+ ],
srcs: [
"src/com/android/server/uri/**/*.java",
],
- auto_gen_config: true,
+}
+
+// Unit tests for compat-framework.
+// Compat-framework does support Ravenwood, and it uses the ravenwood anottations,
+// so we link "services.core.ravenwood".
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood_Compat",
+ defaults: ["FrameworksServicesTestsRavenwood-defaults"],
+ team: "trendy_team_ravenwood",
+ static_libs: [
+ "services.core.ravenwood",
+ ],
+ srcs: [
+ "src/com/android/server/compat/**/*.java",
+ ],
}
java_library {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index c645c0852f1b..9b7bbe04132c 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -114,6 +114,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
+ <uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" />
<queries>
<package android:name="com.android.servicestests.apps.suspendtestapp" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 2edde9b74d0a..d5b930769e43 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -33,6 +33,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE;
@@ -80,6 +81,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -2183,6 +2185,168 @@ public class AccessibilityManagerServiceTest {
verify(mockUserContext).getSystemService(EnhancedConfirmationManager.class);
}
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void handleKeyGestureEvent_toggleMagnifier() {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).containsExactly(MAGNIFICATION_CONTROLLER_NAME);
+
+ // The magnifier will only be toggled on the second event received since the first is
+ // used to toggle the feature on.
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ verify(mInputFilter).notifyMagnificationShortcutTriggered(anyInt());
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void handleKeyGestureEvent_activateSelectToSpeak_trustedService() {
+ setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON);
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
+
+ final AccessibilityServiceInfo trustedService = mockAccessibilityServiceInfo(
+ new ComponentName("package_a", "class_a"),
+ /* isSystemApp= */ true, /* isAlwaysOnService= */ true);
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.add(trustedService);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultSelectToSpeakService,
+ trustedService.getComponentName().flattenToString());
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_trustedAccessibilityServices,
+ new String[]{trustedService.getComponentName().flattenToString()});
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).containsExactly(
+ trustedService.getComponentName().flattenToString());
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void handleKeyGestureEvent_activateSelectToSpeak_preinstalledService() {
+ setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON);
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
+
+ final AccessibilityServiceInfo untrustedService = mockAccessibilityServiceInfo(
+ new ComponentName("package_a", "class_a"),
+ /* isSystemApp= */ true, /* isAlwaysOnService= */ true);
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.add(untrustedService);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultSelectToSpeakService,
+ untrustedService.getComponentName().flattenToString());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void handleKeyGestureEvent_activateSelectToSpeak_downloadedService() {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
+
+ final AccessibilityServiceInfo downloadedService = mockAccessibilityServiceInfo(
+ new ComponentName("package_a", "class_a"),
+ /* isSystemApp= */ false, /* isAlwaysOnService= */ true);
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.add(downloadedService);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultSelectToSpeakService,
+ downloadedService.getComponentName().flattenToString());
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_trustedAccessibilityServices,
+ new String[]{downloadedService.getComponentName().flattenToString()});
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void handleKeyGestureEvent_activateSelectToSpeak_defaultNotInstalled() {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
+
+ final AccessibilityServiceInfo installedService = mockAccessibilityServiceInfo(
+ new ComponentName("package_a", "class_a"),
+ /* isSystemApp= */ true, /* isAlwaysOnService= */ true);
+ final AccessibilityServiceInfo defaultService = mockAccessibilityServiceInfo(
+ new ComponentName("package_b", "class_b"),
+ /* isSystemApp= */ true, /* isAlwaysOnService= */ true);
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.add(installedService);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultSelectToSpeakService,
+ defaultService.getComponentName().flattenToString());
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_trustedAccessibilityServices,
+ new String[]{defaultService.getComponentName().flattenToString()});
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
+ public void handleKeyGestureEvent_activateSelectToSpeak_noDefault() {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
+
+ final AccessibilityServiceInfo installedService = mockAccessibilityServiceInfo(
+ new ComponentName("package_a", "class_a"),
+ /* isSystemApp= */ true, /* isAlwaysOnService= */ true);
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.add(installedService);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_trustedAccessibilityServices,
+ new String[]{installedService.getComponentName().flattenToString()});
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+
+ mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+
+ assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
+ mA11yms.getCurrentUserIdLocked())).isEmpty();
+ }
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
@@ -2298,6 +2462,10 @@ public class AccessibilityManagerServiceTest {
AccessibilityManagerService service) {
super(context, service);
}
+
+ @Override
+ void notifyMagnificationShortcutTriggered(int displayId) {
+ }
}
private static class A11yTestableContext extends TestableContext {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 8c35925debff..cb52eef6adfe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -32,6 +32,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
@@ -174,6 +175,7 @@ public class AccessibilityUserStateTest {
mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), SOFTWARE);
mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), GESTURE);
mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), QUICK_SETTINGS);
+ mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), KEY_GESTURE);
mUserState.updateA11yTilesInQsPanelLocked(
Set.of(AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME));
mUserState.setTargetAssignedToAccessibilityButton(componentNameString);
@@ -201,6 +203,7 @@ public class AccessibilityUserStateTest {
assertTrue(mUserState.getShortcutTargetsLocked(SOFTWARE).isEmpty());
assertTrue(mUserState.getShortcutTargetsLocked(GESTURE).isEmpty());
assertTrue(mUserState.getShortcutTargetsLocked(QUICK_SETTINGS).isEmpty());
+ assertTrue(mUserState.getShortcutTargetsLocked(KEY_GESTURE).isEmpty());
assertTrue(mUserState.getA11yQsTilesInQsPanel().isEmpty());
assertNull(mUserState.getTargetAssignedToAccessibilityButton());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 8164ef9314d9..f0d3456a39de 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -19,9 +19,13 @@ package com.android.server.accessibility.magnification;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE;
+import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE;
import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
import static com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -660,6 +664,90 @@ public class MagnificationControllerTest {
}
@Test
+ public void scaleMagnificationByStep_fullscreenMode_stepInAndOut() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 1.0f, false);
+ reset(mScreenMagnificationController);
+
+ // Verify the zoom scale factor increases by
+ // {@code MagnificationController.DefaultMagnificationScaleStepProvider
+ // .ZOOM_STEP_SCALE_FACTOR} and the center coordinates are
+ // unchanged (Float.NaN as values denotes unchanged center).
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
+ verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+ eq(MagnificationController
+ .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR),
+ eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt());
+
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
+ verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+ eq(SCALE_MIN_VALUE), eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void scaleMagnificationByStep_testMaxScaling() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false);
+ reset(mScreenMagnificationController);
+
+ float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ while (currentScale < SCALE_MAX_VALUE) {
+ assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN)).isTrue();
+ final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ assertThat(nextScale).isGreaterThan(currentScale);
+ currentScale = nextScale;
+ }
+
+ assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE);
+ assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN)).isFalse();
+ }
+
+ @Test
+ public void scaleMagnificationByStep_testMinScaling() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MAX_VALUE, false);
+ reset(mScreenMagnificationController);
+
+ float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ while (currentScale > SCALE_MIN_VALUE) {
+ assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT)).isTrue();
+ final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ assertThat(nextScale).isLessThan(currentScale);
+ currentScale = nextScale;
+ }
+
+ assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE);
+ assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT)).isFalse();
+ }
+
+ @Test
+ public void scaleMagnificationByStep_windowedMode_stepInAndOut() throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false);
+ reset(mMagnificationConnectionManager);
+
+ // Verify the zoom scale factor increases by
+ // {@code MagnificationController.DefaultMagnificationScaleStepProvider
+ // .ZOOM_STEP_SCALE_FACTOR}.
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
+ verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY),
+ eq(MagnificationController
+ .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR));
+
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
+ verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY),
+ eq(SCALE_MIN_VALUE));
+ }
+
+ @Test
public void enableWindowMode_notifyMagnificationChanged() throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS b/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS
deleted file mode 100644
index 0218a7835586..000000000000
--- a/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/adaptiveauth/OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java
index c4b3c149bd8d..5d7ffe91e67d 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsRecentAccessPersistenceTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.app.PropertyInvalidatedCache;
import android.companion.virtual.VirtualDeviceManager;
import android.content.Context;
import android.os.FileUtils;
@@ -69,6 +70,7 @@ public class AppOpsRecentAccessPersistenceTest {
@Before
public void setUp() {
+ PropertyInvalidatedCache.disableForTestMode();
when(mAppOpCheckingService.addAppOpsModeChangedListener(any())).thenReturn(true);
LocalServices.addService(AppOpsCheckingServiceInterface.class, mAppOpCheckingService);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index c9e9f00985f1..96c6cbc9ec64 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.audio;
import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC;
+import static android.media.AudioManager.GET_DEVICES_INPUTS;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,7 +35,9 @@ import android.media.AudioSystem;
import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.EnableFlags;
+import android.util.IntArray;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -80,12 +83,15 @@ public class AudioServiceTest {
private static boolean sLooperPrepared = false;
+ private TestLooper mTestLooper;
+
@Before
public void setUp() throws Exception {
if (!sLooperPrepared) {
Looper.prepare();
sLooperPrepared = true;
}
+ mTestLooper = new TestLooper();
mContext = InstrumentationRegistry.getTargetContext();
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
@@ -93,8 +99,11 @@ public class AudioServiceTest {
when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer,
- mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, null,
- mMockAppOpsManager, mMockPermissionEnforcer, mMockPermissionProvider, r -> r.run());
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
+ mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer,
+ mMockPermissionProvider, r -> r.run());
+
+ mTestLooper.dispatchAll();
}
/**
@@ -114,7 +123,7 @@ public class AudioServiceTest {
Assert.assertEquals("mic mute reporting wrong value",
muted, mAudioService.isMicrophoneMuted());
// verify the intent for mic mute changed is supposed to be fired
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ mTestLooper.dispatchAll();
verify(mSpySystemServer, times(1))
.sendMicrophoneMuteChangedIntent();
reset(mSpySystemServer);
@@ -139,7 +148,7 @@ public class AudioServiceTest {
Assert.assertEquals("mic mute reporting wrong value",
!muted, mAudioService.isMicrophoneMuted());
// verify the intent for mic mute changed is supposed to be fired
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ mTestLooper.dispatchAll();
verify(mSpySystemServer, times(1))
.sendMicrophoneMuteChangedIntent();
reset(mSpySystemServer);
@@ -150,8 +159,7 @@ public class AudioServiceTest {
public void testRingNotifAlias() throws Exception {
Log.i(TAG, "running testRingNotifAlias");
Assert.assertNotNull(mAudioService);
- // TODO add initialization message that can be caught here instead of sleeping
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
+ mTestLooper.dispatchAll(); // wait for full AudioService initialization
// test with aliasing RING and NOTIFICATION
mAudioService.setNotifAliasRingForTest(true);
@@ -162,7 +170,7 @@ public class AudioServiceTest {
mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION,
ringVol, 0, "bla");
mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringMaxVol, 0, "bla");
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ mTestLooper.dispatchAll();
Assert.assertEquals(ringMaxVol,
mAudioService.getStreamVolume(AudioSystem.STREAM_NOTIFICATION));
@@ -216,7 +224,19 @@ public class AudioServiceTest {
public void testInputGainIndex() throws Exception {
Log.i(TAG, "running testInputGainIndex");
Assert.assertNotNull(mAudioService);
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
+
+ IntArray internalDeviceTypes = new IntArray();
+ int status = AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS, internalDeviceTypes);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, "AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS) failed. status:"
+ + status);
+ }
+
+ // Make sure TYPE_BUILTIN_MIC, aka DEVICE_IN_BUILTIN_MIC in terms of internal device type,
+ // is supported.
+ if (!internalDeviceTypes.contains(AudioSystem.DEVICE_IN_BUILTIN_MIC)) {
+ return;
+ }
AudioDeviceAttributes ada =
new AudioDeviceAttributes(
@@ -229,6 +249,8 @@ public class AudioServiceTest {
int inputGainIndex = 20;
mAudioService.setInputGainIndex(ada, inputGainIndex);
+ mTestLooper.dispatchAll();
+
Assert.assertEquals(
"input gain index reporting wrong value",
inputGainIndex,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index bc410d9168a9..88829c1a99b3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -62,6 +62,7 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -70,8 +71,16 @@ import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.keymaster.HardwareAuthenticatorType;
import android.os.Binder;
import android.os.Handler;
@@ -84,6 +93,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
import android.security.GateKeeper;
import android.security.KeyStoreAuthorization;
import android.service.gatekeeper.IGateKeeperService;
@@ -93,6 +103,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -111,6 +122,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -144,6 +156,27 @@ public class BiometricServiceTest {
private static final int SENSOR_ID_FINGERPRINT = 0;
private static final int SENSOR_ID_FACE = 1;
+ private final ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback.Stub>
+ mFingerprintAuthenticatorRegisteredCallbackCaptor = ArgumentCaptor.forClass(
+ IFingerprintAuthenticatorsRegisteredCallback.Stub.class);
+ private final ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback.Stub>
+ mFaceAuthenticatorRegisteredCallbackCaptor = ArgumentCaptor.forClass(
+ IFaceAuthenticatorsRegisteredCallback.Stub.class);
+ private final ArgumentCaptor<BiometricStateListener> mBiometricStateListenerArgumentCaptor =
+ ArgumentCaptor.forClass(BiometricStateListener.class);
+ private final List<FingerprintSensorPropertiesInternal>
+ mFingerprintSensorPropertiesInternals = List.of(
+ new FingerprintSensorPropertiesInternal(SENSOR_ID_FINGERPRINT,
+ SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FingerprintSensorProperties.TYPE_UNKNOWN,
+ true /* resetLockoutRequiresHardwareAuthToken */));
+ private final List<FaceSensorPropertiesInternal>
+ mFaceSensorPropertiesInternals = List.of(
+ new FaceSensorPropertiesInternal(SENSOR_ID_FACE,
+ SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FaceSensorProperties.TYPE_UNKNOWN,
+ false /* supportsFaceDetection */, false /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */));
private BiometricService mBiometricService;
@@ -192,6 +225,10 @@ public class BiometricServiceTest {
@Mock
private BiometricNotificationLogger mNotificationLogger;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
BiometricContextProvider mBiometricContextProvider;
@@ -1975,6 +2012,59 @@ public class BiometricServiceTest {
eq(hardwareAuthenticators));
}
+ @Test
+ public void testMandatoryBiometricsValue_whenParentProfileEnabled() throws RemoteException {
+ final Context context = ApplicationProvider.getApplicationContext();
+ final int profileParentId = context.getContentResolver().getUserId();
+ final int userId = profileParentId + 1;
+ final BiometricService.SettingObserver settingObserver =
+ new BiometricService.SettingObserver(
+ context, mBiometricHandlerProvider.getBiometricCallbackHandler(),
+ new ArrayList<>(), mUserManager, mFingerprintManager, mFaceManager);
+
+ verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
+ mFingerprintAuthenticatorRegisteredCallbackCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorRegisteredCallbackCaptor.capture());
+
+ mFingerprintAuthenticatorRegisteredCallbackCaptor.getValue().onAllAuthenticatorsRegistered(
+ mFingerprintSensorPropertiesInternals);
+ mFaceAuthenticatorRegisteredCallbackCaptor.getValue().onAllAuthenticatorsRegistered(
+ mFaceSensorPropertiesInternals);
+
+ verify(mFingerprintManager).registerBiometricStateListener(
+ mBiometricStateListenerArgumentCaptor.capture());
+
+ mBiometricStateListenerArgumentCaptor.getValue().onEnrollmentsChanged(userId,
+ SENSOR_ID_FINGERPRINT, true /* hasEnrollments */);
+
+ verify(mFaceManager).registerBiometricStateListener(
+ mBiometricStateListenerArgumentCaptor.capture());
+
+ mBiometricStateListenerArgumentCaptor.getValue().onEnrollmentsChanged(userId,
+ SENSOR_ID_FACE, true /* hasEnrollments */);
+
+ when(mUserManager.getProfileParent(userId)).thenReturn(new UserInfo(profileParentId,
+ "", 0));
+ when(mUserManager.getEnabledProfileIds(profileParentId)).thenReturn(new int[]{userId});
+
+ //Disable Identity Check for profile user
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.MANDATORY_BIOMETRICS, 0, userId);
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, 0,
+ userId);
+ //Enable Identity Check for parent user
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.MANDATORY_BIOMETRICS, 1, profileParentId);
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, 1,
+ profileParentId);
+
+ assertTrue(settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+ userId));
+ }
+
// Helper methods
private int invokeCanAuthenticate(BiometricService service, int authenticators)
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index cf628add705a..b81bf3c6e712 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -332,6 +332,28 @@ public class PreAuthInfoTest {
assertThat(preAuthInfo.callingUserId).isEqualTo(USER_ID);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_EFFECTIVE_USER_BP)
+ public void testCredentialOwnerIdAsUserId_forMandatoryBiometrics() throws Exception {
+ when(mUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(OWNER_ID);
+ when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+ OWNER_ID)).thenReturn(true);
+ when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+ USER_ID)).thenReturn(false);
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+
+ final BiometricSensor sensor = getFaceSensor();
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK);
+ promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor), USER_ID , promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+ mUserManager);
+
+ assertThat(preAuthInfo.getIsMandatoryBiometricsAuthentication()).isTrue();
+ }
+
private BiometricSensor getFingerprintSensor() {
BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java
new file mode 100644
index 000000000000..405fb44ca97e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IInvalidationCallback;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+
+@Presubmit
+@SmallTest
+public class FaceInvalidationClientTest {
+
+ private static final int SENSOR_ID = 4;
+ private static final int USER_ID = 0;
+
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricsFace mFace;
+ @Mock
+ private AidlResponseHandler mAidlResponseHandler;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private IInvalidationCallback mInvalidationCallback;
+ @Mock
+ private ClientMonitorCallback mClientMonitorCallback;
+
+ @Test
+ public void testStartInvalidationClient_whenHalIsHidl() throws RemoteException {
+ final OptionalUint64 halId = new OptionalUint64();
+
+ when(mFace.setCallback(any())).thenReturn(halId);
+
+ final AidlSession aidlSession = new AidlSession(mContext, () -> mFace, USER_ID,
+ mAidlResponseHandler);
+ final FaceInvalidationClient faceInvalidationClient =
+ new FaceInvalidationClient(mContext, () -> aidlSession, USER_ID,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, new HashMap<>(),
+ mInvalidationCallback);
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ faceInvalidationClient.cancel();
+ return null;
+ }).when(mAidlResponseHandler).onUnsupportedClientScheduled();
+
+ faceInvalidationClient.start(mClientMonitorCallback);
+
+ verify(mInvalidationCallback).onCompleted();
+ }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java
new file mode 100644
index 000000000000..1ee2fd1e8af9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.biometrics.IInvalidationCallback;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+
+@Presubmit
+@SmallTest
+public class FingerprintInvalidationClientTest {
+
+ private static final int SENSOR_ID = 4;
+ private static final int USER_ID = 0;
+
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricsFingerprint mFingerprint;
+ @Mock
+ private AidlResponseHandler mAidlResponseHandler;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private IInvalidationCallback mInvalidationCallback;
+ @Mock
+ private ClientMonitorCallback mClientMonitorCallback;
+
+ @Test
+ public void testStartInvalidationClient_whenHalIsHidl() throws RemoteException {
+ final AidlSession aidlSession = new AidlSession(
+ () -> mFingerprint, USER_ID, mAidlResponseHandler);
+ final FingerprintInvalidationClient fingerprintInvalidationClient =
+ new FingerprintInvalidationClient(mContext, () -> aidlSession, USER_ID,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, new HashMap<>(),
+ mInvalidationCallback);
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ fingerprintInvalidationClient.cancel();
+ return null;
+ }).when(mAidlResponseHandler).onUnsupportedClientScheduled(
+ FingerprintInvalidationClient.class);
+
+ fingerprintInvalidationClient.start(mClientMonitorCallback);
+
+ verify(mInvalidationCallback).onCompleted();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index e3868089b21f..727d1b59646a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -64,6 +64,7 @@ import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensorConfig;
+import android.companion.virtualdevice.flags.Flags;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -103,6 +104,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
@@ -999,6 +1001,7 @@ public class VirtualDeviceManagerServiceTest {
nullable(String.class), anyInt(), eq(null));
}
+ @DisableFlags(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@Test
public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
@@ -1010,6 +1013,7 @@ public class VirtualDeviceManagerServiceTest {
nullable(String.class), eq(DISPLAY_ID_1), eq(null));
}
+ @DisableFlags(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@Test
public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()
throws RemoteException {
@@ -1022,6 +1026,7 @@ public class VirtualDeviceManagerServiceTest {
nullable(String.class), eq(DISPLAY_ID_1), eq(null));
}
+ @DisableFlags(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@Test
public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
@@ -1037,6 +1042,7 @@ public class VirtualDeviceManagerServiceTest {
verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt());
}
+ @DisableFlags(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@Test
public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 36b163ec84b6..3d695a68f1f9 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -18,12 +18,12 @@ package com.android.server.compat;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
import android.app.compat.ChangeIdStateCache;
import android.app.compat.PackageOverride;
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 1d075401832d..95d601f69344 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -18,6 +18,7 @@ package com.android.server.compat;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -26,9 +27,9 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
-import static org.testng.Assert.assertThrows;
import android.compat.Compatibility.ChangeConfig;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -39,6 +40,8 @@ import android.os.Process;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.os.PermissionEnforcer;
+import android.permission.PermissionCheckerManager;
import androidx.test.runner.AndroidJUnit4;
@@ -90,6 +93,22 @@ public class PlatformCompatTest {
.thenReturn(-1);
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
+
+ var allGrantingPermissionEnforcer = new PermissionEnforcer() {
+ @Override
+ protected int checkPermission(String permission, AttributionSource source) {
+ return PermissionCheckerManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ protected int checkPermission(String permission, int pid, int uid) {
+ return PermissionCheckerManager.PERMISSION_GRANTED;
+ }
+ };
+
+ when(mContext.getSystemService(eq(Context.PERMISSION_ENFORCER_SERVICE)))
+ .thenReturn(allGrantingPermissionEnforcer);
+
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
mPlatformCompat =
new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index c741c6c041a2..077bb03c8359 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -2562,7 +2562,13 @@ public class HdmiCecLocalDevicePlaybackTest {
mTestLooper.dispatchAll();
// User interacted with the DUT, so the device will not go to standby.
- skipActiveSourceLostUi(0, true, true);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 93aa10b9112f..fd221185bacf 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -68,7 +68,6 @@ import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
import com.android.server.compat.PlatformCompat;
-import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.testutils.TestUtils;
import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
deleted file mode 100644
index 57274bf13499..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 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.integrity.model;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayOutputStream;
-
-@RunWith(JUnit4.class)
-public class ByteTrackedOutputStreamTest {
-
- @Test
- public void testConstructorStartsWithZeroBytesWritten() {
- ByteTrackedOutputStream byteTrackedOutputStream =
- new ByteTrackedOutputStream(new ByteArrayOutputStream());
-
- assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(0);
- }
-
- @Test
- public void testSuccessfulWriteAndValidateWrittenBytesCount_directFromByteArray()
- throws Exception {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
-
- byte[] outputContent = "This is going to be outputed for tests.".getBytes();
- byteTrackedOutputStream.write(outputContent);
-
- assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(outputContent.length);
- assertThat(outputStream.toByteArray().length).isEqualTo(outputContent.length);
- }
-
- @Test
- public void testSuccessfulWriteAndValidateWrittenBytesCount_fromBitStream() throws Exception {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
-
- BitOutputStream bitOutputStream = new BitOutputStream(byteTrackedOutputStream);
- bitOutputStream.setNext(/* numOfBits= */5, /* value= */1);
- bitOutputStream.flush();
-
- // Even though we wrote 5 bits, this will complete to 1 byte.
- assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1);
-
- // Add a bit less than 2 bytes (10 bits).
- bitOutputStream.setNext(/* numOfBits= */10, /* value= */1);
- bitOutputStream.flush();
- assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(3);
-
- assertThat(outputStream.toByteArray().length).isEqualTo(3);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
deleted file mode 100644
index d31ed689a7da..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 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.integrity.model;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.Rule;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-@RunWith(JUnit4.class)
-public class IntegrityCheckResultTest {
-
- @Test
- public void createAllowResult() {
- IntegrityCheckResult allowResult = IntegrityCheckResult.allow();
-
- assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- assertThat(allowResult.getMatchedRules()).isEmpty();
- }
-
- @Test
- public void createAllowResultWithRule() {
- String packageName = "com.test.deny";
- Rule forceAllowRule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
- packageName),
- Rule.FORCE_ALLOW);
-
- IntegrityCheckResult allowResult =
- IntegrityCheckResult.allow(Collections.singletonList(forceAllowRule));
-
- assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
- assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule);
- }
-
- @Test
- public void createDenyResultWithRule() {
- String packageName = "com.test.deny";
- Rule failedRule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
- packageName),
- Rule.DENY);
-
- IntegrityCheckResult denyResult =
- IntegrityCheckResult.deny(Collections.singletonList(failedRule));
-
- assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY);
- assertThat(denyResult.getMatchedRules()).containsExactly(failedRule);
- }
-
- @Test
- public void isDenyCausedByAppCertificate() {
- String packageName = "com.test.deny";
- String appCert = "app-cert";
- Rule failedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, packageName),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE, appCert))),
- Rule.DENY);
- Rule otherFailedRule =
- new Rule(
- new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
- AtomicFormula.EQ, 12),
- Rule.DENY);
-
- IntegrityCheckResult denyResult =
- IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule));
-
- assertThat(denyResult.isCausedByAppCertRule()).isTrue();
- assertThat(denyResult.isCausedByInstallerRule()).isFalse();
- }
-
- @Test
- public void isDenyCausedByInstaller() {
- String packageName = "com.test.deny";
- String appCert = "app-cert";
- Rule failedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, packageName),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_CERTIFICATE, appCert))),
- Rule.DENY);
- Rule otherFailedRule =
- new Rule(
- new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
- AtomicFormula.EQ, 12),
- Rule.DENY);
-
- IntegrityCheckResult denyResult =
- IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule));
-
- assertThat(denyResult.isCausedByAppCertRule()).isFalse();
- assertThat(denyResult.isCausedByInstallerRule()).isTrue();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
deleted file mode 100644
index 9ed2e88bd6a2..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.serializer.RuleBinarySerializer.INDEXED_RULE_SIZE_LIMIT;
-import static com.android.server.integrity.serializer.RuleBinarySerializer.NONINDEXED_RULE_SIZE_LIMIT;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.IntegrityUtils;
-import android.content.integrity.Rule;
-
-import androidx.annotation.NonNull;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-@RunWith(JUnit4.class)
-public class RuleBinarySerializerTest {
-
- private static final String SAMPLE_INSTALLER_NAME = "com.test.installer";
- private static final String SAMPLE_INSTALLER_CERT = "installer_cert";
-
- private static final String COMPOUND_FORMULA_START_BITS =
- getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
- private static final String COMPOUND_FORMULA_END_BITS =
- getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
- private static final String ATOMIC_FORMULA_START_BITS =
- getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
-
- private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
- private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
- private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
-
- private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
- private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
- private static final String INSTALLER_NAME = getBits(AtomicFormula.INSTALLER_NAME, KEY_BITS);
- private static final String INSTALLER_CERTIFICATE =
- getBits(AtomicFormula.INSTALLER_CERTIFICATE, KEY_BITS);
- private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
- private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
-
- private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
-
- private static final String IS_NOT_HASHED = "0";
- private static final String IS_HASHED = "1";
-
- private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
-
- private static final String START_BIT = "1";
- private static final String END_BIT = "1";
-
- private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
- getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
-
- private static final String SERIALIZED_START_INDEXING_KEY =
- IS_NOT_HASHED
- + getBits(START_INDEXING_KEY.length(), VALUE_SIZE_BITS)
- + getValueBits(START_INDEXING_KEY);
- private static final String SERIALIZED_END_INDEXING_KEY =
- IS_NOT_HASHED
- + getBits(END_INDEXING_KEY.length(), VALUE_SIZE_BITS)
- + getValueBits(END_INDEXING_KEY);
-
- @Test
- public void testBinaryString_serializeNullRules() {
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- assertExpectException(
- RuleSerializeException.class,
- /* expectedExceptionMessageRegex= */ "Null rules cannot be serialized.",
- () -> binarySerializer.serialize(null, /* formatVersion= */ Optional.empty()));
- }
-
- @Test
- public void testBinaryString_emptyRules() throws Exception {
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- binarySerializer.serialize(
- Collections.emptyList(),
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream);
-
- ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream();
- expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- assertThat(ruleOutputStream.toByteArray())
- .isEqualTo(expectedRuleOutputStream.toByteArray());
-
- ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
- String serializedIndexingBytes =
- SERIALIZED_START_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
- + SERIALIZED_END_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
- byte[] expectedIndexingBytes =
- getBytes(
- serializedIndexingBytes
- + serializedIndexingBytes
- + serializedIndexingBytes);
- expectedIndexingOutputStream.write(expectedIndexingBytes);
- assertThat(indexingOutputStream.toByteArray())
- .isEqualTo(expectedIndexingOutputStream.toByteArray());
- }
-
- @Test
- public void testBinaryStream_serializeValidCompoundFormula() throws Exception {
- String packageName = "com.test.app";
- Rule rule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false))),
- Rule.DENY);
-
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- binarySerializer.serialize(
- Collections.singletonList(rule),
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream);
-
- String expectedBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream();
- expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- expectedRuleOutputStream.write(getBytes(expectedBits));
- assertThat(ruleOutputStream.toByteArray())
- .isEqualTo(expectedRuleOutputStream.toByteArray());
-
- ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
- String expectedIndexingBitsForIndexed =
- SERIALIZED_START_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
- + SERIALIZED_END_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
- String expectedIndexingBitsForUnindexed =
- SERIALIZED_START_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
- + SERIALIZED_END_INDEXING_KEY
- + getBits(
- DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
- /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(
- getBytes(
- expectedIndexingBitsForIndexed
- + expectedIndexingBitsForIndexed
- + expectedIndexingBitsForUnindexed));
-
- assertThat(indexingOutputStream.toByteArray())
- .isEqualTo(expectedIndexingOutputStream.toByteArray());
- }
-
- @Test
- public void testBinaryString_serializeValidCompoundFormula_notConnector() throws Exception {
- String packageName = "com.test.app";
- Rule rule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false))),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeValidCompoundFormula_andConnector() throws Exception {
- String packageName = "com.test.app";
- String appCertificate = "test_cert";
- Rule rule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- appCertificate,
- /* isHashedValue= */ false))),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + AND
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeValidCompoundFormula_orConnector() throws Exception {
- String packageName = "com.test.app";
- String appCertificate = "test_cert";
- Rule rule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.OR,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- appCertificate,
- /* isHashedValue= */ false))),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + OR
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeValidAtomicFormula_stringValue() throws Exception {
- String packageName = "com.test.app";
- Rule rule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeValidAtomicFormula_hashedValue() throws Exception {
- String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
- Rule rule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- IntegrityUtils.getHexDigest(
- appCertificate.getBytes(StandardCharsets.UTF_8)),
- /* isHashedValue= */ true),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeValidAtomicFormula_integerValue() throws Exception {
- long versionCode = 1;
- Rule rule =
- new Rule(
- new AtomicFormula.LongAtomicFormula(
- AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + VERSION_CODE
- + EQ
- + getBits(versionCode, /* numOfBits= */ 64)
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeValidAtomicFormula_booleanValue() throws Exception {
- String preInstalled = "1";
- Rule rule =
- new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
- Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + PRE_INSTALLED
- + EQ
- + preInstalled
- + DENY
- + END_BIT;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- byteArrayOutputStream.write(getBytes(expectedBits));
- byte[] expectedRules = byteArrayOutputStream.toByteArray();
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_serializeInvalidFormulaType() throws Exception {
- IntegrityFormula invalidFormula = getInvalidFormula();
- Rule rule = new Rule(invalidFormula, Rule.DENY);
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- assertExpectException(
- RuleSerializeException.class,
- /* expectedExceptionMessageRegex= */ "Malformed rule identified.",
- () ->
- binarySerializer.serialize(
- Collections.singletonList(rule),
- /* formatVersion= */ Optional.empty()));
- }
-
- @Test
- public void testBinaryString_serializeFormatVersion() throws Exception {
- int formatVersion = 1;
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- String expectedBits = getBits(formatVersion, FORMAT_VERSION_BITS);
- byte[] expectedRules = getBytes(expectedBits);
-
- byte[] actualRules =
- binarySerializer.serialize(
- Collections.emptyList(), /* formatVersion= */ Optional.of(formatVersion));
-
- assertThat(actualRules).isEqualTo(expectedRules);
- }
-
- @Test
- public void testBinaryString_verifyManyRulesAreIndexedCorrectly() throws Exception {
- int ruleCount = 225;
- String packagePrefix = "package.name.";
- String appCertificatePrefix = "app.cert.";
- String installerNamePrefix = "installer.";
-
- // Create the rule set with 225 package name based rules, 225 app certificate indexed rules,
- // and 225 non-indexed rules..
- List<Rule> ruleList = new ArrayList();
- for (int count = 0; count < ruleCount; count++) {
- ruleList.add(
- getRuleWithPackageNameAndSampleInstallerName(
- String.format("%s%04d", packagePrefix, count)));
- }
- for (int count = 0; count < ruleCount; count++) {
- ruleList.add(
- getRuleWithAppCertificateAndSampleInstallerName(
- String.format("%s%04d", appCertificatePrefix, count)));
- }
- for (int count = 0; count < ruleCount; count++) {
- ruleList.add(
- getNonIndexedRuleWithInstallerName(
- String.format("%s%04d", installerNamePrefix, count)));
- }
-
- // Serialize the rules.
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
- binarySerializer.serialize(
- ruleList,
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream);
-
- // Verify the rules file and index files.
- ByteArrayOutputStream expectedOrderedRuleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
-
- expectedOrderedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
- int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length;
-
- String expectedIndexingBytesForPackageNameIndexed =
- SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
- for (int count = 0; count < ruleCount; count++) {
- String packageName = String.format("%s%04d", packagePrefix, count);
- if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
- expectedIndexingBytesForPackageNameIndexed +=
- IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
- }
-
- byte[] bytesForPackage =
- getBytes(
- getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
- packageName));
- expectedOrderedRuleOutputStream.write(bytesForPackage);
- totalBytesWritten += bytesForPackage.length;
- }
- expectedIndexingBytesForPackageNameIndexed +=
- SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
-
- String expectedIndexingBytesForAppCertificateIndexed =
- SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
- for (int count = 0; count < ruleCount; count++) {
- String appCertificate = String.format("%s%04d", appCertificatePrefix, count);
- if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
- expectedIndexingBytesForAppCertificateIndexed +=
- IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + getBits(totalBytesWritten, /* numOfBits= */ 32);
- }
-
- byte[] bytesForPackage =
- getBytes(
- getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
- appCertificate));
- expectedOrderedRuleOutputStream.write(bytesForPackage);
- totalBytesWritten += bytesForPackage.length;
- }
- expectedIndexingBytesForAppCertificateIndexed +=
- SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
-
- String expectedIndexingBytesForUnindexed =
- SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
- for (int count = 0; count < ruleCount; count++) {
- byte[] bytesForPackage =
- getBytes(
- getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
- String.format("%s%04d", installerNamePrefix, count)));
- expectedOrderedRuleOutputStream.write(bytesForPackage);
- totalBytesWritten += bytesForPackage.length;
- }
- expectedIndexingBytesForUnindexed +=
- SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(
- getBytes(
- expectedIndexingBytesForPackageNameIndexed
- + expectedIndexingBytesForAppCertificateIndexed
- + expectedIndexingBytesForUnindexed));
-
- assertThat(ruleOutputStream.toByteArray())
- .isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
- assertThat(indexingOutputStream.toByteArray())
- .isEqualTo(expectedIndexingOutputStream.toByteArray());
- }
-
- @Test
- public void testBinaryString_totalRuleSizeLimitReached() {
- int ruleCount = INDEXED_RULE_SIZE_LIMIT - 1;
- String packagePrefix = "package.name.";
- String appCertificatePrefix = "app.cert.";
- String installerNamePrefix = "installer.";
-
- // Create the rule set with more rules than the system can handle in total.
- List<Rule> ruleList = new ArrayList();
- for (int count = 0; count < ruleCount; count++) {
- ruleList.add(
- getRuleWithPackageNameAndSampleInstallerName(
- String.format("%s%04d", packagePrefix, count)));
- }
- for (int count = 0; count < ruleCount; count++) {
- ruleList.add(
- getRuleWithAppCertificateAndSampleInstallerName(
- String.format("%s%04d", appCertificatePrefix, count)));
- }
- for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT - 1; count++) {
- ruleList.add(
- getNonIndexedRuleWithInstallerName(
- String.format("%s%04d", installerNamePrefix, count)));
- }
-
- // Serialize the rules.
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- assertExpectException(
- RuleSerializeException.class,
- "Too many rules provided",
- () ->
- binarySerializer.serialize(
- ruleList,
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream));
- }
-
- @Test
- public void testBinaryString_tooManyPackageNameIndexedRules() {
- String packagePrefix = "package.name.";
-
- // Create a rule set with too many package name indexed rules.
- List<Rule> ruleList = new ArrayList();
- for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) {
- ruleList.add(
- getRuleWithPackageNameAndSampleInstallerName(
- String.format("%s%04d", packagePrefix, count)));
- }
-
- // Serialize the rules.
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- assertExpectException(
- RuleSerializeException.class,
- "Too many rules provided in the indexing group.",
- () ->
- binarySerializer.serialize(
- ruleList,
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream));
- }
-
- @Test
- public void testBinaryString_tooManyAppCertificateIndexedRules() {
- String appCertificatePrefix = "app.cert.";
-
- // Create a rule set with too many app certificate indexed rules.
- List<Rule> ruleList = new ArrayList();
- for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) {
- ruleList.add(
- getRuleWithAppCertificateAndSampleInstallerName(
- String.format("%s%04d", appCertificatePrefix, count)));
- }
-
- // Serialize the rules.
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- assertExpectException(
- RuleSerializeException.class,
- "Too many rules provided in the indexing group.",
- () ->
- binarySerializer.serialize(
- ruleList,
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream));
- }
-
- @Test
- public void testBinaryString_tooManyNonIndexedRules() {
- String installerNamePrefix = "installer.";
-
- // Create a rule set with too many unindexed rules.
- List<Rule> ruleList = new ArrayList();
- for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT + 1; count++) {
- ruleList.add(
- getNonIndexedRuleWithInstallerName(
- String.format("%s%04d", installerNamePrefix, count)));
- }
-
- // Serialize the rules.
- ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
- ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
- RuleSerializer binarySerializer = new RuleBinarySerializer();
-
- assertExpectException(
- RuleSerializeException.class,
- "Too many rules provided in the indexing group.",
- () ->
- binarySerializer.serialize(
- ruleList,
- /* formatVersion= */ Optional.empty(),
- ruleOutputStream,
- indexingOutputStream));
- }
-
- private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) {
- return new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME,
- SAMPLE_INSTALLER_NAME,
- /* isHashedValue= */ false))),
- Rule.DENY);
- }
-
- private String getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
- String packageName) {
- return START_BIT
- + COMPOUND_FORMULA_START_BITS
- + AND
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + ATOMIC_FORMULA_START_BITS
- + INSTALLER_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(SAMPLE_INSTALLER_NAME.length(), VALUE_SIZE_BITS)
- + getValueBits(SAMPLE_INSTALLER_NAME)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- }
-
- private Rule getRuleWithAppCertificateAndSampleInstallerName(String certificate) {
- return new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- certificate,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME,
- SAMPLE_INSTALLER_NAME,
- /* isHashedValue= */ false))),
- Rule.DENY);
- }
-
- private String getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
- String appCertificate) {
- return START_BIT
- + COMPOUND_FORMULA_START_BITS
- + AND
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + ATOMIC_FORMULA_START_BITS
- + INSTALLER_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(SAMPLE_INSTALLER_NAME.length(), VALUE_SIZE_BITS)
- + getValueBits(SAMPLE_INSTALLER_NAME)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- }
-
- private Rule getNonIndexedRuleWithInstallerName(String installerName) {
- return new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME,
- installerName,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_CERTIFICATE,
- SAMPLE_INSTALLER_CERT,
- /* isHashedValue= */ false))),
- Rule.DENY);
- }
-
- private String getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
- String installerName) {
- return START_BIT
- + COMPOUND_FORMULA_START_BITS
- + AND
- + ATOMIC_FORMULA_START_BITS
- + INSTALLER_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(installerName.length(), VALUE_SIZE_BITS)
- + getValueBits(installerName)
- + ATOMIC_FORMULA_START_BITS
- + INSTALLER_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(SAMPLE_INSTALLER_CERT.length(), VALUE_SIZE_BITS)
- + getValueBits(SAMPLE_INSTALLER_CERT)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- }
-
- private static IntegrityFormula getInvalidFormula() {
- return new AtomicFormula(0) {
- @Override
- public int getTag() {
- return 0;
- }
-
- @Override
- public boolean matches(AppInstallMetadata appInstallMetadata) {
- return false;
- }
-
- @Override
- public boolean isAppCertificateFormula() {
- return false;
- }
-
- @Override
- public boolean isAppCertificateLineageFormula() {
- return false;
- }
-
- @Override
- public boolean isInstallerFormula() {
- return false;
- }
-
- @Override
- public int hashCode() {
- return super.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return super.equals(obj);
- }
-
- @NonNull
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- @Override
- public String toString() {
- return super.toString();
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- }
- };
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
deleted file mode 100644
index 6dccdf51af02..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity.serializer;
-
-import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;
-import static com.android.server.integrity.serializer.RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import androidx.annotation.NonNull;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/** Unit tests for {@link RuleIndexingDetailsIdentifier}. */
-@RunWith(JUnit4.class)
-public class RuleIndexingDetailsIdentifierTest {
-
- private static final String SAMPLE_APP_CERTIFICATE = "testcert";
- private static final String SAMPLE_INSTALLER_NAME = "com.test.installer";
- private static final String SAMPLE_INSTALLER_CERTIFICATE = "installercert";
- private static final String SAMPLE_PACKAGE_NAME = "com.test.package";
-
- private static final AtomicFormula ATOMIC_FORMULA_WITH_PACKAGE_NAME =
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- SAMPLE_PACKAGE_NAME,
- /* isHashedValue= */ false);
- private static final AtomicFormula ATOMIC_FORMULA_WITH_APP_CERTIFICATE =
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- SAMPLE_APP_CERTIFICATE,
- /* isHashedValue= */ false);
- private static final AtomicFormula ATOMIC_FORMULA_WITH_INSTALLER_NAME =
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME,
- SAMPLE_INSTALLER_NAME,
- /* isHashedValue= */ false);
- private static final AtomicFormula ATOMIC_FORMULA_WITH_INSTALLER_CERTIFICATE =
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.INSTALLER_CERTIFICATE,
- SAMPLE_INSTALLER_CERTIFICATE,
- /* isHashedValue= */ false);
- private static final AtomicFormula ATOMIC_FORMULA_WITH_VERSION_CODE =
- new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
- AtomicFormula.EQ, 12);
- private static final AtomicFormula ATOMIC_FORMULA_WITH_ISPREINSTALLED =
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, /* booleanValue= */
- true);
-
-
- private static final Rule RULE_WITH_PACKAGE_NAME =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- ATOMIC_FORMULA_WITH_PACKAGE_NAME,
- ATOMIC_FORMULA_WITH_INSTALLER_NAME)),
- Rule.DENY);
- private static final Rule RULE_WITH_APP_CERTIFICATE =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- ATOMIC_FORMULA_WITH_APP_CERTIFICATE,
- ATOMIC_FORMULA_WITH_INSTALLER_NAME)),
- Rule.DENY);
- private static final Rule RULE_WITH_INSTALLER_RESTRICTIONS =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- ATOMIC_FORMULA_WITH_INSTALLER_NAME,
- ATOMIC_FORMULA_WITH_INSTALLER_CERTIFICATE)),
- Rule.DENY);
-
- private static final Rule RULE_WITH_NONSTRING_RESTRICTIONS =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- ATOMIC_FORMULA_WITH_VERSION_CODE,
- ATOMIC_FORMULA_WITH_ISPREINSTALLED)),
- Rule.DENY);
- public static final int INVALID_FORMULA_TAG = -1;
-
- @Test
- public void getIndexType_nullRule() {
- List<Rule> ruleList = null;
-
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex= */
- "Index buckets cannot be created for null rule list.",
- () -> splitRulesIntoIndexBuckets(ruleList));
- }
-
- @Test
- public void getIndexType_invalidFormula() {
- List<Rule> ruleList = new ArrayList();
- ruleList.add(new Rule(getInvalidFormula(), Rule.DENY));
-
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex= */ "Malformed rule identified.",
- () -> splitRulesIntoIndexBuckets(ruleList));
- }
-
- @Test
- public void getIndexType_ruleContainingPackageNameFormula() {
- List<Rule> ruleList = new ArrayList();
- ruleList.add(RULE_WITH_PACKAGE_NAME);
-
- Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
-
- // Verify the resulting map content.
- assertThat(result.keySet())
- .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
- assertThat(result.get(NOT_INDEXED)).isEmpty();
- assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
- assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly(SAMPLE_PACKAGE_NAME);
- assertThat(result.get(PACKAGE_NAME_INDEXED).get(SAMPLE_PACKAGE_NAME))
- .containsExactly(RULE_WITH_PACKAGE_NAME);
- }
-
- @Test
- public void getIndexType_ruleContainingAppCertificateFormula() {
- List<Rule> ruleList = new ArrayList();
- ruleList.add(RULE_WITH_APP_CERTIFICATE);
-
- Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
-
- assertThat(result.keySet())
- .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
- assertThat(result.get(NOT_INDEXED)).isEmpty();
- assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
- assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet())
- .containsExactly(SAMPLE_APP_CERTIFICATE);
- assertThat(result.get(APP_CERTIFICATE_INDEXED).get(SAMPLE_APP_CERTIFICATE))
- .containsExactly(RULE_WITH_APP_CERTIFICATE);
- }
-
- @Test
- public void getIndexType_ruleWithUnindexedCompoundFormula() {
- List<Rule> ruleList = new ArrayList();
- ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
-
- Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
-
- assertThat(result.keySet())
- .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
- assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
- assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
- assertThat(result.get(NOT_INDEXED).get("N/A"))
- .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS);
- }
-
- @Test
- public void getIndexType_ruleContainingCompoundFormulaWithIntAndBoolean() {
- List<Rule> ruleList = new ArrayList();
- ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
-
- Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
-
- assertThat(result.keySet())
- .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
- assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
- assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
- assertThat(result.get(NOT_INDEXED).get("N/A"))
- .containsExactly(RULE_WITH_NONSTRING_RESTRICTIONS);
- }
-
- @Test
- public void getIndexType_negatedRuleContainingPackageNameFormula() {
- Rule negatedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.NOT,
- Arrays.asList(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- ATOMIC_FORMULA_WITH_PACKAGE_NAME,
- ATOMIC_FORMULA_WITH_APP_CERTIFICATE)))),
- Rule.DENY);
- List<Rule> ruleList = new ArrayList();
- ruleList.add(negatedRule);
-
- Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
-
- assertThat(result.keySet())
- .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
- assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
- assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
- assertThat(result.get(NOT_INDEXED).get("N/A")).containsExactly(negatedRule);
- }
-
- @Test
- public void getIndexType_allRulesTogetherSplitCorrectly() {
- Rule packageNameRuleA = getRuleWithPackageName("aaa");
- Rule packageNameRuleB = getRuleWithPackageName("bbb");
- Rule packageNameRuleC = getRuleWithPackageName("ccc");
- Rule certificateRule1 = getRuleWithAppCertificate("cert1");
- Rule certificateRule2 = getRuleWithAppCertificate("cert2");
- Rule certificateRule3 = getRuleWithAppCertificate("cert3");
-
- List<Rule> ruleList = new ArrayList();
- ruleList.add(packageNameRuleB);
- ruleList.add(packageNameRuleC);
- ruleList.add(packageNameRuleA);
- ruleList.add(certificateRule3);
- ruleList.add(certificateRule2);
- ruleList.add(certificateRule1);
- ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
- ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
-
- Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
-
- assertThat(result.keySet())
- .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
-
- // We check asserts this way to ensure ordering based on package name.
- assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly("aaa", "bbb", "ccc");
-
- // We check asserts this way to ensure ordering based on app certificate.
- assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()).containsExactly("cert1", "cert2",
- "cert3");
-
- assertThat(result.get(NOT_INDEXED).get("N/A"))
- .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS,
- RULE_WITH_NONSTRING_RESTRICTIONS);
- }
-
- private Rule getRuleWithPackageName(String packageName) {
- return new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- ATOMIC_FORMULA_WITH_INSTALLER_NAME)),
- Rule.DENY);
- }
-
- private Rule getRuleWithAppCertificate(String certificate) {
- return new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- certificate,
- /* isHashedValue= */ false),
- ATOMIC_FORMULA_WITH_INSTALLER_NAME)),
- Rule.DENY);
- }
-
- private IntegrityFormula getInvalidFormula() {
- return new AtomicFormula(0) {
- @Override
- public int getTag() {
- return INVALID_FORMULA_TAG;
- }
-
- @Override
- public boolean matches(AppInstallMetadata appInstallMetadata) {
- return false;
- }
-
- @Override
- public boolean isAppCertificateFormula() {
- return false;
- }
-
- @Override
- public boolean isAppCertificateLineageFormula() {
- return false;
- }
-
- @Override
- public boolean isInstallerFormula() {
- return false;
- }
-
- @Override
- public int hashCode() {
- return super.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return super.equals(obj);
- }
-
- @NonNull
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- @Override
- public String toString() {
- return super.toString();
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- }
- };
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java
index 685e8d6a3bc5..e611867493eb 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java
@@ -65,7 +65,7 @@ public class ContextHubServiceTest {
new Pair<>(Arrays.asList(mMockContextHubInfo), Arrays.asList(""));
when(mMockContextHubInfo.getId()).thenReturn(CONTEXT_HUB_ID);
when(mMockContextHubInfo.toString()).thenReturn(CONTEXT_HUB_STRING);
- when(mMockContextHubWrapper.getHubs()).thenReturn(hubInfo);
+ when(mMockContextHubWrapper.getContextHubs()).thenReturn(hubInfo);
when(mMockContextHubWrapper.supportsLocationSettingNotifications()).thenReturn(true);
when(mMockContextHubWrapper.supportsWifiSettingNotifications()).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index dddab657be14..5a7027edc20d 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2158,13 +2158,11 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainEnabled() throws Exception {
verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
@RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN)
public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception {
// initialization calls setFirewallChainEnabled, so we want to reset the invocations.
@@ -2194,10 +2192,7 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled({
- Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE,
- Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN
- })
+ @RequiresFlagsEnabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN)
public void testBackgroundChainOnProcStateChangeDifferentDelays() throws Exception {
// The app will be blocked when there is no prior proc-state.
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
@@ -2247,7 +2242,6 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnAllowlistChange() throws Exception {
// initialization calls setFirewallChainEnabled, so we want to reset the invocations.
clearInvocations(mNetworkManager);
@@ -2285,7 +2279,6 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnTempAllowlistChange() throws Exception {
// initialization calls setFirewallChainEnabled, so we want to reset the invocations.
clearInvocations(mNetworkManager);
@@ -2387,7 +2380,6 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersProcStateChanges() throws Exception {
int testProcStateSeq = 0;
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
@@ -2450,7 +2442,6 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersStaleChanges() throws Exception {
final int testProcStateSeq = 51;
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
@@ -2470,7 +2461,6 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersCapabilityChanges() throws Exception {
int testProcStateSeq = 0;
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
@@ -2559,7 +2549,6 @@ public class NetworkPolicyManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testObsoleteHandleUidChanged() throws Exception {
callAndWaitOnUidGone(UID_A);
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
index 4a43c2e6c180..9d7b6a171bd4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -977,11 +977,19 @@ public final class BackgroundInstallControlServiceTest {
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
mPackageListObserver.onPackageRemoved(PACKAGE_NAME_1, uid);
+ // Test that notifyAllCallbacks doesn't trigger for non-background-installed package
+ mPackageListObserver.onPackageRemoved(PACKAGE_NAME_3, uid);
mTestLooper.dispatchAll();
assertEquals(1, packages.size());
assertFalse(packages.contains(USER_ID_1, PACKAGE_NAME_1));
assertTrue(packages.contains(USER_ID_2, PACKAGE_NAME_2));
+
+ verify(mCallbackHelper)
+ .notifyAllCallbacks(
+ USER_ID_1,
+ PACKAGE_NAME_1,
+ BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index cfe3d84140df..2ed71cecd79d 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -22,10 +22,12 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -39,14 +41,18 @@ import android.content.pm.PackageManager;
import android.hardware.thermal.TemperatureThreshold;
import android.hardware.thermal.ThrottlingSeverity;
import android.os.CoolingDevice;
+import android.os.Flags;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.IThermalEventListener;
+import android.os.IThermalHeadroomListener;
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Temperature;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -56,6 +62,8 @@ import com.android.server.power.ThermalManagerService.TemperatureWatcher;
import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -78,6 +86,11 @@ import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ThermalManagerServiceTest {
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mSetFlagsClassRule = new SetFlagsRule.ClassRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule();
+
private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
private ThermalManagerService mService;
private ThermalHalFake mFakeHal;
@@ -89,6 +102,8 @@ public class ThermalManagerServiceTest {
@Mock
private IThermalService mIThermalServiceMock;
@Mock
+ private IThermalHeadroomListener mHeadroomListener;
+ @Mock
private IThermalEventListener mEventListener1;
@Mock
private IThermalEventListener mEventListener2;
@@ -102,22 +117,23 @@ public class ThermalManagerServiceTest {
*/
private class ThermalHalFake extends ThermalHalWrapper {
private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
- private ArrayList<Temperature> mTemperatureList = new ArrayList<>();
- private ArrayList<CoolingDevice> mCoolingDeviceList = new ArrayList<>();
- private ArrayList<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds();
+ private List<Temperature> mTemperatureList = new ArrayList<>();
+ private List<Temperature> mOverrideTemperatures = null;
+ private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>();
+ private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds();
- private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1",
+ private Temperature mSkin1 = new Temperature(28, Temperature.TYPE_SKIN, "skin1",
INIT_STATUS);
- private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2",
+ private Temperature mSkin2 = new Temperature(31, Temperature.TYPE_SKIN, "skin2",
INIT_STATUS);
- private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt",
+ private Temperature mBattery = new Temperature(34, Temperature.TYPE_BATTERY, "batt",
INIT_STATUS);
- private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport",
+ private Temperature mUsbPort = new Temperature(37, Temperature.TYPE_USB_PORT, "usbport",
INIT_STATUS);
- private CoolingDevice mCpu = new CoolingDevice(0, CoolingDevice.TYPE_BATTERY, "cpu");
- private CoolingDevice mGpu = new CoolingDevice(0, CoolingDevice.TYPE_BATTERY, "gpu");
+ private CoolingDevice mCpu = new CoolingDevice(40, CoolingDevice.TYPE_BATTERY, "cpu");
+ private CoolingDevice mGpu = new CoolingDevice(43, CoolingDevice.TYPE_BATTERY, "gpu");
- private ArrayList<TemperatureThreshold> initializeThresholds() {
+ private List<TemperatureThreshold> initializeThresholds() {
ArrayList<TemperatureThreshold> thresholds = new ArrayList<>();
TemperatureThreshold skinThreshold = new TemperatureThreshold();
@@ -157,6 +173,14 @@ public class ThermalManagerServiceTest {
mCoolingDeviceList.add(mGpu);
}
+ void setOverrideTemperatures(List<Temperature> temperatures) {
+ mOverrideTemperatures = temperatures;
+ }
+
+ void resetOverrideTemperatures() {
+ mOverrideTemperatures = null;
+ }
+
@Override
protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
List<Temperature> ret = new ArrayList<>();
@@ -221,22 +245,36 @@ public class ThermalManagerServiceTest {
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
resetListenerMock();
mService = new ThermalManagerService(mContext, mFakeHal);
- // Register callbacks before AMS ready and no callback sent
+ mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ }
+
+ private void resetListenerMock() {
+ reset(mEventListener1);
+ reset(mStatusListener1);
+ reset(mEventListener2);
+ reset(mStatusListener2);
+ reset(mHeadroomListener);
+ doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
+ doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
+ doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
+ doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
+ doReturn(mock(IBinder.class)).when(mHeadroomListener).asBinder();
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ mService = new ThermalManagerService(mContext, mFakeHal);
+ // Register callbacks before AMS ready and verify they are called after AMS is ready
assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
Temperature.TYPE_SKIN));
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
- verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
- .times(0)).notifyThrottling(any(Temperature.class));
- verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
- .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
- verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
- .times(0)).notifyThrottling(any(Temperature.class));
- verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
- .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC);
resetListenerMock();
mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
+
ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(4)).notifyThrottling(captor.capture());
@@ -251,31 +289,18 @@ public class ThermalManagerServiceTest {
captor.getAllValues());
verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(0)).onStatusChange(Temperature.THROTTLING_NONE);
- }
-
- private void resetListenerMock() {
- reset(mEventListener1);
- reset(mStatusListener1);
- reset(mEventListener2);
- reset(mStatusListener2);
- doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
- doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
- doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
- doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
- }
-
- @Test
- public void testRegister() throws RemoteException {
resetListenerMock();
- // Register callbacks and verify they are called
+
+ // Register callbacks after AMS ready and verify they are called
assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
- ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+ captor = ArgumentCaptor.forClass(Temperature.class);
verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(4)).notifyThrottling(captor.capture());
assertListEqualsIgnoringOrder(mFakeHal.mTemperatureList, captor.getAllValues());
verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
.times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+
// Register new callbacks and verify old ones are not called (remained same) while new
// ones are called
assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
@@ -296,7 +321,15 @@ public class ThermalManagerServiceTest {
}
@Test
- public void testNotifyThrottling() throws RemoteException {
+ public void testNotifyThrottling() throws Exception {
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+ Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC);
+ resetListenerMock();
+
int status = Temperature.THROTTLING_SEVERE;
// Should only notify event not status
Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
@@ -349,6 +382,57 @@ public class ThermalManagerServiceTest {
}
@Test
+ @EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK})
+ public void testNotifyThrottling_headroomCallback() throws Exception {
+ assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
+ Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC);
+ resetListenerMock();
+ int status = Temperature.THROTTLING_SEVERE;
+ mFakeHal.setOverrideTemperatures(new ArrayList<>());
+
+ // Should not notify on non-skin type
+ Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onTemperatureChanged(newBattery);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onHeadroomChange(anyFloat(), anyFloat(), anyInt(), any());
+ resetListenerMock();
+
+ // Notify headroom on skin temperature change
+ Temperature newSkin = new Temperature(37, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(0.9f), anyFloat(), anyInt(),
+ eq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
+ 1.5f}));
+ resetListenerMock();
+
+ // Same or similar temperature should not trigger in a short period
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ newSkin = new Temperature(36.9f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ newSkin = new Temperature(37.1f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onHeadroomChange(anyFloat(), anyFloat(), anyInt(), any());
+ resetListenerMock();
+
+ // Significant temperature should trigger in a short period
+ newSkin = new Temperature(34f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(0.8f), anyFloat(), anyInt(),
+ eq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
+ 1.5f}));
+ resetListenerMock();
+ newSkin = new Temperature(40f, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onTemperatureChanged(newSkin);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(1.0f), anyFloat(), anyInt(),
+ eq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
+ 1.5f}));
+ }
+
+ @Test
public void testGetCurrentTemperatures() throws RemoteException {
assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(false, 0),
Arrays.asList(mService.mService.getCurrentTemperatures()));
@@ -388,13 +472,28 @@ public class ThermalManagerServiceTest {
// Do no call onActivityManagerReady to skip connect HAL
assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
- assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
- assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertFalse(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(any(Temperature.class));
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onHeadroomChange(anyFloat(), anyFloat(), anyInt(), any());
+
assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperatures()).size());
assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperaturesWithType(
- Temperature.TYPE_SKIN)).size());
+ Temperature.TYPE_SKIN)).size());
assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
assertTrue(Float.isNaN(mService.mService.getThermalHeadroom(0)));
+
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener2));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+ assertFalse(mService.mService.unregisterThermalHeadroomListener(mHeadroomListener));
}
@Test
@@ -419,35 +518,45 @@ public class ThermalManagerServiceTest {
}
@Test
- public void testTemperatureWatcherUpdateSevereThresholds() {
+ @EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK,
+ Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS})
+ public void testTemperatureWatcherUpdateSevereThresholds() throws Exception {
+ assertTrue(mService.mService.registerThermalHeadroomListener(mHeadroomListener));
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(0.6f), eq(0.6f), anyInt(),
+ aryEq(new float[]{Float.NaN, 0.6666667f, 0.8333333f, 1.0f, 1.1666666f, 1.3333334f,
+ 1.5f}));
+ resetListenerMock();
TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureThreshold newThreshold = new TemperatureThreshold();
+ newThreshold.name = "skin1";
+ newThreshold.type = Temperature.TYPE_SKIN;
+ // significant change in threshold (> 0.3C) should trigger a callback
+ newThreshold.hotThrottlingThresholds = new float[]{
+ Float.NaN, 43.0f, 46.0f, 49.0f, Float.NaN, Float.NaN, Float.NaN
+ };
+ mFakeHal.mCallback.onThresholdChanged(newThreshold);
synchronized (watcher.mSamples) {
- watcher.mSevereThresholds.erase();
- watcher.getAndUpdateThresholds();
- assertEquals(1, watcher.mSevereThresholds.size());
- assertEquals("skin1", watcher.mSevereThresholds.keyAt(0));
Float threshold = watcher.mSevereThresholds.get("skin1");
assertNotNull(threshold);
- assertEquals(40.0f, threshold, 0.0f);
- assertArrayEquals("Got" + Arrays.toString(watcher.mHeadroomThresholds),
- new float[]{Float.NaN, 0.6667f, 0.8333f, 1.0f, 1.166f, 1.3333f,
- 1.5f},
- watcher.mHeadroomThresholds, 0.01f);
-
- TemperatureThreshold newThreshold = new TemperatureThreshold();
- newThreshold.name = "skin1";
- newThreshold.hotThrottlingThresholds = new float[] {
- Float.NaN, 44.0f, 47.0f, 50.0f, Float.NaN, Float.NaN, Float.NaN
- };
- mFakeHal.mCallback.onThresholdChanged(newThreshold);
- threshold = watcher.mSevereThresholds.get("skin1");
- assertNotNull(threshold);
- assertEquals(50.0f, threshold, 0.0f);
+ assertEquals(49.0f, threshold, 0.0f);
assertArrayEquals("Got" + Arrays.toString(watcher.mHeadroomThresholds),
- new float[]{Float.NaN, 0.8f, 0.9f, 1.0f, Float.NaN, Float.NaN,
- Float.NaN},
+ new float[]{Float.NaN, 0.8f, 0.9f, 1.0f, Float.NaN, Float.NaN, Float.NaN},
watcher.mHeadroomThresholds, 0.01f);
}
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onHeadroomChange(eq(0.3f), eq(0.3f), anyInt(),
+ aryEq(new float[]{Float.NaN, 0.8f, 0.9f, 1.0f, Float.NaN, Float.NaN, Float.NaN}));
+ resetListenerMock();
+
+ // same or similar threshold callback data within a second should not trigger callback
+ mFakeHal.mCallback.onThresholdChanged(newThreshold);
+ newThreshold.hotThrottlingThresholds = new float[]{
+ Float.NaN, 43.1f, 45.9f, 49.0f, Float.NaN, Float.NaN, Float.NaN
+ };
+ mFakeHal.mCallback.onThresholdChanged(newThreshold);
+ verify(mHeadroomListener, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onHeadroomChange(anyFloat(), anyFloat(), anyInt(), any());
}
@Test
@@ -475,28 +584,34 @@ public class ThermalManagerServiceTest {
}
@Test
- public void testGetThermalHeadroomThresholdsOnlyReadOnce() throws Exception {
+ public void testGetThermalHeadroomThresholds() throws Exception {
float[] expected = new float[]{Float.NaN, 0.1f, 0.2f, 0.3f, 0.4f, Float.NaN, 0.6f};
when(mIThermalServiceMock.getThermalHeadroomThresholds()).thenReturn(expected);
Map<Integer, Float> thresholds1 = mPowerManager.getThermalHeadroomThresholds();
verify(mIThermalServiceMock, times(1)).getThermalHeadroomThresholds();
+ checkHeadroomThresholds(expected, thresholds1);
+
+ reset(mIThermalServiceMock);
+ expected = new float[]{Float.NaN, 0.2f, 0.3f, 0.4f, 0.4f, Float.NaN, 0.6f};
+ when(mIThermalServiceMock.getThermalHeadroomThresholds()).thenReturn(expected);
+ Map<Integer, Float> thresholds2 = mPowerManager.getThermalHeadroomThresholds();
+ verify(mIThermalServiceMock, times(1)).getThermalHeadroomThresholds();
+ checkHeadroomThresholds(expected, thresholds2);
+ }
+
+ private void checkHeadroomThresholds(float[] expected, Map<Integer, Float> thresholds) {
for (int status = PowerManager.THERMAL_STATUS_LIGHT;
status <= PowerManager.THERMAL_STATUS_SHUTDOWN; status++) {
if (Float.isNaN(expected[status])) {
- assertFalse(thresholds1.containsKey(status));
+ assertFalse(thresholds.containsKey(status));
} else {
- assertEquals(expected[status], thresholds1.get(status), 0.01f);
+ assertEquals(expected[status], thresholds.get(status), 0.01f);
}
}
- reset(mIThermalServiceMock);
- Map<Integer, Float> thresholds2 = mPowerManager.getThermalHeadroomThresholds();
- verify(mIThermalServiceMock, times(0)).getThermalHeadroomThresholds();
- assertNotSame(thresholds1, thresholds2);
- assertEquals(thresholds1, thresholds2);
}
@Test
- public void testGetThermalHeadroomThresholdsOnDefaultHalResult() throws Exception {
+ public void testGetThermalHeadroomThresholdsOnDefaultHalResult() throws Exception {
TemperatureWatcher watcher = mService.mTemperatureWatcher;
ArrayList<TemperatureThreshold> thresholds = new ArrayList<>();
mFakeHal.mTemperatureThresholdList = thresholds;
@@ -510,8 +625,8 @@ public class ThermalManagerServiceTest {
TemperatureThreshold nanThresholds = new TemperatureThreshold();
nanThresholds.name = "nan";
nanThresholds.type = Temperature.TYPE_SKIN;
- nanThresholds.hotThrottlingThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
- nanThresholds.coldThrottlingThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
+ nanThresholds.hotThrottlingThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
+ nanThresholds.coldThrottlingThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
Arrays.fill(nanThresholds.hotThrottlingThresholds, Float.NaN);
Arrays.fill(nanThresholds.coldThrottlingThresholds, Float.NaN);
thresholds.add(nanThresholds);
@@ -607,7 +722,13 @@ public class ThermalManagerServiceTest {
}
@Test
- public void testDump() {
+ public void testDump() throws Exception {
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+
when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);
final StringWriter out = new StringWriter();
@@ -628,22 +749,22 @@ public class ThermalManagerServiceTest {
assertThat(dumpStr).contains("Thermal Status: 0");
assertThat(dumpStr).contains(
"Cached temperatures:\n"
- + "\tTemperature{mValue=0.0, mType=4, mName=usbport, mStatus=0}\n"
- + "\tTemperature{mValue=0.0, mType=2, mName=batt, mStatus=0}\n"
- + "\tTemperature{mValue=0.0, mType=3, mName=skin1, mStatus=0}\n"
- + "\tTemperature{mValue=0.0, mType=3, mName=skin2, mStatus=0}"
+ + "\tTemperature{mValue=37.0, mType=4, mName=usbport, mStatus=0}\n"
+ + "\tTemperature{mValue=34.0, mType=2, mName=batt, mStatus=0}\n"
+ + "\tTemperature{mValue=28.0, mType=3, mName=skin1, mStatus=0}\n"
+ + "\tTemperature{mValue=31.0, mType=3, mName=skin2, mStatus=0}"
);
assertThat(dumpStr).contains("HAL Ready: true\n"
+ "HAL connection:\n"
+ "\tThermalHAL AIDL 1 connected: yes");
assertThat(dumpStr).contains("Current temperatures from HAL:\n"
- + "\tTemperature{mValue=0.0, mType=3, mName=skin1, mStatus=0}\n"
- + "\tTemperature{mValue=0.0, mType=3, mName=skin2, mStatus=0}\n"
- + "\tTemperature{mValue=0.0, mType=2, mName=batt, mStatus=0}\n"
- + "\tTemperature{mValue=0.0, mType=4, mName=usbport, mStatus=0}\n");
+ + "\tTemperature{mValue=28.0, mType=3, mName=skin1, mStatus=0}\n"
+ + "\tTemperature{mValue=31.0, mType=3, mName=skin2, mStatus=0}\n"
+ + "\tTemperature{mValue=34.0, mType=2, mName=batt, mStatus=0}\n"
+ + "\tTemperature{mValue=37.0, mType=4, mName=usbport, mStatus=0}\n");
assertThat(dumpStr).contains("Current cooling devices from HAL:\n"
- + "\tCoolingDevice{mValue=0, mType=1, mName=cpu}\n"
- + "\tCoolingDevice{mValue=0, mType=1, mName=gpu}\n");
+ + "\tCoolingDevice{mValue=40, mType=1, mName=cpu}\n"
+ + "\tCoolingDevice{mValue=43, mType=1, mName=gpu}\n");
assertThat(dumpStr).contains("Temperature static thresholds from HAL:\n"
+ "\tTemperatureThreshold{mType=3, mName=skin1, mHotThrottlingThresholds=[25.0, "
+ "30.0, 35.0, 40.0, 45.0, 50.0, 55.0], mColdThrottlingThresholds=[0.0, 0.0, 0.0,"
diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java
index d1806881ee37..154494a13072 100644
--- a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.server.adaptiveauth;
+package com.android.server.security.adaptiveauthentication;
import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH;
import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
-import static com.android.server.adaptiveauth.AdaptiveAuthService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS;
+import static com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
@@ -66,12 +66,12 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
- * atest FrameworksServicesTests:AdaptiveAuthServiceTest
+ * atest FrameworksServicesTests:AdaptiveAuthenticationServiceTest
*/
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class AdaptiveAuthServiceTest {
+public class AdaptiveAuthenticationServiceTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -81,7 +81,7 @@ public class AdaptiveAuthServiceTest {
private static final int REASON_UNKNOWN = 0; // BiometricRequestConstants.RequestReason
private Context mContext;
- private AdaptiveAuthService mAdaptiveAuthService;
+ private AdaptiveAuthenticationService mAdaptiveAuthenticationService;
@Mock
LockPatternUtils mLockPatternUtils;
@@ -124,8 +124,9 @@ public class AdaptiveAuthServiceTest {
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mUserManager);
- mAdaptiveAuthService = new AdaptiveAuthService(mContext, mLockPatternUtils);
- mAdaptiveAuthService.init();
+ mAdaptiveAuthenticationService = new AdaptiveAuthenticationService(
+ mContext, mLockPatternUtils);
+ mAdaptiveAuthenticationService.init();
verify(mLockSettings).registerLockSettingsStateListener(
mLockSettingsStateListenerCaptor.capture());
@@ -317,13 +318,13 @@ public class AdaptiveAuthServiceTest {
private void verifyNotLockDevice(int expectedCntFailedAttempts, int userId) {
assertEquals(expectedCntFailedAttempts,
- mAdaptiveAuthService.mFailedAttemptsForUser.get(userId));
+ mAdaptiveAuthenticationService.mFailedAttemptsForUser.get(userId));
verify(mWindowManager, never()).lockNow();
}
private void verifyLockDevice(int userId) {
assertEquals(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS,
- mAdaptiveAuthService.mFailedAttemptsForUser.get(userId));
+ mAdaptiveAuthenticationService.mFailedAttemptsForUser.get(userId));
verify(mLockPatternUtils).requireStrongAuth(
eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(userId));
// If userId is MANAGED_PROFILE_USER_ID, the StrongAuthFlag of its parent (PRIMARY_USER_ID)
diff --git a/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS
new file mode 100644
index 000000000000..bc8efa92c16f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/security/adaptiveauthentication/OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS b/services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS
new file mode 100644
index 000000000000..e068a845c670
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/stats/pull/psi/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java b/services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java
new file mode 100644
index 000000000000..b563c08f6be0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.stats.pull.psi;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+
+public class PsiExtractorTest {
+ @Mock
+ private PsiExtractor.PsiReader mPsiReader;
+ private PsiExtractor mPsiExtractor;
+ // PSI file content with both some and full lines.
+ private static final String PSI_FILE_CONTENT_BOTH_LINES =
+ "some avg10=0.12 avg60=0.34 avg300=0.56 total=12345678\n"
+ + "full avg10=0.21 avg60=0.43 avg300=0.65 total=87654321";
+ // PSI file content with only some line.
+ private static final String PSI_FILE_CONTENT_ONLY_SOME_LINE =
+ "some avg10=0.12 avg60=0.34 avg300=0.56 total=12345678";
+
+ // PSI file content with only full line.
+ private static final String PSI_FILE_CONTENT_ONLY_FULL_LINE =
+ "\nfull avg10=0.21 avg60=0.43 avg300=0.65 total=87654321";
+
+ // PSI file content that is malformed with "avg60" missing from the both lines.
+ private static final String BOTH_AVG60_MISSING_PSI_FILE_CONTENT =
+ "some avg10=0.12 avg300=0.56 total=12345678\n"
+ + "full avg10=0.21 avg300=0.65 total=87654321";
+
+ // PSI file content that is malformed with non number "avg10" from the both lines.
+ private static final String NON_NUM_AVG10_PSI_FILE_CONTENT =
+ "some avg10=1.a2 avg300=0.56 total=12345678\n"
+ + "full avg10=0.2s1 avg60=0.43 avg300=0.65 total=87654321";
+
+ // PSI file content that is malformed with non number "avg300" from the both lines.
+ private static final String NON_NUM_AVG300_PSI_FILE_CONTENT =
+ "some avg10=0.2 avg60=0.43 avg300=0.5ss6 total=12345678\n"
+ + "full avg10=0.21 avg60=0.43 avg300=0.6b5 total=87654321";
+
+ // PSI file content that is malformed with non number "total" from the both lines.
+ private static final String BOTH_TOTAL_MISSING_PSI_FILE_CONTENT =
+ "some avg10=0.2 avg60=0.43 avg300=0.56\n"
+ + "full avg10=0.21 avg60=0.43 avg300=0.65";
+
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mPsiExtractor = new PsiExtractor(mPsiReader);
+ }
+
+ @Test
+ public void getPsiData_bothLinesPresentedAndValidMemory() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ PSI_FILE_CONTENT_BOTH_LINES);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.MEMORY);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56);
+ assertEquals(psiData.getSomeTotalUsec(), 12345678);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65);
+ assertEquals(psiData.getFullTotalUsec(), 87654321);
+ }
+
+ @Test
+ public void getPsiData_bothLinesPresentedAndValidCpu() {
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ PSI_FILE_CONTENT_BOTH_LINES);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.CPU);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56);
+ assertEquals(psiData.getSomeTotalUsec(), 12345678);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65);
+ assertEquals(psiData.getFullTotalUsec(), 87654321);
+ }
+
+ @Test
+ public void getPsiData_bothLinesPresentedAndValidIO() {
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ PSI_FILE_CONTENT_BOTH_LINES);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.IO);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56);
+ assertEquals(psiData.getSomeTotalUsec(), 12345678);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65);
+ assertEquals(psiData.getFullTotalUsec(), 87654321);
+ }
+
+ @Test
+ public void getPsiData_onlySomePresentedAndValidMemory() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ PSI_FILE_CONTENT_ONLY_SOME_LINE);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.MEMORY);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56);
+ assertEquals(psiData.getSomeTotalUsec(), 12345678);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getFullTotalUsec(), -1);
+ }
+
+ @Test
+ public void getPsiData_onlySomePresentedAndValidCpu() {
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ PSI_FILE_CONTENT_ONLY_SOME_LINE);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.CPU);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56);
+ assertEquals(psiData.getSomeTotalUsec(), 12345678);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.0);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.0);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.0);
+ assertEquals(psiData.getFullTotalUsec(), 0);
+ }
+
+ @Test
+ public void getPsiData_onlySomePresentedAndValidIO() {
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ PSI_FILE_CONTENT_ONLY_SOME_LINE);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.IO);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56);
+ assertEquals(psiData.getSomeTotalUsec(), 12345678);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getFullTotalUsec(), -1);
+ }
+
+ @Test
+ public void getPsiData_onlyFullPresentedAndValidMemory() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ PSI_FILE_CONTENT_ONLY_FULL_LINE);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.MEMORY);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeTotalUsec(), -1);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65);
+ assertEquals(psiData.getFullTotalUsec(), 87654321);
+ }
+
+ @Test
+ public void getPsiData_onlyFullPresentedAndValidCpu() {
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ PSI_FILE_CONTENT_ONLY_FULL_LINE);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.CPU);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeTotalUsec(), -1);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65);
+ assertEquals(psiData.getFullTotalUsec(), 87654321);
+ }
+
+ @Test
+ public void getPsiData_onlyFullPresentedAndValidIO() {
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ PSI_FILE_CONTENT_ONLY_FULL_LINE);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData.getResourceType(), PsiData.ResourceType.IO);
+ assertEquals(psiData.getSomeAvg10SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeAvg60SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeAvg300SecPercentage(), (float) -1.0);
+ assertEquals(psiData.getSomeTotalUsec(), -1);
+ assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21);
+ assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43);
+ assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65);
+ assertEquals(psiData.getFullTotalUsec(), 87654321);
+ }
+
+ @Test
+ public void getPsiData_emptyFile() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn("");
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn("");
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn("");
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData, null);
+ }
+
+ @Test
+ public void getPsiData_avg60Missing() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ BOTH_AVG60_MISSING_PSI_FILE_CONTENT);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ BOTH_AVG60_MISSING_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ BOTH_AVG60_MISSING_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData, null);
+ }
+
+ @Test
+ public void getPsiData_totalMissing() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ BOTH_TOTAL_MISSING_PSI_FILE_CONTENT);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ BOTH_TOTAL_MISSING_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ BOTH_TOTAL_MISSING_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData, null);
+ }
+
+ @Test
+ public void getPsiData_avg10NonNum() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ NON_NUM_AVG10_PSI_FILE_CONTENT);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ NON_NUM_AVG10_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ NON_NUM_AVG10_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData, null);
+ }
+
+ @Test
+ public void getPsiData_avg300NonNum() {
+ Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(
+ NON_NUM_AVG300_PSI_FILE_CONTENT);
+ PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(
+ NON_NUM_AVG300_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU);
+ assertEquals(psiData, null);
+
+ Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(
+ NON_NUM_AVG300_PSI_FILE_CONTENT);
+ psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO);
+ assertEquals(psiData, null);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 6bd4279152ab..79b0623640f6 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -16,12 +16,20 @@
package com.android.server.supervision
+import android.app.admin.DevicePolicyManagerInternal
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.UserInfo
import android.os.Bundle
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.R
import com.android.server.LocalServices
+import com.android.server.SystemService.TargetUser
import com.android.server.pm.UserManagerInternal
import com.google.common.truth.Truth.assertThat
-import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -29,11 +37,12 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
/**
- * Unit tests for {@link SupervisionService}.
- * <p/>
- * Run with <code>atest SupervisionServiceTest</code>.
+ * Unit tests for [SupervisionService].
+ *
+ * Run with `atest SupervisionServiceTest`.
*/
@RunWith(AndroidJUnit4::class)
class SupervisionServiceTest {
@@ -41,18 +50,21 @@ class SupervisionServiceTest {
const val USER_ID = 100
}
- private lateinit var service: SupervisionService
+ @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
- @Rule
- @JvmField
- val mocks: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
+ @Mock private lateinit var mockUserManagerInternal: UserManagerInternal
- @Mock
- private lateinit var mockUserManagerInternal: UserManagerInternal
+ private lateinit var context: Context
+ private lateinit var service: SupervisionService
@Before
- fun setup() {
- val context = InstrumentationRegistry.getInstrumentation().context
+ fun setUp() {
+ context = InstrumentationRegistry.getInstrumentation().context
+
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
+ LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
LocalServices.removeServiceForTest(UserManagerInternal::class.java)
LocalServices.addService(UserManagerInternal::class.java, mockUserManagerInternal)
@@ -61,7 +73,46 @@ class SupervisionServiceTest {
}
@Test
- fun testSetSupervisionEnabledForUser() {
+ @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
+ fun syncStateWithDevicePolicyManager_supervisionAppIsProfileOwner_enablesSupervision() {
+ val supervisionPackageName =
+ context.getResources().getString(R.string.config_systemSupervision)
+
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(ComponentName(supervisionPackageName, "MainActivity"))
+
+ service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID))
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
+ fun syncStateWithDevicePolicyManager_userPreCreated_doesNotEnableSupervision() {
+ val supervisionPackageName =
+ context.getResources().getString(R.string.config_systemSupervision)
+
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(ComponentName(supervisionPackageName, "MainActivity"))
+
+ service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID, preCreated = true))
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
+ fun syncStateWithDevicePolicyManager_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(ComponentName("other.package", "MainActivity"))
+
+ service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID))
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ }
+
+ @Test
+ fun setSupervisionEnabledForUser() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
service.setSupervisionEnabledForUser(USER_ID, true)
@@ -72,7 +123,18 @@ class SupervisionServiceTest {
}
@Test
- fun testSetSupervisionLockscreenEnabledForUser() {
+ fun supervisionEnabledForUser_internal() {
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ }
+
+ @Test
+ fun setSupervisionLockscreenEnabledForUser() {
var userData = service.getUserDataLocked(USER_ID)
assertThat(userData.supervisionLockScreenEnabled).isFalse()
assertThat(userData.supervisionLockScreenOptions).isNull()
@@ -87,4 +149,10 @@ class SupervisionServiceTest {
assertThat(userData.supervisionLockScreenEnabled).isFalse()
assertThat(userData.supervisionLockScreenOptions).isNull()
}
+
+ private fun newTargetUser(userId: Int, preCreated: Boolean = false): TargetUser {
+ val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
+ userInfo.preCreated = preCreated
+ return TargetUser(userInfo)
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 24abc183cad1..f5494534716a 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -61,7 +61,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import org.junit.Before;
@@ -77,9 +76,6 @@ import java.util.Set;
@RunWith(Parameterized.class)
public class UriGrantsManagerServiceTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
/**
* Why this class needs to test all combinations of
* {@link android.security.Flags#FLAG_CONTENT_URI_PERMISSION_APIS}:
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
index 611c51463246..fe66f738487d 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
@@ -37,18 +37,13 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.os.SystemClock;
-import android.platform.test.ravenwood.RavenwoodRule;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class UriPermissionTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
@Mock
private UriGrantsManagerInternal mService;
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index c9d5241c57b7..b3ec2153542a 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -30,7 +30,6 @@ import android.testing.TestableContext;
import androidx.test.InstrumentationRegistry;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
@@ -42,7 +41,6 @@ import org.mockito.MockitoAnnotations;
public class UiServiceTestCase {
@Mock protected PackageManagerInternal mPmi;
- @Mock protected UserManagerInternal mUmi;
@Mock protected UriGrantsManagerInternal mUgmInternal;
protected static final String PKG_N_MR1 = "com.example.n_mr1";
@@ -94,8 +92,6 @@ public class UiServiceTestCase {
}
});
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.addService(UserManagerInternal.class, mUmi);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
when(mUgmInternal.checkGrantUriPermission(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 22a4f85758eb..cc0286508cdc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -34,10 +34,12 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS;
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS;
import static com.android.server.notification.GroupHelper.AGGREGATE_GROUP_KEY;
import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
import static com.android.server.notification.GroupHelper.BASE_FLAGS;
@@ -2217,6 +2219,7 @@ public class GroupHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+ @DisableFlags(FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION)
public void testMoveAggregateGroups_updateChannel_multipleChannels() {
final String pkg = "package";
final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
@@ -2265,16 +2268,17 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
notificationList);
- // Check that channel1's notifications are moved to the silent section group
- // But not enough to auto-group => remove override group key
- verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
- anyString(), anyInt(), any());
- verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ // Check that the override group key was cleared
for (NotificationRecord record: notificationList) {
if (record.getChannel().getId().equals(channel1.getId())) {
assertThat(record.getSbn().getOverrideGroupKey()).isNull();
}
}
+ // Check that channel1's notifications are moved to the silent section group
+ // and a group summary is created + notifications are added to the group
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
+ anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
// Check that the alerting section group is not removed, only updated
expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
@@ -2287,6 +2291,428 @@ public class GroupHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testMoveAggregateGroups_updateChannel_multipleChannels_regroupOnClassifEnabled() {
+ final String pkg = "package";
+ final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ int numNotificationChannel1 = 0;
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
+ "TEST_CHANNEL_ID2", IMPORTANCE_DEFAULT);
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ // Post notifications with different channels that autogroup within the same section
+ NotificationRecord r;
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ if (i % 2 == 0) {
+ r = getNotificationRecord(pkg, i, String.valueOf(i),
+ UserHandle.SYSTEM, "testGrp " + i, false, channel1);
+ numNotificationChannel1++;
+ } else {
+ r = getNotificationRecord(pkg, i, String.valueOf(i),
+ UserHandle.SYSTEM, "testGrp " + i, false, channel2);
+ }
+ notificationList.add(r);
+ mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
+ }
+ NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
+ mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ "TEST_CHANNEL_ID1");
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_alerting), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ Mockito.reset(mCallback);
+
+ // Update channel1's importance
+ final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
+ channel1.setImportance(IMPORTANCE_LOW);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ record.updateNotificationChannel(channel1);
+ }
+ }
+ mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
+ notificationList);
+
+ // Check that the override group key was cleared
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())) {
+ assertThat(record.getSbn().getOverrideGroupKey()).isNull();
+ }
+ }
+ // Check that channel1's notifications are moved to the silent section group
+ // and a group summary is created + notifications are added to the group
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_silent), anyInt(), any());
+ verify(mCallback, times(numNotificationChannel1)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_silent), anyBoolean());
+
+ // Check that the alerting section group is not removed, only updated
+ expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
+ mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ "TEST_CHANNEL_ID2");
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_alerting));
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_alerting), eq(expectedSummaryAttr));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testMoveSections_notificationBundled() {
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final String pkg = "package";
+ final int summaryId = 0;
+ final int numChildNotif = 4;
+
+ // Create an app-provided group: summary + child notifications
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId,
+ true, channel1);
+ notificationList.add(summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ for (int i = 0; i < numChildNotif; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp " + summaryId, false, channel1);
+ notificationList.add(child);
+ }
+
+ // Classify/bundle child notifications
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ final NotificationChannel newsChannel = new NotificationChannel(
+ NotificationChannel.NEWS_ID, NotificationChannel.NEWS_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_news = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "NewsSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_news = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.NEWS_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())
+ && record.getNotification().isGroupChild()
+ && record.getSbn().getId() % 2 == 0) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ if (record.getChannel().getId().equals(channel1.getId())
+ && record.getNotification().isGroupChild()
+ && record.getSbn().getId() % 2 != 0) {
+ record.updateNotificationChannel(newsChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that 2 autogroup summaries were created for the news & social sections
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_news), anyInt(), eq(expectedSummaryAttr_news));
+ // Check that half of the child notifications were grouped in each new section
+ verify(mCallback, times(numChildNotif / 2)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_news), eq(true));
+ verify(mCallback, times(numChildNotif / 2)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, times(numChildNotif / 2)).updateAutogroupSummary(anyInt(), anyString(),
+ anyString(), any());
+ verify(mCallback, times(numChildNotif)).removeAppProvidedSummaryOnClassification(
+ anyString(), eq(originalAppGroupKey));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testCacheAndCancelAppSummary_notificationBundled() {
+ // check that the original app summary is canceled & cached on classification regrouping
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final String pkg = "package";
+ final int summaryId = 0;
+ final int numChildNotif = 4;
+
+ // Create an app-provided group: summary + child notifications
+ final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+ "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId,
+ true, channel1);
+ notificationList.add(summary);
+ final String originalAppGroupKey = summary.getGroupKey();
+ final String originalAppGroupName = summary.getNotification().getGroup();
+ for (int i = 0; i < numChildNotif; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp " + summaryId, false, channel1);
+ notificationList.add(child);
+ }
+
+ // Last regrouped notification will trigger summary cancellation in NMS
+ when(mCallback.removeAppProvidedSummaryOnClassification(anyString(),
+ eq(originalAppGroupKey))).thenReturn(summary);
+
+ // Classify/bundle child notifications
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ for (NotificationRecord record: notificationList) {
+ if (record.getChannel().getId().equals(channel1.getId())
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that the original app summary was cached
+ CachedSummary cachedSummary = mGroupHelper.findCanceledSummary(pkg,
+ String.valueOf(summaryId), summaryId, UserHandle.SYSTEM.getIdentifier());
+ assertThat(cachedSummary.originalGroupKey()).isEqualTo(originalAppGroupName);
+ assertThat(cachedSummary.key()).isEqualTo(summary.getKey());
+
+ // App cancels the original summary
+ reset(mCallback);
+ mGroupHelper.maybeCancelGroupChildrenForCanceledSummary(pkg, String.valueOf(summaryId),
+ summaryId, UserHandle.SYSTEM.getIdentifier(), REASON_APP_CANCEL);
+ // Check that child notifications are removed and cache is cleared
+ verify(mCallback, times(1)).removeNotificationFromCanceledGroup(
+ eq(UserHandle.SYSTEM.getIdentifier()), eq(pkg), eq(originalAppGroupName),
+ eq(REASON_APP_CANCEL));
+ cachedSummary = mGroupHelper.findCanceledSummary(pkg, String.valueOf(summaryId), summaryId,
+ UserHandle.SYSTEM.getIdentifier());
+ assertThat(cachedSummary).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
+ public void testSingletonGroupsRegrouped_notificationBundledBeforeDelayTimeout() {
+ // Check that singleton group notifications are regrouped if classification is done
+ // before onNotificationPostedWithDelay
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ // Post singleton groups, above forced group limit
+ for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ NotificationRecord summary = getNotificationRecord(pkg, i,
+ String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
+ notificationList.add(summary);
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp " + i, false);
+ notificationList.add(child);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ }
+
+ // Classify/bundle child notifications
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that notifications are forced grouped and app-provided summaries are canceled
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(2)).removeAppProvidedSummaryOnClassification(
+ anyString(), anyString());
+
+ // Adjust group key and cancel summaries
+ for (NotificationRecord record: notificationList) {
+ if (record.getNotification().isGroupSummary()) {
+ record.isCanceled = true;
+ } else {
+ record.setOverrideGroupKey(expectedGroupKey_social);
+ }
+ }
+
+ // Check that after onNotificationPostedWithDelay there is no change in the grouping
+ reset(mCallback);
+ for (NotificationRecord record: notificationList) {
+ mGroupHelper.onNotificationPostedWithDelay(record, notificationList, summaryByGroup);
+ }
+
+ verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any());
+ verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
+ public void testSingletonGroupsRegrouped_notificationBundledAfterDelayTimeout() {
+ // Check that singleton group notifications are regrouped if classification is done
+ // after onNotificationPostedWithDelay
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+ final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
+ String expectedTriggeringKey = null;
+ // Post singleton groups, above forced group limit
+ for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
+ NotificationRecord summary = getNotificationRecord(pkg, i,
+ String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
+ notificationList.add(summary);
+ NotificationRecord child = getNotificationRecord(pkg, i + 42,
+ String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
+ notificationList.add(child);
+ expectedTriggeringKey = child.getKey();
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
+ summary.isCanceled = true; // simulate removing the app summary
+ mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
+ }
+
+ // Check that notifications are forced grouped and app-provided summaries are canceled
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedTriggeringKey), eq(expectedGroupKey_alerting), anyInt(),
+ eq(getNotificationAttributes(BASE_FLAGS)));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_alerting), eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+ any());
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(
+ anyString());
+ assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(0), 0,
+ UserHandle.SYSTEM.getIdentifier())).isNotNull();
+ assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(1), 1,
+ UserHandle.SYSTEM.getIdentifier())).isNotNull();
+
+ // Classify/bundle child notifications
+ reset(mCallback);
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ mGroupHelper.onChannelUpdated(record);
+ }
+ }
+
+ // Check that all notifications are moved to the social section group
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
+ eq(expectedGroupKey_social), eq(true));
+ // Check that the alerting section group is removed
+ verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
+ eq(expectedGroupKey_alerting));
+ verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).updateAutogroupSummary(anyInt(),
+ anyString(), anyString(), any());
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
+ FLAG_NOTIFICATION_CLASSIFICATION})
+ public void testValidGroupsRegrouped_notificationBundledWhileEnqueued() {
+ // Check that valid group notifications are regrouped if classification is done
+ // before onNotificationPostedWithDelay (within DELAY_FOR_ASSISTANT_TIME)
+ final List<NotificationRecord> notificationList = new ArrayList<>();
+ final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
+ final String pkg = "package";
+
+ final int summaryId = 0;
+ final int numChildren = 3;
+ // Post a regular/valid group: summary + notifications
+ NotificationRecord summary = getNotificationRecord(pkg, summaryId,
+ String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
+ notificationList.add(summary);
+ summaryByGroup.put(summary.getGroupKey(), summary);
+ for (int i = 0; i < numChildren; i++) {
+ NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
+ UserHandle.SYSTEM, "testGrp", false);
+ notificationList.add(child);
+ }
+
+ // Classify/bundle child notifications. Don't call onChannelUpdated,
+ // adjustments applied while enqueued will use NotificationAdjustmentExtractor.
+ final NotificationChannel socialChannel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+ IMPORTANCE_DEFAULT);
+ final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
+ AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
+ final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
+ BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+ NotificationChannel.SOCIAL_MEDIA_ID);
+ for (NotificationRecord record: notificationList) {
+ if (record.getOriginalGroupKey().contains("testGrp")
+ && record.getNotification().isGroupChild()) {
+ record.updateNotificationChannel(socialChannel);
+ }
+ }
+
+ // Check that notifications are forced grouped and app-provided summaries are canceled
+ for (NotificationRecord record: notificationList) {
+ mGroupHelper.onNotificationPostedWithDelay(record, notificationList, summaryByGroup);
+ }
+
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
+ verify(mCallback, times(numChildren)).addAutoGroup(anyString(), eq(expectedGroupKey_social),
+ eq(true));
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+ verify(mCallback, times(numChildren - 1)).updateAutogroupSummary(anyInt(), anyString(),
+ anyString(), any());
+ verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(anyString(),
+ anyString());
+ }
+
+ @Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
public void testMoveAggregateGroups_updateChannel_groupsUngrouped() {
final String pkg = "package";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index b5724b5c0cc8..e5c42082ab97 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -21,10 +21,8 @@ import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
-import static com.android.server.notification.Flags.FLAG_NOTIFICATION_NLS_REBIND;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
import static com.android.server.notification.NotificationManagerService.privateSpaceFlagsEnabled;
@@ -65,14 +63,11 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
-import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -87,9 +82,7 @@ import com.android.server.UiServiceTestCase;
import com.google.android.collect.Lists;
-import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -110,10 +103,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-
public class ManagedServicesTest extends UiServiceTestCase {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@Mock
private IPackageManager mIpm;
@@ -125,7 +115,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
private ManagedServices.UserProfiles mUserProfiles;
@Mock private DevicePolicyManager mDpm;
Object mLock = new Object();
- private TestableLooper mTestableLooper;
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
@@ -153,7 +142,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTestableLooper = new TestableLooper(Looper.getMainLooper());
mContext.setMockPackageManager(mPm);
mContext.addMockSystemService(Context.USER_SERVICE, mUm);
@@ -211,11 +199,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
mIpm, APPROVAL_BY_COMPONENT);
}
- @After
- public void tearDown() throws Exception {
- mTestableLooper.destroy();
- }
-
@Test
public void testBackupAndRestore_migration() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -905,7 +888,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, true);
service.reregisterService(cn, 0);
@@ -936,7 +919,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a", 0, false);
service.reregisterService(cn, 0);
@@ -967,7 +950,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, true);
service.reregisterService(cn, 0);
@@ -998,7 +981,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
service.addApprovedList("a/a", 0, false);
service.reregisterService(cn, 0);
@@ -1070,78 +1053,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void registerService_bindingDied_rebindIsClearedOnUserSwitch() throws Exception {
- Context context = mock(Context.class);
- PackageManager pm = mock(PackageManager.class);
- ApplicationInfo ai = new ApplicationInfo();
- ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-
- when(context.getPackageName()).thenReturn(mPkg);
- when(context.getUserId()).thenReturn(mUser.getIdentifier());
- when(context.getPackageManager()).thenReturn(pm);
- when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
- APPROVAL_BY_PACKAGE);
- service = spy(service);
- ComponentName cn = ComponentName.unflattenFromString("a/a");
-
- // Trigger onBindingDied for component when registering
- // => will schedule a rebind in 10 seconds
- when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
- Object[] args = invocation.getArguments();
- ServiceConnection sc = (ServiceConnection) args[1];
- sc.onBindingDied(cn);
- return true;
- });
- service.registerService(cn, 0);
- assertThat(service.isBound(cn, 0)).isFalse();
-
- // Switch to user 10
- service.onUserSwitched(10);
-
- // Check that the scheduled rebind for user 0 was cleared
- mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
- mTestableLooper.processAllMessages();
- verify(service, never()).reregisterService(any(), anyInt());
- }
-
- @Test
- public void registerService_bindingDied_rebindIsExecutedAfterTimeout() throws Exception {
- Context context = mock(Context.class);
- PackageManager pm = mock(PackageManager.class);
- ApplicationInfo ai = new ApplicationInfo();
- ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-
- when(context.getPackageName()).thenReturn(mPkg);
- when(context.getUserId()).thenReturn(mUser.getIdentifier());
- when(context.getPackageManager()).thenReturn(pm);
- when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
- APPROVAL_BY_PACKAGE);
- service = spy(service);
- ComponentName cn = ComponentName.unflattenFromString("a/a");
-
- // Trigger onBindingDied for component when registering
- // => will schedule a rebind in 10 seconds
- when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
- Object[] args = invocation.getArguments();
- ServiceConnection sc = (ServiceConnection) args[1];
- sc.onBindingDied(cn);
- return true;
- });
- service.registerService(cn, 0);
- assertThat(service.isBound(cn, 0)).isFalse();
-
- // Check that the scheduled rebind is run
- mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
- mTestableLooper.processAllMessages();
- verify(service, times(1)).reregisterService(eq(cn), eq(0));
- }
-
- @Test
public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1300,65 +1211,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
- Context context = spy(getContext());
- doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
-
- ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- List<String> packages = new ArrayList<>();
- packages.add("package");
- addExpectedServices(service, packages, 0);
-
- final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
- final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
-
- // Both components are approved initially
- mExpectedPrimaryComponentNames.clear();
- mExpectedPrimaryPackages.clear();
- mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
- mExpectedSecondaryComponentNames.clear();
- mExpectedSecondaryPackages.clear();
-
- loadXml(service);
-
- //Component package/C1 loses serviceInterface intent filter
- ManagedServices.Config config = service.getConfig();
- when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
- .thenAnswer(new Answer<List<ResolveInfo>>() {
- @Override
- public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
- throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Intent invocationIntent = (Intent) args[0];
- if (invocationIntent != null) {
- if (invocationIntent.getAction().equals(config.serviceInterface)
- && packages.contains(invocationIntent.getPackage())) {
- List<ResolveInfo> dummyServices = new ArrayList<>();
- ResolveInfo resolveInfo = new ResolveInfo();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = invocationIntent.getPackage();
- serviceInfo.name = approvedComponent.getClassName();
- serviceInfo.permission = service.getConfig().bindPermission;
- resolveInfo.serviceInfo = serviceInfo;
- dummyServices.add(resolveInfo);
- return dummyServices;
- }
- }
- return new ArrayList<>();
- }
- });
-
- // Trigger package update
- service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
-
- assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
- assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
- }
-
- @Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1371,21 +1223,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
"user10package1/K", "user10.3/Component", "user10package2/L",
"user10.4/Component"}));
- // mock permissions for services
- PackageManager pm = mock(PackageManager.class);
- when(getContext().getPackageManager()).thenReturn(pm);
- List<ComponentName> enabledComponents = List.of(
- ComponentName.unflattenFromString("package/Comp"),
- ComponentName.unflattenFromString("package/C2"),
- ComponentName.unflattenFromString("again/M4"),
- ComponentName.unflattenFromString("user10package/B"),
- ComponentName.unflattenFromString("user10/Component"),
- ComponentName.unflattenFromString("user10package1/K"),
- ComponentName.unflattenFromString("user10.3/Component"),
- ComponentName.unflattenFromString("user10package2/L"),
- ComponentName.unflattenFromString("user10.4/Component"));
- mockServiceInfoWithMetaData(enabledComponents, service, pm, new ArrayMap<>());
-
for (int userId : expectedEnabled.keySet()) {
ArrayList<String> expectedForUser = expectedEnabled.get(userId);
for (int i = 0; i < expectedForUser.size(); i++) {
@@ -1447,90 +1284,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void testSetPackageOrComponentEnabled_pkgInstalledAfterEnabling() throws Exception {
- ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- final int userId = 0;
- final String validComponent = "again/M4";
- ArrayList<String> expectedEnabled = Lists.newArrayList("package/Comp", "package/C2",
- validComponent);
-
- PackageManager pm = mock(PackageManager.class);
- when(getContext().getPackageManager()).thenReturn(pm);
- service = spy(service);
-
- // Component again/M4 is a valid service and the package is available
- doReturn(true).when(service)
- .isValidService(ComponentName.unflattenFromString(validComponent), userId);
- when(pm.isPackageAvailable("again")).thenReturn(true);
-
- // "package" is not available and its services are not valid
- doReturn(false).when(service)
- .isValidService(ComponentName.unflattenFromString("package/Comp"), userId);
- doReturn(false).when(service)
- .isValidService(ComponentName.unflattenFromString("package/C2"), userId);
- when(pm.isPackageAvailable("package")).thenReturn(false);
-
- // Enable all components
- for (String component: expectedEnabled) {
- service.setPackageOrComponentEnabled(component, userId, true, true);
- }
-
- // Verify everything added is approved
- for (String component: expectedEnabled) {
- assertTrue("Not allowed: user: " + userId + " entry: " + component
- + " for approval level " + APPROVAL_BY_COMPONENT,
- service.isPackageOrComponentAllowed(component, userId));
- }
-
- // Add missing package "package"
- service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
-
- // Check that component of "package" are not enabled
- assertFalse(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString("package/Comp")));
- assertFalse(service.isPackageOrComponentAllowed("package/Comp", userId));
-
- assertFalse(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString("package/C2")));
- assertFalse(service.isPackageOrComponentAllowed("package/C2", userId));
-
- // Check that the valid components are still enabled
- assertTrue(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString(validComponent)));
- assertTrue(service.isPackageOrComponentAllowed(validComponent, userId));
- }
-
- @Test
- @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
- public void testSetPackageOrComponentEnabled_invalidComponent() throws Exception {
- ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
-
- final int userId = 0;
- final String invalidComponent = "package/Comp";
-
- PackageManager pm = mock(PackageManager.class);
- when(getContext().getPackageManager()).thenReturn(pm);
- service = spy(service);
-
- // Component is an invalid service and the package is available
- doReturn(false).when(service)
- .isValidService(ComponentName.unflattenFromString(invalidComponent), userId);
- when(pm.isPackageAvailable("package")).thenReturn(true);
- service.setPackageOrComponentEnabled(invalidComponent, userId, true, true);
-
- // Verify that the component was not enabled
- assertFalse("Not allowed: user: " + userId + " entry: " + invalidComponent
- + " for approval level " + APPROVAL_BY_COMPONENT,
- service.isPackageOrComponentAllowed(invalidComponent, userId));
- assertFalse(service.isComponentEnabledForCurrentProfiles(
- ComponentName.unflattenFromString(invalidComponent)));
- }
-
- @Test
public void testGetAllowedPackages_byUser() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1731,7 +1484,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
}
- @SuppressWarnings("GuardedBy")
@Test
public void populateComponentsToBind() {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
@@ -1755,8 +1507,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
- service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser,
- /* isVisibleBackgroundUser= */ false);
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
assertEquals(2, componentsToBind.size());
assertEquals(1, componentsToBind.get(0).size());
@@ -1766,33 +1517,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
}
- @SuppressWarnings("GuardedBy")
- @Test
- public void populateComponentsToBind_isVisibleBackgroundUser_addComponentsToBindButNotAddToEnabledComponent() {
- ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
- APPROVAL_BY_COMPONENT);
-
- SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
- ArraySet<ComponentName> allowed = new ArraySet<>();
- allowed.add(ComponentName.unflattenFromString("pkg1/cmp1"));
- approvedComponentsByUser.put(11, allowed);
- IntArray users = new IntArray();
- users.add(11);
-
- SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
-
- service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser,
- /* isVisibleBackgroundUser= */ true);
-
- assertEquals(1, componentsToBind.size());
- assertEquals(1, componentsToBind.get(11).size());
- assertTrue(componentsToBind.get(11).contains(ComponentName.unflattenFromString(
- "pkg1/cmp1")));
- assertThat(service.isComponentEnabledForCurrentProfiles(
- new ComponentName("pkg1", "cmp1"))).isFalse();
- assertThat(service.isComponentEnabledForPackage("pkg1")).isFalse();
- }
-
@Test
public void testOnNullBinding() throws Exception {
Context context = mock(Context.class);
@@ -2191,7 +1915,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
metaDatas.put(cn_allowed, metaDataAutobindAllow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_allowed.flattenToString(), 0, true);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2236,7 +1960,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2275,7 +1999,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2346,8 +2070,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
- ManagedServices service, PackageManager packageManager,
- ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
+ ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
+ throws RemoteException {
when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
(Answer<ServiceInfo>) invocation -> {
ComponentName invocationCn = invocation.getArgument(0);
@@ -2362,39 +2086,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
return null;
}
);
-
- // add components to queryIntentServicesAsUser response
- final List<String> packages = new ArrayList<>();
- for (ComponentName cn: componentNames) {
- packages.add(cn.getPackageName());
- }
- ManagedServices.Config config = service.getConfig();
- when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
- thenAnswer(new Answer<List<ResolveInfo>>() {
- @Override
- public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
- throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Intent invocationIntent = (Intent) args[0];
- if (invocationIntent != null) {
- if (invocationIntent.getAction().equals(config.serviceInterface)
- && packages.contains(invocationIntent.getPackage())) {
- List<ResolveInfo> dummyServices = new ArrayList<>();
- for (ComponentName cn: componentNames) {
- ResolveInfo resolveInfo = new ResolveInfo();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = invocationIntent.getPackage();
- serviceInfo.name = cn.getClassName();
- serviceInfo.permission = service.getConfig().bindPermission;
- resolveInfo.serviceInfo = serviceInfo;
- dummyServices.add(resolveInfo);
- }
- return dummyServices;
- }
- }
- return new ArrayList<>();
- }
- });
}
private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 2c645e0ca353..6eb2f718a0e9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -17,6 +17,9 @@ package com.android.server.notification;
import static android.os.UserHandle.USER_ALL;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS;
@@ -28,7 +31,6 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
-
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
@@ -58,6 +60,8 @@ import android.testing.TestableContext;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
+import android.util.Slog;
import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
@@ -198,8 +202,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testWriteXml_userTurnedOffNAS() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
-
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
@@ -435,10 +437,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
-
- doReturn(true).when(mAssistants).isValidService(eq(component1), eq(mZero.id));
- doReturn(true).when(mAssistants).isValidService(eq(component2), eq(mZero.id));
-
mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
true, true);
verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal(
@@ -584,7 +582,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetAdjustmentTypeSupportedState() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -608,7 +605,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetAdjustmentTypeSupportedState_readWriteXml_entries() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -632,7 +628,6 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
public void testSetAdjustmentTypeSupportedState_readWriteXml_empty() throws Exception {
int userId = ActivityManager.getCurrentUser();
- doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -690,4 +685,47 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
assertThat(mAssistants.getAllowedAssistantAdjustments())
.containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENTS);
}
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testSetAssistantAdjustmentKeyTypeState_allow() {
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactly(TYPE_PROMOTION);
+
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION));
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testSetAssistantAdjustmentKeyTypeState_disallow() {
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testDisallowAdjustmentKeyType_readWriteXml() throws Exception {
+ mAssistants.loadDefaultsFromConfig(true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
+
+ writeXmlAndReload(USER_ALL);
+
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION));
+ }
+
+ @Test
+ public void testDefaultAllowedKeyAdjustments_readWriteXml() throws Exception {
+ mAssistants.loadDefaultsFromConfig(true);
+
+ writeXmlAndReload(USER_ALL);
+
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
+ .containsExactly(TYPE_PROMOTION);
+ }
} \ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 828c1b65765a..704c1b858b8d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -113,6 +113,7 @@ import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
@@ -209,6 +210,7 @@ import android.app.Person;
import android.app.RemoteInput;
import android.app.RemoteInputHistoryItem;
import android.app.StatsManager;
+import android.app.ZenBypassingApp;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.BackupRestoreEventLogger;
import android.app.job.JobScheduler;
@@ -2684,6 +2686,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testAggregateGroups_RemoveAppSummary_onClassification() throws Exception {
+ final String originalGroupName = "originalGroup";
+ final int summaryId = 0;
+ final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 1, originalGroupName, false);
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 2, originalGroupName, false);
+ mService.addNotification(r2);
+ final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
+ summaryId, originalGroupName, true);
+ mService.addNotification(summary);
+ final String originalGroupKey = summary.getGroupKey();
+ assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary);
+
+ // Regroup first child notification
+ r1.setOverrideGroupKey("newGroup");
+ // Check that removeAppProvidedSummaryOnClassificationLocked is null
+ // => there is still one child left in the original group
+ assertThat(mService.removeAppProvidedSummaryOnClassificationLocked(r1.getKey(),
+ originalGroupKey)).isNull();
+
+ // Regroup last child notification
+ r2.setOverrideGroupKey("newGroup");
+ // Check that removeAppProvidedSummaryOnClassificationLocked returns the original summary
+ // and that the original app-provided summary is canceled
+ assertThat(mService.removeAppProvidedSummaryOnClassificationLocked(r2.getKey(),
+ originalGroupKey)).isEqualTo(summary);
+ waitForIdle();
+ verify(mWorkerHandler, times(1)).scheduleCancelNotification(any(), eq(summaryId));
+ assertThat(mService.mSummaryByGroupKey).doesNotContainKey(originalGroupKey);
+ }
+
+ @Test
@EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
public void testUngroupingAggregateSummary() throws Exception {
final String originalGroupName = "originalGroup";
@@ -7489,6 +7526,64 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testClassificationChannelAdjustmentsLogged() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+
+ // Set up notifications that will be adjusted
+ final NotificationRecord r1 = spy(generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true));
+ when(r1.getLifespanMs(anyLong())).thenReturn(234);
+
+ r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ // Enqueues the notification to be posted, so hasPosted will be false.
+ mService.addEnqueuedNotification(r1);
+
+ // Test an adjustment for an enqueued notification
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment1 = new Adjustment(
+ r1.getSbn().getPackageName(), r1.getKey(), signals, "",
+ r1.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment1);
+ assertTrue(mService.checkLastClassificationChannelLog(false /*hasPosted*/,
+ true /*isAlerting*/, 3 /*TYPE_NEWS*/, 234));
+
+ // Set up notifications that will be adjusted
+ // This notification starts on a low importance channel, so isAlerting is false.
+ NotificationChannel mLowImportanceNotificationChannel = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_LOW);
+ final NotificationRecord r2 = spy(generateNotificationRecord(
+ mLowImportanceNotificationChannel, 1, null, true));
+ when(r2.getLifespanMs(anyLong())).thenReturn(345);
+
+ r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ // Adds the notification as already posted, so hasPosted will be true.
+ mService.addNotification(r2);
+ // The signal is removed when used so it has to be readded.
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment2 = new Adjustment(
+ r2.getSbn().getPackageName(), r2.getKey(), signals, "",
+ r2.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment2);
+ assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
+ false /*isAlerting*/, 3 /*TYPE_NEWS*/, 345)); // currently failing
+
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_PROMOTION);
+ Adjustment adjustment3 = new Adjustment(
+ r2.getSbn().getPackageName(), r2.getKey(), signals, "",
+ r2.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment3);
+ assertTrue(mService.checkLastClassificationChannelLog(true /*hasPosted*/,
+ false /*isAlerting*/, 1 /*TYPE_PROMOTION*/, 345));
+ }
+
+ @Test
public void testAdjustmentToImportanceNone_cancelsNotification() throws Exception {
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
@@ -13103,6 +13198,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void getPackagesBypassingDnd_blocked()
+ throws RemoteException, PackageManager.NameNotFoundException {
+
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id4", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(false);
+ // has DND access, so can set bypassDnd attribute
+ mService.mPreferencesHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
+ /*has DND access*/ true, UID_N_MR1, false);
+ mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel2, true, true,
+ UID_P, false);
+ mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true,
+ UID_P, false);
+
+ when(mPackageManager.getPackageUid(eq(PKG_P), anyLong(), anyInt())).thenReturn(UID_P);
+ when(mPackageManager.getPackageUid(eq(PKG_N_MR1), anyLong(), anyInt()))
+ .thenReturn(UID_N_MR1);
+ when(mPermissionHelper.hasPermission(UID_N_MR1)).thenReturn(false);
+ when(mPermissionHelper.hasPermission(UID_P)).thenReturn(true);
+
+ assertThat(mBinderService.getPackagesBypassingDnd(UserHandle.getUserId(UID_P)).getList())
+ .containsExactly(new ZenBypassingApp(PKG_P, false));
+ }
+
+ @Test
public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
@@ -13116,125 +13242,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testGetPackagesBypassingDnd_empty() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true);
+ List<String> result = mBinderService.getPackagesBypassingDnd(mUserId).getList();
assertThat(result).isEmpty();
}
@Test
- public void testGetPackagesBypassingDnd_excludeConversationChannels() throws RemoteException {
- mService.setPreferencesHelper(mPreferencesHelper);
-
- // Set packages
- PackageInfo pkg0 = new PackageInfo();
- pkg0.packageName = "pkg0";
- pkg0.applicationInfo = new ApplicationInfo();
- pkg0.applicationInfo.uid = mUid;
- PackageInfo pkg1 = new PackageInfo();
- pkg1.packageName = "pkg1";
- pkg1.applicationInfo = new ApplicationInfo();
- pkg1.applicationInfo.uid = mUid;
- PackageInfo pkg2 = new PackageInfo();
- pkg2.packageName = "pkg2";
- pkg2.applicationInfo = new ApplicationInfo();
- pkg2.applicationInfo.uid = mUid;
-
- when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId))
- .thenReturn(List.of(pkg0, pkg1, pkg2));
-
- // Conversation channels
- NotificationChannel nc0 = new NotificationChannel("id0", "id0",
- NotificationManager.IMPORTANCE_HIGH);
- nc0.setConversationId("parentChannel", "conversationId");
-
- // Demoted conversation channel
- NotificationChannel nc1 = new NotificationChannel("id1", "id1",
- NotificationManager.IMPORTANCE_HIGH);
- nc1.setConversationId("parentChannel", "conversationId");
- nc1.setDemoted(true);
-
- // Non-conversation channels
- NotificationChannel nc2 = new NotificationChannel("id2", "id2",
- NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel nc3 = new NotificationChannel("id3", "id3",
- NotificationManager.IMPORTANCE_HIGH);
-
- ParceledListSlice<NotificationChannel> pls0 =
- new ParceledListSlice(ImmutableList.of(nc0));
- ParceledListSlice<NotificationChannel> pls1 =
- new ParceledListSlice(ImmutableList.of(nc1));
- ParceledListSlice<NotificationChannel> pls2 =
- new ParceledListSlice(ImmutableList.of(nc2, nc3));
-
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid))
- .thenReturn(pls0);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid))
- .thenReturn(pls1);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid))
- .thenReturn(pls2);
-
- List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, false);
-
- assertThat(result).containsExactly("pkg1", "pkg2");
- }
-
- @Test
- public void testGetPackagesBypassingDnd_includeConversationChannels() throws RemoteException {
- mService.setPreferencesHelper(mPreferencesHelper);
-
- // Set packages
- PackageInfo pkg0 = new PackageInfo();
- pkg0.packageName = "pkg0";
- pkg0.applicationInfo = new ApplicationInfo();
- pkg0.applicationInfo.uid = mUid;
- PackageInfo pkg1 = new PackageInfo();
- pkg1.packageName = "pkg1";
- pkg1.applicationInfo = new ApplicationInfo();
- pkg1.applicationInfo.uid = mUid;
- PackageInfo pkg2 = new PackageInfo();
- pkg2.packageName = "pkg2";
- pkg2.applicationInfo = new ApplicationInfo();
- pkg2.applicationInfo.uid = mUid;
-
- when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId))
- .thenReturn(List.of(pkg0, pkg1, pkg2));
-
- // Conversation channels
- NotificationChannel nc0 = new NotificationChannel("id0", "id0",
- NotificationManager.IMPORTANCE_HIGH);
- nc0.setConversationId("parentChannel", "conversationId");
-
- // Demoted conversation channel
- NotificationChannel nc1 = new NotificationChannel("id1", "id1",
- NotificationManager.IMPORTANCE_HIGH);
- nc1.setConversationId("parentChannel", "conversationId");
- nc1.setDemoted(true);
-
- // Non-conversation channels
- NotificationChannel nc2 = new NotificationChannel("id2", "id2",
- NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel nc3 = new NotificationChannel("id3", "id3",
- NotificationManager.IMPORTANCE_HIGH);
-
- ParceledListSlice<NotificationChannel> pls0 =
- new ParceledListSlice(ImmutableList.of(nc0));
- ParceledListSlice<NotificationChannel> pls1 =
- new ParceledListSlice(ImmutableList.of(nc1));
- ParceledListSlice<NotificationChannel> pls2 =
- new ParceledListSlice(ImmutableList.of(nc2, nc3));
-
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid))
- .thenReturn(pls0);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid))
- .thenReturn(pls1);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid))
- .thenReturn(pls2);
-
- List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true);
-
- assertThat(result).containsExactly("pkg0", "pkg1", "pkg2");
- }
-
- @Test
public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception {
// set the testable NMS to not system uid/appid
mService.isSystemUid = false;
@@ -14386,9 +14398,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
}
- private NotificationRecord createBigPictureRecord(boolean isBigPictureStyle, boolean hasImage,
- boolean isImageBitmap, boolean isExpired) {
- Notification.Builder builder = new Notification.Builder(mContext);
+ private Notification createBigPictureNotification(boolean isBigPictureStyle, boolean hasImage,
+ boolean isImageBitmap) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
Notification.BigPictureStyle style = new Notification.BigPictureStyle();
if (isBigPictureStyle && hasImage) {
@@ -14404,12 +14417,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification notification = builder.setChannelId(TEST_CHANNEL_ID).build();
+ return notification;
+ }
+
+ private NotificationRecord createBigPictureRecord(boolean isBigPictureStyle, boolean hasImage,
+ boolean isImageBitmap, boolean isExpired) {
long timePostedMs = System.currentTimeMillis();
if (isExpired) {
timePostedMs -= BITMAP_DURATION.toMillis();
}
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, "tag", mUid, 0,
- notification, UserHandle.getUserHandleForUid(mUid), null, timePostedMs);
+ createBigPictureNotification(isBigPictureStyle, hasImage, isImageBitmap),
+ UserHandle.getUserHandleForUid(mUid), null, timePostedMs);
return new NotificationRecord(mContext, sbn, mTestNotificationChannel);
}
@@ -14421,6 +14440,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testRemoveBitmaps_canRemoveRevokedDelegate() throws Exception {
+ Notification n = createBigPictureNotification(true, true, true);
+ long timePostedMs = System.currentTimeMillis();
+ timePostedMs -= BITMAP_DURATION.toMillis();
+
+ when(mPermissionHelper.hasPermission(UID_O)).thenReturn(true);
+ when(mPackageManagerInternal.isSameApp(PKG_O, UID_O, UserHandle.getUserId(UID_O)))
+ .thenReturn(true);
+ mService.mPreferencesHelper.createNotificationChannel(PKG_O, UID_O,
+ mTestNotificationChannel, true /* fromTargetApp */, false, UID_O,
+ false);
+ mBinderService.createNotificationChannels(PKG_O, new ParceledListSlice(
+ Arrays.asList(mTestNotificationChannel, mSilentChannel, mMinChannel)));
+
+ StatusBarNotification sbn = new StatusBarNotification(PKG_O, "old.delegate", 8, "tag",
+ UID_O, 0, n, UserHandle.getUserHandleForUid(UID_O), null, timePostedMs);
+
+ mService.addNotification(new NotificationRecord(mContext, sbn, mTestNotificationChannel));
+ mInternalService.removeBitmaps();
+
+ waitForIdle();
+
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.EnqueueNotificationRunnable.class));
+ }
+
+ @Test
public void testRemoveBitmaps_notBigPicture_noRepost() {
addRecordAndRemoveBitmaps(
createBigPictureRecord(
@@ -17095,6 +17141,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
mService.setHandler(handler);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
Bundle signals = new Bundle();
signals.putInt(KEY_TYPE, TYPE_NEWS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 411a6102f45a..361df94e8a90 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -44,7 +44,7 @@ import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.server.UiServiceTestCase;
@@ -89,7 +89,7 @@ import java.util.stream.Stream;
import javax.annotation.Nullable;
@RunWith(AndroidJUnit4.class)
-@EnableFlags({Flags.FLAG_VISIT_PERSON_URI, Flags.FLAG_API_RICH_ONGOING})
+@EnableFlags({Flags.FLAG_API_RICH_ONGOING})
public class NotificationVisitUrisTest extends UiServiceTestCase {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index e1b478cd1a1b..dda060d5d586 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.ZenBypassingApp;
import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -2620,6 +2621,72 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void getPackagesBypassingDnd_noChannelsBypassing() throws Exception {
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_N_MR1))).isEmpty();
+ }
+
+ @Test
+ public void getPackagesBypassingDnd_oneChannelBypassing_deleted() {
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setDeleted(true);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
+ /*has DND access*/ true, UID_N_MR1, false);
+
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_N_MR1))).isEmpty();
+ }
+
+ @Test
+ public void getPackagesBypassingDnd_oneChannelBypassing_groupBlocked() {
+ int uid = UID_N_MR1;
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ true,
+ uid, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true,
+ uid, false);
+ ncg.setBlocked(true);
+
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(uid))).isEmpty();
+ }
+
+ @Test
+ public void getPackagesBypassingDnd_multipleApps() {
+ List<ZenBypassingApp> expected = ImmutableList.of(
+ new ZenBypassingApp(PKG_O, true), new ZenBypassingApp(PKG_P, false));
+
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel4 = new NotificationChannel("id4", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(false);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(true);
+ channel4.setBypassDnd(false);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
+ /*has DND access*/ true, UID_N_MR1, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, true,
+ UID_O, false);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true,
+ UID_P, false);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel4, true, true,
+ UID_P, false);
+
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_O)))
+ .containsExactlyElementsIn(expected);
+ }
+
+ @Test
public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() {
int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 07d25dfd814e..ba91ca2323af 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -52,6 +52,14 @@ public class TestableNotificationManagerService extends NotificationManagerServi
}
public SensitiveLog lastSensitiveLog = null;
+ private static class ClassificationChannelLog {
+ public boolean hasPosted;
+ public boolean isAlerting;
+ public long classification;
+ public long lifetime;
+ }
+ public ClassificationChannelLog lastClassificationChannelLog = null;
+
TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
InstanceIdSequence notificationInstanceIdSequence) {
super(context, logger, notificationInstanceIdSequence);
@@ -211,4 +219,29 @@ public class TestableNotificationManagerService extends NotificationManagerServi
public interface ComponentPermissionChecker {
int check(String permission, int uid, int owningUid, boolean exported);
}
+
+ @Override
+ protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
+ int classification, int lifetimeMs) {
+ lastClassificationChannelLog = new ClassificationChannelLog();
+ lastClassificationChannelLog.hasPosted = hasPosted;
+ lastClassificationChannelLog.isAlerting = isAlerting;
+ lastClassificationChannelLog.classification = classification;
+ lastClassificationChannelLog.lifetime = lifetimeMs;
+ }
+
+ /**
+ * Returns true if the last recorded classification channel log has all the values specified.
+ */
+ public boolean checkLastClassificationChannelLog(boolean hasPosted, boolean isAlerting,
+ int classification, int lifetime) {
+ if (lastClassificationChannelLog == null) {
+ return false;
+ }
+
+ return hasPosted == lastClassificationChannelLog.hasPosted
+ && isAlerting == lastClassificationChannelLog.isAlerting
+ && classification == lastClassificationChannelLog.classification
+ && lifetime == lastClassificationChannelLog.lifetime;
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index c53fe9520e2a..020670dc0f0a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -494,6 +494,22 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testZenOn_RepeatCallers_CallTypesBlocked() {
+ mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[]{PKG_O});
+ // Any call allowed but no repeat callers
+ mZenModeHelper.mConsolidatedPolicy = new Policy(PRIORITY_CATEGORY_CALLS,
+ PRIORITY_SENDERS_ANY, 0, 0, 0);
+ mZenModeHelper.applyRestrictions();
+
+ verifyApplyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
+ verifyApplyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
+ }
+
+
+ @Test
public void testZenOn_StarredCallers_CallTypesBlocked() {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[]{PKG_O});
@@ -502,7 +518,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
| PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
| PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS
| PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REMINDERS
- | PRIORITY_CATEGORY_SYSTEM,
+ | PRIORITY_CATEGORY_SYSTEM | PRIORITY_CATEGORY_REPEAT_CALLERS,
PRIORITY_SENDERS_STARRED,
PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_ANYONE);
mZenModeHelper.applyRestrictions();
diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml
index c0f514fb9673..850884f84b01 100644
--- a/services/tests/vibrator/AndroidManifest.xml
+++ b/services/tests/vibrator/AndroidManifest.xml
@@ -32,6 +32,9 @@
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" />
<!-- Required to play system-only haptic feedback constants -->
<uses-permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" />
+ <!-- Required to play vendor effects and start vendor sessions -->
+ <uses-permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS" />
+ <uses-permission android:name="android.permission.START_VIBRATION_SESSIONS" />
<application android:debuggable="true">
<uses-library android:name="android.test.mock" android:required="true" />
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index dfdd0cde6aba..88ba9e3af6df 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -83,6 +84,8 @@ import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.test.FakeVibrator;
import android.os.test.TestLooper;
+import android.os.vibrator.IVibrationSession;
+import android.os.vibrator.IVibrationSessionCallback;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
@@ -195,6 +198,7 @@ public class VibratorManagerServiceTest {
new SparseArray<>();
private final List<HalVibration> mPendingVibrations = new ArrayList<>();
+ private final List<VendorVibrationSession> mPendingSessions = new ArrayList<>();
private VibratorManagerService mService;
private Context mContextSpy;
@@ -264,6 +268,11 @@ public class VibratorManagerServiceTest {
grantPermission(android.Manifest.permission.VIBRATE);
// Cancel any pending vibration from tests, including external vibrations.
cancelVibrate(mService);
+ // End pending sessions.
+ for (VendorVibrationSession session : mPendingSessions) {
+ session.cancelSession();
+ }
+ mTestLooper.dispatchAll();
// Wait until pending vibrations end asynchronously.
for (HalVibration vibration : mPendingVibrations) {
vibration.waitForEnd();
@@ -1229,6 +1238,36 @@ public class VibratorManagerServiceTest {
.anyMatch(PrebakedSegment.class::isInstance));
}
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @Test
+ public void vibrate_withOngoingHigherImportanceSession_ignoresEffect() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+ verify(callback).onStarted(any(IVibrationSession.class));
+
+ HalVibration vibration = vibrateAndWaitUntilFinished(service,
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+ assertThat(vibration.getStatus()).isEqualTo(Status.IGNORED_FOR_HIGHER_IMPORTANCE);
+ verify(callback, never()).onFinishing();
+ verify(callback, never()).onFinished(anyInt());
+ // The second vibration shouldn't have played any prebaked segment.
+ assertFalse(fakeVibrator.getAllEffectSegments().stream()
+ .anyMatch(PrebakedSegment.class::isInstance));
+ }
+
@Test
public void vibrate_withOngoingLowerImportanceVibration_cancelsOngoingEffect()
throws Exception {
@@ -1289,6 +1328,36 @@ public class VibratorManagerServiceTest {
.filter(PrebakedSegment.class::isInstance).count());
}
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @Test
+ public void vibrate_withOngoingLowerImportanceSession_cancelsOngoingSession() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+ verify(callback).onStarted(any(IVibrationSession.class));
+
+ HalVibration vibration = vibrateAndWaitUntilFinished(service,
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
+ assertThat(vibration.getStatus()).isEqualTo(Status.FINISHED);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+ // One segment played is the prebaked CLICK from the new vibration.
+ assertEquals(1, mVibratorProviders.get(1).getAllEffectSegments().stream()
+ .filter(PrebakedSegment.class::isInstance).count());
+ }
+
@Test
public void vibrate_withOngoingSameImportancePipelinedVibration_continuesOngoingEffect()
throws Exception {
@@ -1416,16 +1485,16 @@ public class VibratorManagerServiceTest {
// The native callback will be dispatched manually in this test.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
- ArgumentCaptor<VibratorManagerService.OnSyncedVibrationCompleteListener> listenerCaptor =
+ ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
ArgumentCaptor.forClass(
- VibratorManagerService.OnSyncedVibrationCompleteListener.class);
+ VibratorManagerService.VibratorManagerNativeCallbacks.class);
verify(mNativeWrapperMock).init(listenerCaptor.capture());
CountDownLatch triggerCountDown = new CountDownLatch(1);
// Mock trigger callback on registered listener right after the synced vibration starts.
when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
when(mNativeWrapperMock.triggerSynced(anyLong())).then(answer -> {
- listenerCaptor.getValue().onComplete(answer.getArgument(0));
+ listenerCaptor.getValue().onSyncedVibrationComplete(answer.getArgument(0));
triggerCountDown.countDown();
return true;
});
@@ -2318,6 +2387,34 @@ public class VibratorManagerServiceTest {
assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates());
}
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @Test
+ public void onExternalVibration_withOngoingHigherImportanceSession_ignoreNewVibration()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+ verify(callback).onStarted(any(IVibrationSession.class));
+
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ // External vibration is ignored.
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+
+ // Session still running.
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+ verify(callback, never()).onFinishing();
+ verify(callback, never()).onFinished(anyInt());
+ }
+
@Test
public void onExternalVibration_withNewSameImportanceButRepeating_cancelsOngoingVibration()
throws Exception {
@@ -2373,6 +2470,36 @@ public class VibratorManagerServiceTest {
assertEquals(Arrays.asList(false), mVibratorProviders.get(1).getExternalControlStates());
}
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @Test
+ public void onExternalVibration_withOngoingLowerImportanceSession_cancelsOngoingSession()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+ verify(callback).onStarted(any(IVibrationSession.class));
+
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+ mTestLooper.dispatchAll();
+
+ // Session is cancelled.
+ assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+ assertEquals(Arrays.asList(false, true),
+ mVibratorProviders.get(1).getExternalControlStates());
+ }
+
@Test
public void onExternalVibration_withRingtone_usesRingerModeSettings() {
mockVibrators(1);
@@ -2638,6 +2765,376 @@ public class VibratorManagerServiceTest {
}
@Test
+ @DisableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withoutFeatureFlag_throwsException() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ int vibratorId = 1;
+ mockVibrators(vibratorId);
+ VibratorManagerService service = createSystemReadyService();
+
+ IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
+ assertThrows("Expected starting session without feature flag to fail!",
+ UnsupportedOperationException.class,
+ () -> startSession(service, RINGTONE_ATTRS, callback, vibratorId));
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(callback, never()).onStarted(any(IVibrationSession.class));
+ verify(callback, never()).onFinishing();
+ verify(callback, never()).onFinished(anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withoutCapability_doesNotStart() throws Exception {
+ int vibratorId = 1;
+ mockVibrators(vibratorId);
+ VibratorManagerService service = createSystemReadyService();
+
+ IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
+ callback, vibratorId);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
+ verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(callback, never()).onFinishing();
+ verify(callback)
+ .onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withoutCallback_doesNotStart() {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ int vibratorId = 1;
+ mockVibrators(vibratorId);
+ VibratorManagerService service = createSystemReadyService();
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
+ /* callback= */ null, vibratorId);
+ mTestLooper.dispatchAll();
+
+ assertThat(session).isNull();
+ verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withoutVibratorIds_doesNotStart() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ int[] nullIds = null;
+ IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, nullIds);
+ assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
+
+ int[] emptyIds = {};
+ session = startSession(service, RINGTONE_ATTRS, callback, emptyIds);
+ assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
+
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(callback, never()).onFinishing();
+ verify(callback, times(2))
+ .onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_badVibratorId_failsToStart() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ when(mNativeWrapperMock.startSession(anyLong(), any(int[].class))).thenReturn(false);
+ doReturn(false).when(mNativeWrapperMock).startSession(anyLong(), eq(new int[] {1, 3}));
+ doReturn(true).when(mNativeWrapperMock).startSession(anyLong(), eq(new int[] {1, 2}));
+ VibratorManagerService service = createSystemReadyService();
+
+ IBinder token = mock(IBinder.class);
+ IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
+ doReturn(token).when(callback).asBinder();
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 3);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 3}));
+ verify(callback, never()).onStarted(any(IVibrationSession.class));
+ verify(callback, never()).onFinishing();
+ verify(callback)
+ .onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_thenFinish_returnsSuccessAfterCallback() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ captor.getValue().finishSession();
+
+ // Session not ended until HAL callback.
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+
+ // Dispatch HAL callbacks.
+ mTestLooper.moveTimeForward(sessionFinishDelayMs);
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_thenSendCancelSignal_cancelsSession() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ VibratorManagerService service = createSystemReadyService();
+ int sessionFinishDelayMs = 200;
+ IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ session.getCancellationSignal().cancel();
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_BY_USER);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_thenCancel_returnsCancelStatus() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ VibratorManagerService service = createSystemReadyService();
+ // Delay not applied when session is aborted.
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ captor.getValue().cancelSession();
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_BY_USER);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_finishThenCancel_returnsRightAwayWithFinishedStatus()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ VibratorManagerService service = createSystemReadyService();
+ // Delay not applied when session is aborted.
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+
+ captor.getValue().finishSession();
+ mTestLooper.dispatchAll();
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+
+ captor.getValue().cancelSession();
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_thenHalCancels_returnsCancelStatus()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1, 2);
+ VibratorManagerService service = createSystemReadyService();
+ ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
+ ArgumentCaptor.forClass(
+ VibratorManagerService.VibratorManagerNativeCallbacks.class);
+ verify(mNativeWrapperMock).init(listenerCaptor.capture());
+ doReturn(true).when(mNativeWrapperMock).startSession(anyLong(), any(int[].class));
+
+ IBinder token = mock(IBinder.class);
+ IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
+ doReturn(token).when(callback).asBinder();
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
+ verify(callback).onStarted(any(IVibrationSession.class));
+
+ // Mock HAL ending session unexpectedly.
+ listenerCaptor.getValue().onVibrationSessionComplete(session.getSessionId());
+ mTestLooper.dispatchAll();
+
+ assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_BY_UNKNOWN_REASON);
+ verify(callback).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withPowerMode_usesPowerModeState() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+ VendorVibrationSession session1 = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ VendorVibrationSession session2 = startSession(service, RINGTONE_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
+ verify(callback).onStarted(captor.capture());
+ captor.getValue().cancelSession();
+ mTestLooper.dispatchAll();
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+ VendorVibrationSession session3 = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock, never())
+ .startSession(eq(session1.getSessionId()), any(int[].class));
+ verify(mNativeWrapperMock).startSession(eq(session2.getSessionId()), eq(new int[] {1}));
+ verify(mNativeWrapperMock).startSession(eq(session3.getSessionId()), eq(new int[] {1}));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withOngoingHigherImportanceVibration_ignoresSession()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[]{10, 10_000}, new int[]{128, 255}, -1);
+ vibrate(service, effect, ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2,
+ service, TEST_TIMEOUT_MILLIS));
+
+ VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+
+ verify(mNativeWrapperMock, never())
+ .startSession(eq(session.getSessionId()), any(int[].class));
+ assertThat(session.getStatus()).isEqualTo(Status.IGNORED_FOR_HIGHER_IMPORTANCE);
+ verify(callback, never()).onFinishing();
+ verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_IGNORED));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withOngoingLowerImportanceVibration_cancelsOngoing()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[]{10, 10_000}, new int[]{128, 255}, -1);
+ HalVibration vibration = vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async.
+ // Wait until second step started to ensure the noteVibratorOn was triggered.
+ assertTrue(waitUntil(s -> fakeVibrator.getAmplitudes().size() == 2, service,
+ TEST_TIMEOUT_MILLIS));
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ vibration.waitForEnd();
+ assertTrue(waitUntil(s -> session.isStarted(), service, TEST_TIMEOUT_MILLIS));
+ mTestLooper.dispatchAll();
+
+ assertThat(vibration.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
+ assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] { 1 }));
+ verify(callback).onStarted(any(IVibrationSession.class));
+ }
+
+ @Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void startVibrationSession_withOngoingLowerImportanceExternalVibration_cancelsOngoing()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ VibratorManagerService service = createSystemReadyService();
+ IVibrationSessionCallback callback =
+ mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+
+ IBinder firstToken = mock(IBinder.class);
+ IExternalVibrationController controller = mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS,
+ controller, firstToken);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+ VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ mTestLooper.dispatchAll();
+
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+ // The external vibration should have been cancelled
+ verify(controller).mute();
+ assertEquals(Arrays.asList(false, true, false),
+ mVibratorProviders.get(1).getExternalControlStates());
+ verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] { 1 }));
+ verify(callback).onStarted(any(IVibrationSession.class));
+ }
+
+ @Test
public void frameworkStats_externalVibration_reportsAllMetrics() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -3050,6 +3547,30 @@ public class VibratorManagerServiceTest {
when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds);
}
+ private IVibrationSessionCallback mockSessionCallbacks(long delayToEndSessionMillis) {
+ Handler handler = new Handler(mTestLooper.getLooper());
+ ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
+ ArgumentCaptor.forClass(
+ VibratorManagerService.VibratorManagerNativeCallbacks.class);
+ verify(mNativeWrapperMock).init(listenerCaptor.capture());
+ doReturn(true).when(mNativeWrapperMock).startSession(anyLong(), any(int[].class));
+ doAnswer(args -> {
+ handler.postDelayed(
+ () -> listenerCaptor.getValue().onVibrationSessionComplete(args.getArgument(0)),
+ delayToEndSessionMillis);
+ return null;
+ }).when(mNativeWrapperMock).endSession(anyLong(), eq(false));
+ doAnswer(args -> {
+ listenerCaptor.getValue().onVibrationSessionComplete(args.getArgument(0));
+ return null;
+ }).when(mNativeWrapperMock).endSession(anyLong(), eq(true));
+
+ IBinder token = mock(IBinder.class);
+ IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
+ doReturn(token).when(callback).asBinder();
+ return callback;
+ }
+
private void cancelVibrate(VibratorManagerService service) {
service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
}
@@ -3157,6 +3678,16 @@ public class VibratorManagerServiceTest {
return vib;
}
+ private VendorVibrationSession startSession(VibratorManagerService service,
+ VibrationAttributes attrs, IVibrationSessionCallback callback, int... vibratorIds) {
+ VendorVibrationSession session = service.startVendorVibrationSessionInternal(UID,
+ Context.DEVICE_ID_DEFAULT, PACKAGE_NAME, vibratorIds, attrs, "reason", callback);
+ if (session != null) {
+ mPendingSessions.add(session);
+ }
+ return session;
+ }
+
private boolean waitUntil(Predicate<VibratorManagerService> predicate,
VibratorManagerService service, long timeout) throws InterruptedException {
long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index ad11c2681779..1e9038ee1769 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -123,51 +123,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
{"MEDIA_PLAY_PAUSE key -> Media Control",
new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE},
KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
- KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0},
- {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
- KeyEvent.KEYCODE_B, META_ON},
- {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
- KeyEvent.KEYCODE_EXPLORER, 0},
- {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
- KeyEvent.KEYCODE_C, META_ON},
- {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
- KeyEvent.KEYCODE_CONTACTS, 0},
- {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
- KeyEvent.KEYCODE_E, META_ON},
- {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
- KeyEvent.KEYCODE_ENVELOPE, 0},
- {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
- KeyEvent.KEYCODE_K, META_ON},
- {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
- KeyEvent.KEYCODE_CALENDAR, 0},
- {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
- KeyEvent.KEYCODE_P, META_ON},
- {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
- KeyEvent.KEYCODE_MUSIC, 0},
- {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
- KeyEvent.KEYCODE_U, META_ON},
- {"CALCULATOR key -> Launch Default Calculator",
- new int[]{KeyEvent.KEYCODE_CALCULATOR},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
- KeyEvent.KEYCODE_CALCULATOR, 0},
- {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
- KeyEvent.KEYCODE_M, META_ON},
- {"Meta + S -> Launch Default Messaging App",
- new int[]{META_KEY, KeyEvent.KEYCODE_S},
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
- KeyEvent.KEYCODE_S, META_ON}};
+ KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0}};
}
@Keep
@@ -295,7 +251,51 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
KeyEvent.KEYCODE_DPAD_DOWN,
- META_ON | CTRL_ON}};
+ META_ON | CTRL_ON},
+ {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+ KeyEvent.KEYCODE_B, META_ON},
+ {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+ KeyEvent.KEYCODE_EXPLORER, 0},
+ {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+ KeyEvent.KEYCODE_C, META_ON},
+ {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+ KeyEvent.KEYCODE_CONTACTS, 0},
+ {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+ KeyEvent.KEYCODE_E, META_ON},
+ {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+ KeyEvent.KEYCODE_ENVELOPE, 0},
+ {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+ KeyEvent.KEYCODE_K, META_ON},
+ {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+ KeyEvent.KEYCODE_CALENDAR, 0},
+ {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+ KeyEvent.KEYCODE_P, META_ON},
+ {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+ KeyEvent.KEYCODE_MUSIC, 0},
+ {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+ KeyEvent.KEYCODE_U, META_ON},
+ {"CALCULATOR key -> Launch Default Calculator",
+ new int[]{KeyEvent.KEYCODE_CALCULATOR},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+ KeyEvent.KEYCODE_CALCULATOR, 0},
+ {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
+ KeyEvent.KEYCODE_M, META_ON},
+ {"Meta + S -> Launch Default Messaging App",
+ new int[]{META_KEY, KeyEvent.KEYCODE_S},
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
+ KeyEvent.KEYCODE_S, META_ON}};
}
@Keep
@@ -331,6 +331,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideTogglePanel();
mPhoneWindowManager.overrideInjectKeyEvent();
+ mPhoneWindowManager.overrideRoleManager();
}
@Test
@@ -396,7 +397,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
- @EnableFlags(Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL)
+ @EnableFlags(Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
@DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
public void testToggleTalkbackPress() {
testShortcutInternal("Meta + Alt + T -> Toggle talkback",
@@ -744,7 +745,7 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
}
@Test
- @EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL)
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
public void testKeyGestureToggleTalkback() {
Assert.assertTrue(
sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 28ae271e20fc..cf5323e1f3a5 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -288,7 +288,6 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
* Sends a KEYCODE_SCREENSHOT and validates screenshot is taken if flag is enabled
*/
@Test
- @EnableFlags(com.android.hardware.input.Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE)
@DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
public void testTakeScreenshot_flagEnabled() {
sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0);
@@ -296,17 +295,6 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
}
/**
- * Sends a KEYCODE_SCREENSHOT and validates screenshot is not taken if flag is disabled
- */
- @Test
- @DisableFlags({com.android.hardware.input.Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE,
- com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER})
- public void testTakeScreenshot_flagDisabled() {
- sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0);
- mPhoneWindowManager.assertTakeScreenshotNotCalled();
- }
-
- /**
* META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 6596ee935b4b..a51ce9951ab4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -699,8 +699,8 @@ class TestPhoneWindowManager {
void assertPowerWakeUp() {
mTestLooper.dispatchAll();
- verify(mWindowWakeUpPolicy)
- .wakeUpFromKey(anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean());
+ verify(mWindowWakeUpPolicy).wakeUpFromKey(
+ eq(DEFAULT_DISPLAY), anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean());
}
void assertNoPowerSleep() {
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
index 7322e5a3b681..3ca352cfa60d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -22,6 +22,7 @@ import static android.os.PowerManager.WAKE_REASON_GESTURE;
import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.KeyEvent.KEYCODE_HOME;
@@ -35,6 +36,7 @@ import static com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraL
import static com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch;
import static com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture;
import static com.android.server.policy.Flags.FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE;
+import static com.android.server.power.feature.flags.Flags.FLAG_PER_DISPLAY_WAKE_BY_TOUCH;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -43,6 +45,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -52,6 +55,8 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.os.PowerManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.Display;
@@ -125,6 +130,7 @@ public final class WindowWakeUpPolicyTests {
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testMotionWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
setTheaterModeEnabled(false);
mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
@@ -136,7 +142,8 @@ public final class WindowWakeUpPolicyTests {
// Verify the policy wake up call succeeds because of the call on the delegate, and not
// because of a PowerManager wake up.
- assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isTrue();
+ assertThat(mPolicy.wakeUpFromMotion(
+ mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isTrue();
verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true);
verifyNoPowerManagerWakeUp();
@@ -144,12 +151,14 @@ public final class WindowWakeUpPolicyTests {
// Verify the policy wake up call succeeds because of the PowerManager wake up, since the
// delegate would not handle the wake up request.
- assertThat(mPolicy.wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false)).isTrue();
+ assertThat(mPolicy.wakeUpFromMotion(
+ mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false)).isTrue();
verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false);
verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testKeyWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
setTheaterModeEnabled(false);
mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
@@ -161,7 +170,7 @@ public final class WindowWakeUpPolicyTests {
// Verify the policy wake up call succeeds because of the call on the delegate, and not
// because of a PowerManager wake up.
- assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isTrue();
+ assertThat(mPolicy.wakeUpFromKey(DEFAULT_DISPLAY, 200, KEYCODE_POWER, true)).isTrue();
verify(mInputWakeUpDelegate).wakeUpFromKey(200, KEYCODE_POWER, true);
verifyNoPowerManagerWakeUp();
@@ -169,7 +178,8 @@ public final class WindowWakeUpPolicyTests {
// Verify the policy wake up call succeeds because of the PowerManager wake up, since the
// delegate would not handle the wake up request.
- assertThat(mPolicy.wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false)).isTrue();
+ assertThat(mPolicy.wakeUpFromKey(
+ DEFAULT_DISPLAY, 300, KEYCODE_STEM_PRIMARY, false)).isTrue();
verify(mInputWakeUpDelegate).wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false);
verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_KEY, "android.policy:KEY");
}
@@ -186,7 +196,7 @@ public final class WindowWakeUpPolicyTests {
.setInputWakeUpDelegate(mInputWakeUpDelegate);
// Check that the wake up does not happen because the theater mode policy check fails.
- assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isFalse();
+ assertThat(mPolicy.wakeUpFromKey(DEFAULT_DISPLAY, 200, KEYCODE_POWER, true)).isFalse();
verify(mInputWakeUpDelegate, never()).wakeUpFromKey(anyLong(), anyInt(), anyBoolean());
}
@@ -201,11 +211,13 @@ public final class WindowWakeUpPolicyTests {
.setInputWakeUpDelegate(mInputWakeUpDelegate);
// Check that the wake up does not happen because the theater mode policy check fails.
- assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isFalse();
+ assertThat(mPolicy.wakeUpFromMotion(
+ mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isFalse();
verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean());
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testTheaterModeChecksNotAppliedWhenScreenIsOn() {
mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
setDefaultDisplayState(Display.STATE_ON);
@@ -213,30 +225,69 @@ public final class WindowWakeUpPolicyTests {
setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
- mPolicy.wakeUpFromMotion(200L, SOURCE_TOUCHSCREEN, true);
+ mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true);
verify(mPowerManager).wakeUp(200L, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testWakeUpFromMotion() {
runPowerManagerUpChecks(
- () -> mPolicy.wakeUpFromMotion(mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
+ () -> mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(),
+ mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
config_allowTheaterModeWakeFromMotion,
WAKE_REASON_WAKE_MOTION,
"android.policy:MOTION");
}
@Test
+ @EnableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
+ public void testWakeUpFromMotion_perDisplayWakeByTouchEnabled() {
+ setTheaterModeEnabled(false);
+ final int displayId = 555;
+ mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+ boolean displayWokeUp = mPolicy.wakeUpFromMotion(
+ displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+
+ // Verify that display is woken up
+ assertThat(displayWokeUp).isTrue();
+ verify(mPowerManager).wakeUp(anyLong(), eq(WAKE_REASON_WAKE_MOTION),
+ eq("android.policy:MOTION"), eq(displayId));
+ }
+
+ @Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
+ public void testWakeUpFromMotion_perDisplayWakeByTouchDisabled() {
+ setTheaterModeEnabled(false);
+ final int displayId = 555;
+ mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+ boolean displayWokeUp = mPolicy.wakeUpFromMotion(
+ displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+
+ // Verify that power is woken up and display isn't woken up individually
+ assertThat(displayWokeUp).isTrue();
+ verify(mPowerManager).wakeUp(
+ anyLong(), eq(WAKE_REASON_WAKE_MOTION), eq("android.policy:MOTION"));
+ verify(mPowerManager, never()).wakeUp(anyLong(), eq(WAKE_REASON_WAKE_MOTION),
+ eq("android.policy:MOTION"), eq(displayId));
+ }
+
+ @Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testWakeUpFromKey_nonPowerKey() {
runPowerManagerUpChecks(
- () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_HOME, true),
+ () -> mPolicy.wakeUpFromKey(
+ DEFAULT_DISPLAY, mClock.uptimeMillis(), KEYCODE_HOME, true),
config_allowTheaterModeWakeFromKey,
WAKE_REASON_WAKE_KEY,
"android.policy:KEY");
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testWakeUpFromKey_powerKey() {
// Disable the resource affecting all wake keys because it affects power key as well.
// That way, power key wake during theater mode will solely be controlled by
@@ -245,7 +296,8 @@ public final class WindowWakeUpPolicyTests {
// Test with power key
runPowerManagerUpChecks(
- () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, true),
+ () -> mPolicy.wakeUpFromKey(
+ DEFAULT_DISPLAY, mClock.uptimeMillis(), KEYCODE_POWER, true),
config_allowTheaterModeWakeFromPowerKey,
WAKE_REASON_POWER_BUTTON,
"android.policy:POWER");
@@ -254,13 +306,31 @@ public final class WindowWakeUpPolicyTests {
// even if the power-key specific theater mode config is disabled.
setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false);
runPowerManagerUpChecks(
- () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, false),
+ () -> mPolicy.wakeUpFromKey(
+ DEFAULT_DISPLAY, mClock.uptimeMillis(), KEYCODE_POWER, false),
config_allowTheaterModeWakeFromKey,
WAKE_REASON_POWER_BUTTON,
"android.policy:POWER");
}
@Test
+ @EnableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
+ public void testWakeUpFromKey_invalidDisplay_perDisplayWakeByTouchEnabled() {
+ setTheaterModeEnabled(false);
+ final int displayId = Display.INVALID_DISPLAY;
+ mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
+
+ boolean displayWokeUp = mPolicy.wakeUpFromKey(
+ displayId, mClock.uptimeMillis(), KEYCODE_POWER, /* isDown= */ false);
+
+ // Verify that default display is woken up
+ assertThat(displayWokeUp).isTrue();
+ verify(mPowerManager).wakeUp(anyLong(), eq(WAKE_REASON_POWER_BUTTON),
+ eq("android.policy:POWER"), eq(DEFAULT_DISPLAY));
+ }
+
+ @Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testWakeUpFromLid() {
runPowerManagerUpChecks(
() -> mPolicy.wakeUpFromLid(),
@@ -270,6 +340,7 @@ public final class WindowWakeUpPolicyTests {
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testWakeUpFromWakeGesture() {
runPowerManagerUpChecks(
() -> mPolicy.wakeUpFromWakeGesture(),
@@ -279,6 +350,7 @@ public final class WindowWakeUpPolicyTests {
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testwakeUpFromCameraCover() {
runPowerManagerUpChecks(
() -> mPolicy.wakeUpFromCameraCover(mClock.uptimeMillis()),
@@ -288,6 +360,7 @@ public final class WindowWakeUpPolicyTests {
}
@Test
+ @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH})
public void testWakeUpFromPowerKeyCameraGesture() {
// Disable the resource affecting all wake keys because it affects power key as well.
// That way, power key wake during theater mode will solely be controlled by
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index d4ba3b25178d..9e7575f1c644 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -538,7 +538,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
public void testConsecutiveLaunchNewTask() {
final IBinder launchCookie = mock(IBinder.class);
final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
- mTrampolineActivity.noDisplay = true;
+ mTrampolineActivity.setIsNoDisplay(true);
mTrampolineActivity.mLaunchCookie = launchCookie;
mTrampolineActivity.mLaunchRootTask = launchRootTask;
onActivityLaunched(mTrampolineActivity);
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 c3466b9cee18..fee646d9cb9c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3139,11 +3139,13 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testOnStartingWindowDrawn() {
+ // Skip unnecessary resume top.
+ mSupervisor.beginDeferResume();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
// The task-has-been-visible should not affect the decision of making transition ready.
activity.getTask().setHasBeenVisible(true);
activity.detachFromProcess();
- activity.mStartingData = mock(StartingData.class);
+ activity.mStartingData = new SplashScreenStartingData(mWm, 0, 0);
registerTestTransitionPlayer();
final Transition transition = activity.mTransitionController.requestTransitionIfNeeded(
WindowManager.TRANSIT_OPEN, 0 /* flags */, null /* trigger */, mDisplayContent);
@@ -3151,7 +3153,11 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(activity.mStartingData.mIsDisplayed);
// The transition can be ready by the starting window of a visible-requested activity
// without a running process.
- assertTrue(transition.allReady());
+ if (!transition.allReady()) {
+ // Print unsatisfied conditions.
+ transition.onReadyTimeout();
+ Assert.fail(transition + " must be ready by onStartingWindowDrawn");
+ }
// If other event makes the transition unready, the reentrant of onStartingWindowDrawn
// should not replace the readiness again.
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 20dcdde63cc4..d6be9159694b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -24,7 +24,9 @@ import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.window.BackNavigationInfo.typeToString;
+import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -103,6 +105,13 @@ public class BackNavigationControllerTests extends WindowTestsBase {
@Before
public void setUp() throws Exception {
+ final TransitionController transitionController = mAtm.getTransitionController();
+ final Transition fakeTransition = new Transition(TRANSIT_PREPARE_BACK_NAVIGATION,
+ 0 /* flag */, transitionController, transitionController.mSyncEngine);
+ spyOn(transitionController);
+ doReturn(fakeTransition).when(transitionController)
+ .createTransition(anyInt(), anyInt());
+
final BackNavigationController original = new BackNavigationController();
original.setWindowManager(mWm);
mBackNavigationController = Mockito.spy(original);
@@ -111,6 +120,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
mBackAnimationAdapter = mock(BackAnimationAdapter.class);
doReturn(true).when(mBackAnimationAdapter).isAnimatable(anyInt());
+ Mockito.doNothing().when(mBackNavigationController).startAnimation();
mNavigationMonitor = mock(BackNavigationController.NavigationMonitor.class);
mRootHomeTask = initHomeActivity();
}
@@ -446,7 +456,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
new OnBackInvokedCallbackInfo(
callback,
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- /* isAnimationCallback = */ false));
+ /* isAnimationCallback = */ false, OVERRIDE_UNDEFINED));
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
@@ -467,7 +477,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
new OnBackInvokedCallbackInfo(
callback,
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- /* isAnimationCallback = */ true));
+ /* isAnimationCallback = */ true, OVERRIDE_UNDEFINED));
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
@@ -608,7 +618,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
new OnBackInvokedCallbackInfo(
callback,
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- /* isAnimationCallback = */ false));
+ /* isAnimationCallback = */ false, OVERRIDE_UNDEFINED));
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertThat(backNavigationInfo).isNull();
@@ -722,7 +732,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
new OnBackInvokedCallbackInfo(
callback,
OnBackInvokedDispatcher.PRIORITY_SYSTEM,
- /* isAnimationCallback = */ false));
+ /* isAnimationCallback = */ false, OVERRIDE_UNDEFINED));
return callback;
}
@@ -732,7 +742,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
new OnBackInvokedCallbackInfo(
callback,
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- /* isAnimationCallback = */ false));
+ /* isAnimationCallback = */ false, OVERRIDE_UNDEFINED));
return callback;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 965b65c8f1d1..ade591d006f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -23,11 +23,13 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_I
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_NON_APP_VISIBLE_WINDOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
@@ -50,6 +52,7 @@ import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.DeviceConfig;
import android.util.Pair;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -126,6 +129,8 @@ public class BackgroundActivityStartControllerExemptionTests {
AppOpsManager mAppOpsManager;
MirrorActiveUids mActiveUids = new MirrorActiveUids();
WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap();
+ @Mock
+ VisibleActivityProcessTracker mVisibleActivityProcessTracker;
@Mock
ActivityTaskSupervisor mSupervisor;
@@ -182,6 +187,8 @@ public class BackgroundActivityStartControllerExemptionTests {
mService.mRootWindowContainer = mRootWindowContainer;
when(mService.getAppOpsManager()).thenReturn(mAppOpsManager);
setViaReflection(mService, "mProcessMap", mProcessMap);
+ setViaReflection(mService, "mVisibleActivityProcessTracker",
+ mVisibleActivityProcessTracker);
setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks);
@@ -257,7 +264,7 @@ public class BackgroundActivityStartControllerExemptionTests {
int realCallingPid = REGULAR_PID_2;
// setup state
- when(mService.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true);
+ when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(callingUid))).thenReturn(true);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
// prepare call
@@ -269,6 +276,8 @@ public class BackgroundActivityStartControllerExemptionTests {
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
+ assertThat(balState.toString()).contains("callingUidHasVisibleActivity: true");
+ assertThat(balState.toString()).contains("callingUidHasNonAppVisibleWindow: false");
// call
BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
@@ -289,7 +298,8 @@ public class BackgroundActivityStartControllerExemptionTests {
int realCallingPid = REGULAR_PID_2;
// setup state
- when(mService.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true);
+ when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(realCallingUid))).thenReturn(
+ true);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
// prepare call
@@ -301,6 +311,8 @@ public class BackgroundActivityStartControllerExemptionTests {
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
+ assertThat(balState.toString()).contains("realCallingUidHasVisibleActivity: true");
+ assertThat(balState.toString()).contains("realCallingUidHasNonAppVisibleWindow: false");
// call
BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
@@ -313,6 +325,74 @@ public class BackgroundActivityStartControllerExemptionTests {
}
@Test
+ public void testCaller_appHasNonAppVisibleWindow() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ mActiveUids.onNonAppSurfaceVisibilityChanged(callingUid, true);
+ when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ boolean allowBalExemptionForSystemProcess = false;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions;
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
+ checkedOptions);
+ assertThat(balState.toString()).contains("callingUidHasVisibleActivity: false");
+ assertThat(balState.toString()).contains("callingUidHasNonAppVisibleWindow: true");
+
+ // call
+ BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+ balState);
+ balState.setResultForCaller(callerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_NON_APP_VISIBLE_WINDOW);
+ }
+
+ @Test
+ public void testRealCaller_appHasNonAppVisibleWindow() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ mActiveUids.onNonAppSurfaceVisibilityChanged(realCallingUid, true);
+ when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ boolean allowBalExemptionForSystemProcess = false;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions;
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
+ checkedOptions);
+ assertThat(balState.toString()).contains("realCallingUidHasVisibleActivity: false");
+ assertThat(balState.toString()).contains("realCallingUidHasNonAppVisibleWindow: true");
+
+ // call
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+ balState);
+ balState.setResultForRealCaller(realCallerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_NON_APP_VISIBLE_WINDOW);
+ }
+
+ @Test
@RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
public void testCaller_appHasVisibleWindowWithIfVisibleOptIn() {
int callingUid = REGULAR_UID_1;
@@ -322,7 +402,7 @@ public class BackgroundActivityStartControllerExemptionTests {
int realCallingPid = REGULAR_PID_2;
// setup state
- when(mService.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true);
+ when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(callingUid))).thenReturn(true);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
// prepare call
@@ -336,6 +416,8 @@ public class BackgroundActivityStartControllerExemptionTests {
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
+ assertThat(balState.toString()).contains("callingUidHasVisibleActivity: true");
+ assertThat(balState.toString()).contains("callingUidHasNonAppVisibleWindow: false");
// call
BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
@@ -357,7 +439,8 @@ public class BackgroundActivityStartControllerExemptionTests {
int realCallingPid = REGULAR_PID_2;
// setup state
- when(mService.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true);
+ when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(realCallingUid))).thenReturn(
+ true);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
// prepare call
@@ -371,6 +454,8 @@ public class BackgroundActivityStartControllerExemptionTests {
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
+ assertThat(balState.toString()).contains("realCallingUidHasVisibleActivity: true");
+ assertThat(balState.toString()).contains("realCallingUidHasNonAppVisibleWindow: false");
// call
BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
index 7f7462debe8f..99e730ae76cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
@@ -67,12 +67,16 @@ public class BackgroundActivityStartControllerLogTests {
@Mock
PendingIntentRecord mPendingIntentRecord;
MirrorActiveUids mActiveUids = new MirrorActiveUids();
+ @Mock
+ VisibleActivityProcessTracker mVisibleActivityProcessTracker;
BackgroundActivityStartController mController;
BackgroundActivityStartController.BalState mState;
@Before
public void setup() {
setViaReflection(mService, "mActiveUids", mActiveUids);
+ setViaReflection(mService, "mVisibleActivityProcessTracker",
+ mVisibleActivityProcessTracker);
mController = new BackgroundActivityStartController(mService,
mSupervisor);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index db3ce0bc98eb..854bda03f18d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -111,6 +111,9 @@ public class BackgroundActivityStartControllerTests {
@Mock
AppOpsManager mAppOpsManager;
MirrorActiveUids mActiveUids = new MirrorActiveUids();
+ @Mock
+ VisibleActivityProcessTracker mVisibleActivityProcessTracker;
+
WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap();
@Mock
@@ -200,6 +203,8 @@ public class BackgroundActivityStartControllerTests {
mService.mRootWindowContainer = mRootWindowContainer;
Mockito.when(mService.getAppOpsManager()).thenReturn(mAppOpsManager);
setViaReflection(mService, "mProcessMap", mProcessMap);
+ setViaReflection(mService, "mVisibleActivityProcessTracker",
+ mVisibleActivityProcessTracker);
//Mockito.when(mSupervisor.getBackgroundActivityLaunchController()).thenReturn(mController);
setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks);
@@ -551,13 +556,14 @@ public class BackgroundActivityStartControllerTests {
assertThat(balState.callerExplicitOptInOrOut()).isFalse();
assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isTrue();
assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
- assertThat(balState.toString()).startsWith(
+ assertThat(balState.toString()).isEqualTo(
"[callingPackage: package.app1; "
+ "callingPackageTargetSdk: -1; "
+ "callingUid: 10001; "
+ "callingPid: 11001; "
+ "appSwitchState: 0; "
- + "callingUidHasAnyVisibleWindow: false; "
+ + "callingUidHasVisibleActivity: false; "
+ + "callingUidHasNonAppVisibleWindow: false; "
+ "callingUidProcState: NONEXISTENT; "
+ "isCallingUidPersistentSystemProcess: false; "
+ "allowBalExemptionForSystemProcess: false; "
@@ -576,13 +582,17 @@ public class BackgroundActivityStartControllerTests {
+ "realCallingPackageTargetSdk: -1; "
+ "realCallingUid: 1; "
+ "realCallingPid: 1; "
- + "realCallingUidHasAnyVisibleWindow: false; "
+ + "realCallingUidHasVisibleActivity: false; "
+ + "realCallingUidHasNonAppVisibleWindow: false; "
+ "realCallingUidProcState: NONEXISTENT; "
+ "isRealCallingUidPersistentSystemProcess: false; "
+ "originatingPendingIntent: null; "
+ "realCallerApp: null; "
+ "balAllowedByPiSender: BSP.ALLOW_BAL; "
- + "resultIfPiSenderAllowsBal: null");
+ + "resultIfPiSenderAllowsBal: null; "
+ + "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ + "balRequireOptInByPendingIntentCreator: true; "
+ + "balDontBringExistingBackgroundTaskStackToFg: true]");
}
@Test
@@ -651,13 +661,14 @@ public class BackgroundActivityStartControllerTests {
assertThat(balState.callerExplicitOptInOrOut()).isFalse();
assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isFalse();
assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
- assertThat(balState.toString()).startsWith(
+ assertThat(balState.toString()).isEqualTo(
"[callingPackage: package.app1; "
+ "callingPackageTargetSdk: -1; "
+ "callingUid: 10001; "
+ "callingPid: 11001; "
+ "appSwitchState: 0; "
- + "callingUidHasAnyVisibleWindow: false; "
+ + "callingUidHasVisibleActivity: false; "
+ + "callingUidHasNonAppVisibleWindow: false; "
+ "callingUidProcState: NONEXISTENT; "
+ "isCallingUidPersistentSystemProcess: false; "
+ "allowBalExemptionForSystemProcess: false; "
@@ -676,12 +687,16 @@ public class BackgroundActivityStartControllerTests {
+ "realCallingPackageTargetSdk: -1; "
+ "realCallingUid: 1; "
+ "realCallingPid: 1; "
- + "realCallingUidHasAnyVisibleWindow: false; "
+ + "realCallingUidHasVisibleActivity: false; "
+ + "realCallingUidHasNonAppVisibleWindow: false; "
+ "realCallingUidProcState: NONEXISTENT; "
+ "isRealCallingUidPersistentSystemProcess: false; "
+ "originatingPendingIntent: PendingIntentRecord; "
+ "realCallerApp: null; "
+ "balAllowedByPiSender: BSP.ALLOW_FGS; "
- + "resultIfPiSenderAllowsBal: null");
+ + "resultIfPiSenderAllowsBal: null; "
+ + "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ + "balRequireOptInByPendingIntentCreator: true; "
+ + "balDontBringExistingBackgroundTaskStackToFg: true]");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index eacb8e9d628d..a0c5b54603f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -25,7 +25,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -190,14 +189,9 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
eq(appWindow.getSurfaceControl()), anyFloat(),
eq(Surface.FRAME_RATE_COMPATIBILITY_EXACT), eq(Surface.CHANGE_FRAME_RATE_ALWAYS));
- if (explicitRefreshRateHints()) {
- verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
- appWindow.getSurfaceControl(),
- SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
- } else {
- verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
- any(SurfaceControl.class), anyInt());
- }
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
@Test
@@ -226,14 +220,9 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
eq(appWindow.getSurfaceControl()), anyFloat(),
eq(Surface.FRAME_RATE_COMPATIBILITY_EXACT), eq(Surface.CHANGE_FRAME_RATE_ALWAYS));
- if (explicitRefreshRateHints()) {
- verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
- appWindow.getSurfaceControl(),
- SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
- } else {
- verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
- any(SurfaceControl.class), anyInt());
- }
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
@Test
@@ -288,14 +277,9 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
appWindow.getSurfaceControl(), 60,
Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS);
- if (explicitRefreshRateHints()) {
- verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
- appWindow.getSurfaceControl(),
- SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
- } else {
- verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
- any(SurfaceControl.class), anyInt());
- }
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
@Test
@@ -352,13 +336,8 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
appWindow.getSurfaceControl(), 60,
Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, Surface.CHANGE_FRAME_RATE_ALWAYS);
- if (explicitRefreshRateHints()) {
- verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
- appWindow.getSurfaceControl(),
- SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
- } else {
- verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
- any(SurfaceControl.class), anyInt());
- }
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 3fa38bfe7185..3d08ca2905f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -21,14 +21,11 @@ import static android.view.SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.hardware.display.DisplayManager;
@@ -36,7 +33,6 @@ import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.view.Display.Mode;
import android.view.Surface;
-import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import androidx.test.filters.SmallTest;
@@ -274,97 +270,6 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
}
@Test
- public void testAnimatingAppOverridePreferredModeId() {
- final WindowState overrideWindow = createWindow("overrideWindow");
- overrideWindow.mAttrs.packageName = "com.android.test";
- overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
- parcelLayoutParams(overrideWindow);
- assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
- assertTrue(mPolicy.updateFrameRateVote(overrideWindow));
- assertEquals(FRAME_RATE_VOTE_LOW_EXACT, overrideWindow.mFrameRateVote);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
-
- if (explicitRefreshRateHints()) {
- return;
- }
- overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
- overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
- assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
- assertTrue(mPolicy.updateFrameRateVote(overrideWindow));
- assertEquals(FRAME_RATE_VOTE_NONE, overrideWindow.mFrameRateVote);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
-
- // Use default mode if it is animating by shell transition.
- overrideWindow.mActivityRecord.mSurfaceAnimator.cancelAnimation();
- registerTestTransitionPlayer();
- final Transition transition = overrideWindow.mTransitionController.createTransition(
- WindowManager.TRANSIT_OPEN);
- transition.collect(overrideWindow.mActivityRecord);
- assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
-
- // If there will be display size change when switching from preferred mode to default mode,
- // then keep the current preferred mode during animating.
- mDisplayInfo = spy(mDisplayInfo);
- final Mode defaultMode = new Mode(4321 /* width */, 1234 /* height */, LOW_REFRESH_RATE);
- doReturn(defaultMode).when(mDisplayInfo).getDefaultMode();
- mPolicy = new RefreshRatePolicy(mWm, mDisplayInfo, mDenylist);
- assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
- }
-
- @Test
- public void testAnimatingAppOverridePreferredRefreshRate() {
- final WindowState overrideWindow = createWindow("overrideWindow");
- overrideWindow.mAttrs.packageName = "com.android.test";
- overrideWindow.mAttrs.preferredRefreshRate = LOW_REFRESH_RATE;
- parcelLayoutParams(overrideWindow);
- assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
- assertTrue(mPolicy.updateFrameRateVote(overrideWindow));
- assertEquals(FRAME_RATE_VOTE_LOW_PREFERRED, overrideWindow.mFrameRateVote);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
-
- if (explicitRefreshRateHints()) {
- return;
- }
- overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
- overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
- assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
- assertTrue(mPolicy.updateFrameRateVote(overrideWindow));
- assertEquals(FRAME_RATE_VOTE_NONE, overrideWindow.mFrameRateVote);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- }
-
- @Test
- public void testAnimatingDenylist() {
- final WindowState window = createWindow("overrideWindow");
- window.mAttrs.packageName = "com.android.test";
- parcelLayoutParams(window);
- when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
- assertEquals(0, mPolicy.getPreferredModeId(window));
- assertTrue(mPolicy.updateFrameRateVote(window));
- assertEquals(FRAME_RATE_VOTE_DENY_LIST, window.mFrameRateVote);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
-
- if (explicitRefreshRateHints()) {
- return;
- }
- window.mActivityRecord.mSurfaceAnimator.startAnimation(
- window.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
- assertEquals(0, mPolicy.getPreferredModeId(window));
- assertTrue(mPolicy.updateFrameRateVote(window));
- assertEquals(FRAME_RATE_VOTE_NONE, window.mFrameRateVote);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
- }
-
- @Test
public void testAnimatingCamera() {
final WindowState cameraUsingWindow = createWindow("cameraUsingWindow");
cameraUsingWindow.mAttrs.packageName = "com.android.test";
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 1c878021c9e9..41f1e2359c88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -87,6 +87,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.times;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -4782,6 +4783,114 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testCameraCompatAspectRatioAppliedForFixedOrientationCameraActivities() {
+ // Needed to create camera compat policy in DisplayContent.
+ allowDesktopMode();
+ // Create display that has all stable insets and does not rotate.
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+ .setSystemDecorations(true).setCanRotate(false).build();
+
+ final float cameraCompatAspectRatio = 4.0f;
+ setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+ // Create task on test display.
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+ // Create fixed portrait activity.
+ final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
+ .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+ final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ assertEquals(cameraCompatAspectRatio, computeAspectRatio(fixedOrientationAppBounds),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testCameraCompatAspectRatioForFixedOrientationCameraActivitiesPortraitWindow() {
+ // Needed to create camera compat policy in DisplayContent.
+ allowDesktopMode();
+ // Create portrait display that has all stable insets and does not rotate.
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 1600)
+ .setSystemDecorations(true).setCanRotate(false).build();
+
+ final float cameraCompatAspectRatio = 4.0f;
+ setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+ // Create task on test display.
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+ // Create fixed portrait activity.
+ final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
+ .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+ final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ assertEquals(cameraCompatAspectRatio, computeAspectRatio(fixedOrientationAppBounds),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testCameraCompatAspectRatioAppliedInsteadOfDefaultAspectRatio() {
+ // Needed to create camera compat policy in DisplayContent.
+ allowDesktopMode();
+ // Create display that has all stable insets and does not rotate.
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+ .setSystemDecorations(true).setCanRotate(false).build();
+
+ final float cameraCompatAspectRatio = 5.0f;
+ setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+ // Create task on test display.
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+ // App's target min aspect ratio - this should not be used, as camera controls aspect ratio.
+ final float targetMinAspectRatio = 4.0f;
+
+ // Create fixed portrait activity with min aspect ratio greater than parent aspect ratio.
+ final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm)
+ .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(targetMinAspectRatio).build();
+ final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ assertEquals(cameraCompatAspectRatio, computeAspectRatio(minAspectRatioAppBounds),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testCameraCompatAspectRatio_defualtAspectRatioAppliedWhenGreater() {
+ // Needed to create camera compat policy in DisplayContent.
+ allowDesktopMode();
+ // Create display that has all stable insets and does not rotate.
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+ .setSystemDecorations(true).setCanRotate(false).build();
+
+ final float cameraCompatAspectRatio = 5.0f;
+ setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
+
+ // Create task on test display.
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+ // App's target min aspect ratio bigger than camera compat aspect ratio - use that instead.
+ final float targetMinAspectRatio = 6.0f;
+
+ // Create fixed portrait activity with min aspect ratio greater than parent aspect ratio.
+ final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm)
+ .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(targetMinAspectRatio).build();
+ final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ assertEquals(targetMinAspectRatio, computeAspectRatio(minAspectRatioAppBounds),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ }
+
+ @Test
public void testUniversalResizeable() {
mWm.mConstants.mIgnoreActivityOrientationRequest = true;
setUpApp(mDisplayContent);
@@ -4812,6 +4921,23 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.isResizeable());
assertEquals(maxAspect, aspectRatioPolicy.getMaxAspectRatio(), 0 /* delta */);
assertNotEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOverrideOrientation());
+
+ // Activity can opt-out the resizability by component level property.
+ final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+ final PackageManager pm = mContext.getPackageManager();
+ spyOn(pm);
+ final PackageManager.Property property = new PackageManager.Property("propertyName",
+ true /* value */, name.getPackageName(), name.getClassName());
+ try {
+ doReturn(property).when(pm).getPropertyAsUser(
+ WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
+ name.getPackageName(), name.getClassName(), 0 /* userId */);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ final ActivityRecord optOutActivity = new ActivityBuilder(mAtm)
+ .setComponent(name).setTask(mTask).build();
+ assertFalse(optOutActivity.isUniversalResizeable());
}
@@ -4851,6 +4977,25 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
}
+ /**
+ * {@code canEnterDesktopMode} is called when {@link CameraCompatFreeformPolicy} is created in
+ * {@link AppCompatCameraPolicy}.
+ *
+ * <p>{@link #allowDesktopMode()} needs to be called before {@link DisplayContent} is created.
+ */
+ private void allowDesktopMode() {
+ doReturn(true).when(() -> DesktopModeHelper.canEnterDesktopMode(any()));
+ }
+
+ private void setupCameraCompatAspectRatio(float cameraCompatAspectRatio,
+ @NonNull DisplayContent display) {
+ CameraCompatFreeformPolicy cameraPolicy = display.mAppCompatCameraPolicy
+ .mCameraCompatFreeformPolicy;
+ spyOn(cameraPolicy);
+ doReturn(true).when(cameraPolicy).shouldCameraCompatControlAspectRatio(any());
+ doReturn(cameraCompatAspectRatio).when(cameraPolicy).getCameraCompatAspectRatio(any());
+ }
+
private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) {
final AppCompatReachabilityOverrides reachabilityOverrides =
mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 9967ccebeb1f..7dba1422d61d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -21,7 +21,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static org.junit.Assert.assertEquals;
@@ -165,31 +164,6 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
}
@Test
- public void testDelayingAnimationStart() {
- mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
- verifyZeroInteractions(mSpec);
- assertAnimating(mAnimatable);
- assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed());
- mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
- verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION), any());
- }
-
- @Test
- public void testDelayingAnimationStartAndCancelled() {
- mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
- mAnimatable.mSurfaceAnimator.cancelAnimation();
- verifyZeroInteractions(mSpec);
- assertNotAnimating(mAnimatable);
- assertTrue(mAnimatable.mFinishedCallbackCalled);
- assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
- verify(mTransaction).remove(eq(mAnimatable.mLeash));
- }
-
- @Test
public void testTransferAnimation() {
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
ANIMATION_TYPE_APP_TRANSITION);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 3c921c612705..4568c77204a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -249,7 +249,7 @@ public class TaskLaunchParamsModifierTests extends
ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
ActivityRecord source = createSourceActivity(freeformDisplay);
source.mHandoverLaunchDisplayId = freeformDisplay.mDisplayId;
- source.noDisplay = true;
+ source.setIsNoDisplay(true);
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder()
@@ -272,7 +272,7 @@ public class TaskLaunchParamsModifierTests extends
ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
ActivityRecord source = createSourceActivity(freeformDisplay);
source.mHandoverTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
- source.noDisplay = true;
+ source.setIsNoDisplay(true);
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder()
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index e8779c2b9ead..039a3ddd3e4f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -51,7 +51,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -1632,6 +1631,8 @@ public class TransitionTests extends WindowTestsBase {
transition.collect(taskA);
transition.setTransientLaunch(recent, taskA);
taskRecent.moveToFront("move-recent-to-front");
+ recent.setVisibility(true);
+ recent.setState(ActivityRecord.State.RESUMED, "test");
// During collecting and playing, the recent is on top so it is visible naturally.
// While B needs isTransientVisible to keep visibility because it is occluded by recents.
@@ -1644,15 +1645,21 @@ public class TransitionTests extends WindowTestsBase {
// Switch to another task. For example, use gesture navigation to switch tasks.
taskB.moveToFront("move-b-to-front");
+ appB.setVisibility(true);
// The previous app (taskA) should be paused first so it loses transient visible. Because
// visually it is taskA -> taskB, the pause -> resume order should be the same.
assertFalse(controller.isTransientVisible(taskA));
- // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
- // to avoid the latency to resume the current top, i.e. appB.
- assertTrue(controller.isTransientVisible(taskRecent));
- // The recent is paused after the transient transition is finished.
- controller.finishTransition(ActionChain.testFinish(transition));
+ // The recent is occluded by appB.
assertFalse(controller.isTransientVisible(taskRecent));
+ // Active transient launch won't be paused if the transition is not finished. It is to
+ // avoid the latency to resume the current top (appB) by waiting for both recent and appA
+ // to complete pause.
+ assertEquals(recent, taskRecent.getResumedActivity());
+ assertFalse(taskRecent.startPausing(false /* uiSleeping */, appB /* resuming */, "test"));
+ // ActivityRecord#makeInvisible will add the invisible recent to the stopping list.
+ // So when the transition finished, the recent can still be notified to pause and stop.
+ mDisplayContent.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
+ assertTrue(mSupervisor.mStoppingActivities.contains(recent));
}
@Test
@@ -2883,17 +2890,14 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTransitionsTriggerPerformanceHints() {
- final boolean explicitRefreshRateHints = explicitRefreshRateHints();
final var session = new SystemPerformanceHinter.HighPerfSession[1];
- if (explicitRefreshRateHints) {
- final SystemPerformanceHinter perfHinter = mWm.mSystemPerformanceHinter;
- spyOn(perfHinter);
- doAnswer(invocation -> {
- session[0] = (SystemPerformanceHinter.HighPerfSession) invocation.callRealMethod();
- spyOn(session[0]);
- return session[0];
- }).when(perfHinter).createSession(anyInt(), anyInt(), anyString());
- }
+ final SystemPerformanceHinter perfHinter = mWm.mSystemPerformanceHinter;
+ spyOn(perfHinter);
+ doAnswer(invocation -> {
+ session[0] = (SystemPerformanceHinter.HighPerfSession) invocation.callRealMethod();
+ spyOn(session[0]);
+ return session[0];
+ }).when(perfHinter).createSession(anyInt(), anyInt(), anyString());
final TransitionController controller = mDisplayContent.mTransitionController;
final TestTransitionPlayer player = registerTestTransitionPlayer();
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
@@ -2905,15 +2909,11 @@ public class TransitionTests extends WindowTestsBase {
player.start();
verify(mDisplayContent).enableHighPerfTransition(true);
- if (explicitRefreshRateHints) {
- verify(session[0]).start();
- }
+ verify(session[0]).start();
player.finish();
verify(mDisplayContent).enableHighPerfTransition(false);
- if (explicitRefreshRateHints) {
- verify(session[0]).close();
- }
+ verify(session[0]).close();
}
@Test
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
index d35dbb56437b..2dff392d4e34 100644
--- a/services/usb/OWNERS
+++ b/services/usb/OWNERS
@@ -1,9 +1,9 @@
-aprasath@google.com
-kumarashishg@google.com
-sarup@google.com
anothermark@google.com
+febinthattil@google.com
+aprasath@google.com
badhri@google.com
elaurent@google.com
albertccwang@google.com
jameswei@google.com
-howardyen@google.com \ No newline at end of file
+howardyen@google.com
+kumarashishg@google.com \ No newline at end of file
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 45a7fafa90a7..07969bd2cd43 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -304,11 +304,17 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
@Override
public void unloadModel(int modelHandle) {
synchronized (SoundTriggerModule.this) {
- int sessionId;
checkValid();
- sessionId = mLoadedModels.get(modelHandle).unload();
- mAudioSessionProvider.releaseSession(sessionId);
+ final var session = mLoadedModels.get(modelHandle).getSession();
+ mLoadedModels.remove(modelHandle);
+ mAudioSessionProvider.releaseSession(session.mSessionHandle);
}
+ // We don't need to post-synchronize on anything once the HAL has finished the unload
+ // and dispatched any appropriate callbacks -- since we don't do any state checking
+ // onModelUnloaded regardless.
+ // This is generally safe since there is no post-condition on the framework side when
+ // a model is unloaded. We assume that we won't ever have a modelHandle collision.
+ mHalService.unloadSoundModel(modelHandle);
}
@Override
@@ -402,6 +408,10 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
return mState;
}
+ private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession getSession() {
+ return mSession;
+ }
+
private void setState(@NonNull ModelState state) {
mState = state;
SoundTriggerModule.this.notifyAll();
@@ -426,16 +436,6 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo
return mHandle;
}
- /**
- * Unloads the model.
- * @return The audio session handle.
- */
- private int unload() {
- mHalService.unloadSoundModel(mHandle);
- mLoadedModels.remove(mHandle);
- return mSession.mSessionHandle;
- }
-
private IBinder startRecognition(@NonNull RecognitionConfig config) {
if (mIsStopping == true) {
throw new RecoverableException(Status.INTERNAL_ERROR, "Race occurred");
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index a34094ce6452..98949d0c45cf 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -68,7 +68,7 @@ public class Log {
// Used to synchronize singleton logging lazy initialization
private static final Object sSingletonSync = new Object();
private static EventManager sEventManager;
- private static SessionManager sSessionManager;
+ private static volatile SessionManager sSessionManager;
private static Object sLock = null;
/**
@@ -379,6 +379,23 @@ public class Log {
return sSessionManager;
}
+ @VisibleForTesting
+ public static SessionManager setSessionManager(Context context,
+ java.lang.Runnable cleanSessionRunnable) {
+ // Checking for null again outside of synchronization because we only need to synchronize
+ // during the lazy loading of the session logger. We don't need to synchronize elsewhere.
+ if (sSessionManager == null) {
+ synchronized (sSingletonSync) {
+ if (sSessionManager == null) {
+ sSessionManager = new SessionManager(cleanSessionRunnable);
+ sSessionManager.setContext(context);
+ return sSessionManager;
+ }
+ }
+ }
+ return sSessionManager;
+ }
+
public static void setTag(String tag) {
TAG = tag;
DEBUG = isLoggable(android.util.Log.DEBUG);
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 00e344c67cc5..ac1e69e92ec0 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -62,9 +62,7 @@ public class SessionManager {
@VisibleForTesting
public final ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(64);
- @VisibleForTesting
- public java.lang.Runnable mCleanStaleSessions = () ->
- cleanupStaleSessions(getSessionCleanupTimeoutMs());
+ private final java.lang.Runnable mCleanStaleSessions;
private final Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
// Overridden in LogTest to skip query to ContentProvider
@@ -110,29 +108,39 @@ public class SessionManager {
}
public SessionManager() {
+ mCleanStaleSessions = () -> cleanupStaleSessions(getSessionCleanupTimeoutMs());
+ }
+
+ @VisibleForTesting
+ public SessionManager(java.lang.Runnable cleanStaleSessionsRunnable) {
+ mCleanStaleSessions = cleanStaleSessionsRunnable;
}
private long getSessionCleanupTimeoutMs() {
return mSessionCleanupTimeoutMs.get();
}
- private synchronized void resetStaleSessionTimer() {
+ private void resetStaleSessionTimer() {
if (!Flags.endSessionImprovements()) {
- mSessionCleanupHandler.removeCallbacksAndMessages(null);
- // Will be null in Log Testing
- if (mCleanStaleSessions != null) {
- mSessionCleanupHandler.postDelayed(mCleanStaleSessions,
- getSessionCleanupTimeoutMs());
- }
- } else {
- if (mCleanStaleSessions != null
- && !mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) {
+ resetStaleSessionTimerOld();
+ return;
+ }
+ // Will be null in Log Testing
+ if (mCleanStaleSessions == null) return;
+ synchronized (mSessionCleanupHandler) {
+ if (!mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) {
mSessionCleanupHandler.postDelayed(mCleanStaleSessions,
getSessionCleanupTimeoutMs());
}
}
}
+ private synchronized void resetStaleSessionTimerOld() {
+ if (mCleanStaleSessions == null) return;
+ mSessionCleanupHandler.removeCallbacksAndMessages(null);
+ mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs());
+ }
+
/**
* Determines whether or not to start a new session or continue an existing session based on
* the {@link Session.Info} info passed into startSession. If info is null, a new Session is
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 9b83719402a0..be34619acac1 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -47,8 +47,6 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseArray;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -538,7 +536,13 @@ public final class SmsApplication {
}
private static String getDefaultSmsPackage(Context context, int userId) {
- return context.getSystemService(RoleManager.class).getSmsRoleHolder(userId);
+ // RoleManager might be null in unit tests running older mockito versions that do not
+ // support mocking final classes.
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ if (roleManager == null) {
+ return "";
+ }
+ return roleManager.getSmsRoleHolder(userId);
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0808cc3f1a75..6490cbe3e31a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2183,8 +2183,8 @@ public class CarrierConfigManager {
* Maximum size in bytes of the PDU to send or download when connected to a non-terrestrial
* network. MmsService will return a result code of MMS_ERROR_TOO_LARGE_FOR_TRANSPORT if
* the PDU exceeds this limit when connected to a non-terrestrial network.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT =
"mms_max_ntn_payload_size_bytes_int";
@@ -9850,9 +9850,8 @@ public class CarrierConfigManager {
* manually scanning available cellular network.
* If key is {@code true}, satellite plmn should not be exposed to user and should be
* automatically set, {@code false} otherwise. Default value is {@code true}.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL =
"remove_satellite_plmn_in_manual_network_scan_bool";
@@ -9877,18 +9876,18 @@ public class CarrierConfigManager {
/**
* Doesn't support unrestricted traffic on satellite network.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0;
/**
* Support unrestricted but bandwidth_constrained traffic on satellite network.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1;
/**
* Support unrestricted satellite network that serves all traffic.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_DATA_SUPPORT_ALL = 2;
/**
* Indicates what kind of traffic an {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED}
@@ -9898,8 +9897,8 @@ public class CarrierConfigManager {
* {@link ApnSetting#INFRASTRUCTURE_SATELLITE} from APN infrastructure_bitmask, and this
* configuration is ignored.
* By default it only supports restricted data.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT =
"satellite_data_support_mode_int";
@@ -9911,8 +9910,8 @@ public class CarrierConfigManager {
* {@link com.android.ims.ImsConfig.WfcModeFeatureValueConstants#WIFI_PREFERRED}
* {@code false} - roaming preference can be changed by user independently and is not
* overridden when device is connected to non-terrestrial network.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL =
"override_wfc_roaming_mode_while_using_ntn_bool";
@@ -9945,8 +9944,8 @@ public class CarrierConfigManager {
* Reference: GSMA TS.43-v11, 2.8.5 Fast Authentication and Token Management.
* `app_name` is an optional attribute in the request and may vary depending on the carrier
* requirement.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING =
"satellite_entitlement_app_name_string";
@@ -9954,9 +9953,8 @@ public class CarrierConfigManager {
* URL to redirect user to get more information about the carrier support for satellite.
*
* The default value is empty string.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING =
"satellite_information_redirect_url_string";
/**
@@ -9966,9 +9964,8 @@ public class CarrierConfigManager {
* This will need agreement with carriers before enabling this flag.
*
* The default value is false.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL =
"emergency_messaging_supported_bool";
@@ -9983,9 +9980,8 @@ public class CarrierConfigManager {
* prompt user to switch to using satellite emergency messaging.
*
* The default value is 30 seconds.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT =
"emergency_call_to_satellite_t911_handover_timeout_millis_int";
@@ -9998,9 +9994,8 @@ public class CarrierConfigManager {
* The default capabilities are
* {@link NetworkRegistrationInfo#SERVICE_TYPE_SMS}, and
* {@link NetworkRegistrationInfo#SERVICE_TYPE_MMS}
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY =
"carrier_roaming_satellite_default_services_int_array";
@@ -10031,9 +10026,8 @@ public class CarrierConfigManager {
* Defines the NIDD (Non-IP Data Delivery) APN to be used for carrier roaming to satellite
* attachment. For more on NIDD, see 3GPP TS 29.542.
* Note this config is the only source of truth regarding the definition of the APN.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING =
"satellite_nidd_apn_name_string";
@@ -10044,9 +10038,8 @@ public class CarrierConfigManager {
*
* If {@code false}, the emergency call is always blocked if device is in emergency satellite
* mode. Note if device is NOT in emergency satellite mode, emergency call is always allowed.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL =
"satellite_roaming_turn_off_session_for_emergency_call_bool";
@@ -10059,14 +10052,14 @@ public class CarrierConfigManager {
/**
* Device can connect to carrier roaming non-terrestrial network automatically.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC = 0;
/**
* Device can connect to carrier roaming non-terrestrial network only if user manually triggers
* satellite connection.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int CARRIER_ROAMING_NTN_CONNECT_MANUAL = 1;
/**
* Indicates carrier roaming non-terrestrial network connect type that the device can use to
@@ -10074,8 +10067,8 @@ public class CarrierConfigManager {
* If this key is set to CARRIER_ROAMING_NTN_CONNECT_MANUAL then connect button will be
* displayed to user when the device is eligible to use carrier roaming
* non-terrestrial network.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT =
"carrier_roaming_ntn_connect_type_int";
@@ -10088,7 +10081,6 @@ public class CarrierConfigManager {
* will be made to T911.
*
* The default value is {@link SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911}.
- *
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final String
@@ -10105,9 +10097,8 @@ public class CarrierConfigManager {
* After the timer is expired, device is marked as eligible for satellite communication.
*
* The default value is 180 seconds.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT =
"carrier_supported_satellite_notification_hysteresis_sec_int";
@@ -10153,13 +10144,23 @@ public class CarrierConfigManager {
"satellite_roaming_esos_inactivity_timeout_sec_int";
/**
+ * A string array containing the list of messaging apps that support satellite.
+ *
+ * The default value contains only "com.google.android.apps.messaging"
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final String KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY =
+ "satellite_supported_msg_apps_string_array";
+
+ /**
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
* the default APN (i.e. internet) will be used for tethering.
*
* This config is only available when using Preset APN(not user edited) as Preferred APN.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL =
"disable_dun_apn_while_roaming_with_preset_apn_bool";
@@ -11304,6 +11305,8 @@ public class CarrierConfigManager {
NetworkRegistrationInfo.SERVICE_TYPE_SMS,
NetworkRegistrationInfo.SERVICE_TYPE_MMS
});
+ sDefaults.putStringArray(KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY, new String[]{
+ "com.google.android.apps.messaging"});
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false);
sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.aidl b/telephony/java/android/telephony/satellite/EarfcnRange.aidl
new file mode 100644
index 000000000000..0b224d0b09bd
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/EarfcnRange.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable EarfcnRange;
diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.java b/telephony/java/android/telephony/satellite/EarfcnRange.java
new file mode 100644
index 000000000000..38043b570c2f
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/EarfcnRange.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * EARFCN (E-UTRA Absolute Radio Frequency Channel Number): A number that identifies a
+ * specific frequency channel in LTE/5G NR, used to define the carrier frequency.
+ * The range can be [0 ~ 65535] according to the 3GPP TS 36.101
+ *
+ * In satellite communication:
+ * - Efficient frequency allocation across a wide coverage area.
+ * - Handles Doppler shift due to satellite movement.
+ * - Manages interference with terrestrial networks.
+ *
+ * See 3GPP TS 36.101 and 38.101-1 for details.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public final class EarfcnRange implements Parcelable {
+
+ /**
+ * The start frequency of the earfcn range and is inclusive in the range
+ */
+ private int mStartEarfcn;
+
+ /**
+ * The end frequency of the earfcn range and is inclusive in the range.
+ */
+ private int mEndEarfcn;
+
+ private EarfcnRange(@NonNull Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mStartEarfcn);
+ dest.writeInt(mEndEarfcn);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mStartEarfcn = in.readInt();
+ mEndEarfcn = in.readInt();
+ }
+
+ /**
+ * Constructor for the EarfcnRange class.
+ * The range can be [0 ~ 65535] according to the 3GPP TS 36.101
+ *
+ * @param startEarfcn The starting earfcn value.
+ * @param endEarfcn The ending earfcn value.
+ */
+ public EarfcnRange(@IntRange(from = 0, to = 65535) int endEarfcn,
+ @IntRange(from = 0, to = 65535) int startEarfcn) {
+ mEndEarfcn = endEarfcn;
+ mStartEarfcn = startEarfcn;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "startEarfcn: " + mStartEarfcn + ", " + "endEarfcn: " + mEndEarfcn;
+ }
+
+ @NonNull
+ public static final Creator<EarfcnRange> CREATOR = new Creator<EarfcnRange>() {
+ @Override
+ public EarfcnRange createFromParcel(Parcel in) {
+ return new EarfcnRange(in);
+ }
+
+ @Override
+ public EarfcnRange[] newArray(int size) {
+ return new EarfcnRange[size];
+ }
+ };
+
+ /**
+ * Returns the starting earfcn value for this range.
+ * It can be [0 ~ 65535] according to the 3GPP TS 36.101
+ *
+ * @return The starting earfcn.
+ */
+ public @IntRange(from = 0, to = 65535) int getStartEarfcn() {
+ return mStartEarfcn;
+ }
+
+ /**
+ * Returns the ending earfcn value for this range.
+ * It can be [0 ~ 65535] according to the 3GPP TS 36.101
+ *
+ * @return The ending earfcn.
+ */
+ public @IntRange(from = 0, to = 65535) int getEndEarfcn() {
+ return mEndEarfcn;
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl
index a7eda482cb76..2730f90c4e5e 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl
@@ -16,6 +16,8 @@
package android.telephony.satellite;
+import android.telephony.satellite.SatelliteAccessConfiguration;
+
/**
* Interface for satellite communication allowed state callback.
* @hide
@@ -29,4 +31,14 @@ oneway interface ISatelliteCommunicationAllowedStateCallback {
* @param allowed whether satellite communication state or not
*/
void onSatelliteCommunicationAllowedStateChanged(in boolean isAllowed);
+
+ /**
+ * Callback method invoked when the satellite access configuration changes
+ *
+ * @param The satellite access configuration associated with the current location.
+ * When satellite is not allowed at the current location,
+ * {@code satelliteRegionalConfiguration} will be null.
+ */
+ void onSatelliteAccessConfigurationChanged(in SatelliteAccessConfiguration
+ satelliteAccessConfiguration);
}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDisallowedReasonsCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteDisallowedReasonsCallback.aidl
new file mode 100644
index 000000000000..9a6f6b821d28
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ISatelliteDisallowedReasonsCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+/**
+ * Interface for satellite disallowed reason change callback.
+ *
+ * @hide
+ */
+oneway interface ISatelliteDisallowedReasonsCallback {
+ /**
+ * Indicates that disallowed reason of satellite has changed.
+ * @param disallowedReasons list of disallowed reasons.
+ */
+ void onSatelliteDisallowedReasonsChanged(in int[] disallowedReasons);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl
new file mode 100644
index 000000000000..0214193a654f
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.telephony.satellite;
+
+ parcelable SatelliteAccessConfiguration; \ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
new file mode 100644
index 000000000000..c3ae70b48854
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.List;
+
+/**
+ * SatelliteAccessConfiguration is used to store satellite access configuration
+ * that will be applied to the satellite communication at the corresponding region.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public final class SatelliteAccessConfiguration implements Parcelable {
+ /**
+ * The list of satellites available at the current location.
+ */
+ @NonNull
+ private List<SatelliteInfo> mSatelliteInfoList;
+
+ /**
+ * The list of tag IDs associated with the current location
+ */
+ @NonNull
+ private int[] mTagIds;
+
+ /**
+ * Constructor for {@link SatelliteAccessConfiguration}.
+ *
+ * @param satelliteInfos The list of {@link SatelliteInfo} objects representing the satellites
+ * accessible with this configuration.
+ * @param tagIds The list of tag IDs associated with this configuration.
+ */
+ public SatelliteAccessConfiguration(@NonNull List<SatelliteInfo> satelliteInfos,
+ @NonNull int[] tagIds) {
+ mSatelliteInfoList = satelliteInfos;
+ mTagIds = tagIds;
+ }
+
+ public SatelliteAccessConfiguration(Parcel in) {
+ mSatelliteInfoList = in.createTypedArrayList(SatelliteInfo.CREATOR);
+ mTagIds = new int[in.readInt()];
+ in.readIntArray(mTagIds);
+ }
+
+ public static final Creator<SatelliteAccessConfiguration> CREATOR =
+ new Creator<SatelliteAccessConfiguration>() {
+ @Override
+ public SatelliteAccessConfiguration createFromParcel(Parcel in) {
+ return new SatelliteAccessConfiguration(in);
+ }
+
+ @Override
+ public SatelliteAccessConfiguration[] newArray(int size) {
+ return new SatelliteAccessConfiguration[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mSatelliteInfoList);
+ if (mTagIds != null && mTagIds.length > 0) {
+ dest.writeInt(mTagIds.length);
+ dest.writeIntArray(mTagIds);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ /**
+ * Returns a list of {@link SatelliteInfo} objects representing the satellites
+ * associated with this object.
+ *
+ * @return The list of {@link SatelliteInfo} objects.
+ */
+ @NonNull
+ public List<SatelliteInfo> getSatelliteInfos() {
+ return mSatelliteInfoList;
+ }
+
+ /**
+ * Returns a list of tag IDs associated with this object.
+ *
+ * @return The list of tag IDs.
+ */
+ @NonNull
+ public int[] getTagIds() {
+ return mTagIds;
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
index 1a870202d096..bffb11f23d56 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
import com.android.internal.telephony.flags.Flags;
@@ -40,4 +41,17 @@ public interface SatelliteCommunicationAllowedStateCallback {
*/
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteCommunicationAllowedStateChanged(boolean isAllowed);
+
+ /**
+ * Callback method invoked when the satellite access configuration changes
+ *
+ * @param satelliteAccessConfiguration The satellite access configuration associated with
+ * the current location. When satellite is not allowed at
+ * the current location,
+ * {@code satelliteRegionalConfiguration} will be null.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ default void onSatelliteAccessConfigurationChanged(
+ @Nullable SatelliteAccessConfiguration satelliteAccessConfiguration) {};
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java b/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
new file mode 100644
index 000000000000..5e276aa49b05
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteDisallowedReasonsCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for disallowed reason of satellite change events.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteDisallowedReasonsCallback {
+
+ /**
+ * Called when disallowed reason of satellite has changed.
+ * @param disallowedReasons Integer array of disallowed reasons.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ void onSatelliteDisallowedReasonsChanged(@NonNull int[] disallowedReasons);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.aidl b/telephony/java/android/telephony/satellite/SatelliteInfo.aidl
new file mode 100644
index 000000000000..fc2303b080a5
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.telephony.satellite;
+
+ parcelable SatelliteInfo; \ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.java b/telephony/java/android/telephony/satellite/SatelliteInfo.java
new file mode 100644
index 000000000000..bca907e49993
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteInfo.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * SatelliteInfo stores a satellite's identification, position, and frequency information
+ * facilitating efficient satellite communications.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public class SatelliteInfo implements Parcelable {
+ /**
+ * Unique identification number for the satellite.
+ * This ID is used to distinguish between different satellites in the network.
+ */
+ @NonNull
+ private UUID mId;
+
+ /**
+ * Position information of a satellite.
+ * This includes the longitude and altitude of the satellite.
+ */
+ private SatellitePosition mPosition;
+
+ /**
+ * The frequency bands to scan. Bands and earfcns won't overlap.
+ * Bands will be filled only if the whole band is needed.
+ * Maximum length of the vector is 8.
+ */
+ private int[] mBands;
+
+ /**
+ * EARFCN (E-UTRA Absolute Radio Frequency Channel Number) Ranges
+ * The supported frequency range list.
+ * Maximum length of the vector is 8.
+ */
+ private final List<EarfcnRange> mEarfcnRangeList;
+
+ protected SatelliteInfo(Parcel in) {
+ ParcelUuid parcelUuid = in.readParcelable(
+ ParcelUuid.class.getClassLoader(), ParcelUuid.class);
+ if (parcelUuid != null) {
+ mId = parcelUuid.getUuid();
+ }
+ mPosition = in.readParcelable(SatellitePosition.class.getClassLoader(),
+ SatellitePosition.class);
+ int numBands = in.readInt();
+ mBands = new int[numBands];
+ if (numBands > 0) {
+ for (int i = 0; i < numBands; i++) {
+ mBands[i] = in.readInt();
+ }
+ }
+ mEarfcnRangeList = in.createTypedArrayList(EarfcnRange.CREATOR);
+ }
+
+ /**
+ * Constructor for {@link SatelliteInfo}.
+ *
+ * @param satelliteId The ID of the satellite.
+ * @param satellitePosition The {@link SatellitePosition} of the satellite.
+ * @param bands The list of frequency bands supported by the satellite.
+ * @param earfcnRanges The list of {@link EarfcnRange} objects representing the EARFCN
+ * ranges supported by the satellite.
+ */
+ public SatelliteInfo(@NonNull UUID satelliteId, @NonNull SatellitePosition satellitePosition,
+ @NonNull int[] bands, @NonNull List<EarfcnRange> earfcnRanges) {
+ mId = satelliteId;
+ mPosition = satellitePosition;
+ mBands = bands;
+ mEarfcnRangeList = earfcnRanges;
+ }
+
+ public static final Creator<SatelliteInfo> CREATOR = new Creator<SatelliteInfo>() {
+ @Override
+ public SatelliteInfo createFromParcel(Parcel in) {
+ return new SatelliteInfo(in);
+ }
+
+ @Override
+ public SatelliteInfo[] newArray(int size) {
+ return new SatelliteInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(new ParcelUuid(mId), flags);
+ dest.writeParcelable(mPosition, flags);
+ if (mBands != null && mBands.length > 0) {
+ dest.writeInt(mBands.length);
+ dest.writeIntArray(mBands);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeTypedList(mEarfcnRangeList);
+ }
+
+ /**
+ * Returns the ID of the satellite.
+ *
+ * @return The satellite ID.
+ */
+ @NonNull
+ public UUID getSatelliteId() {
+ return mId;
+ }
+
+ /**
+ * Returns the position of the satellite.
+ *
+ * @return The {@link SatellitePosition} of the satellite.
+ */
+ public SatellitePosition getSatellitePosition() {
+ return mPosition;
+ }
+
+ /**
+ * Returns the list of frequency bands supported by the satellite.
+ *
+ * @return The list of frequency bands.
+ */
+ @NonNull
+ public int[] getBands() {
+ return mBands;
+ }
+
+ /**
+ * Returns the list of EARFCN ranges supported by the satellite.
+ *
+ * @return The list of {@link EarfcnRange} objects.
+ */
+ @NonNull
+ public List<EarfcnRange> getEarfcnRanges() {
+ return mEarfcnRangeList;
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index be02232abe1b..887b79883710 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -36,6 +36,7 @@ import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyFrameworkInitializer;
@@ -104,6 +105,11 @@ public final class SatelliteManager {
sSatelliteCommunicationAllowedStateCallbackMap =
new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<SatelliteDisallowedReasonsCallback,
+ ISatelliteDisallowedReasonsCallback>
+ sSatelliteDisallowedReasonsCallbackMap =
+ new ConcurrentHashMap<>();
+
private final int mSubId;
/**
@@ -267,6 +273,14 @@ public final class SatelliteManager {
public static final String KEY_DEPROVISION_SATELLITE_TOKENS = "deprovision_satellite";
/**
+ * Bundle key to get the response from
+ * {@link #requestSatelliteAccessConfigurationForCurrentLocation(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+ public static final String KEY_SATELLITE_ACCESS_CONFIGURATION =
+ "satellite_access_configuration";
+
+ /**
* The request was successfully processed.
* @hide
*/
@@ -478,43 +492,43 @@ public final class SatelliteManager {
/**
* Telephony framework needs to access the current location of the device to perform the
* request. However, location in the settings is disabled by users.
- *
* @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_LOCATION_DISABLED = 25;
/**
* Telephony framework needs to access the current location of the device to perform the
* request. However, Telephony fails to fetch the current location from location service.
- *
* @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_LOCATION_NOT_AVAILABLE = 26;
/**
* Emergency call is in progress.
- *
* @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS = 27;
/**
* Disabling satellite is in progress.
- *
* @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_DISABLE_IN_PROGRESS = 28;
/**
* Enabling satellite is in progress.
- *
* @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_RESULT_ENABLE_IN_PROGRESS = 29;
/** @hide */
@@ -710,7 +724,7 @@ public final class SatelliteManager {
public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2;
/**
- * This intent will be broadcasted if there are any change to list of subscriber informations.
+ * This intent will be broadcasted if there are any change to list of subscriber information.
* This intent will be sent only to the app with component defined in
* config_satellite_carrier_roaming_esos_provisioned_class and package defined in
* config_satellite_gateway_service_package
@@ -1344,12 +1358,16 @@ public final class SatelliteManager {
* The satellite modem is being powered on.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_MODEM_STATE_ENABLING_SATELLITE = 8;
/**
* The satellite modem is being powered off.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_MODEM_STATE_DISABLING_SATELLITE = 9;
/**
@@ -1409,6 +1427,8 @@ public final class SatelliteManager {
* there is any incoming message.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3;
/**
@@ -1416,6 +1436,8 @@ public final class SatelliteManager {
* is the last message to emergency service provider indicating still needs help.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4;
/**
@@ -1423,12 +1445,16 @@ public final class SatelliteManager {
* is the last message to emergency service provider indicating no more help is needed.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5;
/**
* Datagram type indicating that the message to be sent or received is of type SMS.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int DATAGRAM_TYPE_SMS = 6;
/**
@@ -1436,6 +1462,8 @@ public final class SatelliteManager {
* for pending incoming SMS.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS = 7;
/** @hide */
@@ -1456,6 +1484,8 @@ public final class SatelliteManager {
* Satellite communication restricted by user.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER = 0;
/**
@@ -1487,6 +1517,47 @@ public final class SatelliteManager {
public @interface SatelliteCommunicationRestrictionReason {}
/**
+ * Satellite is disallowed because it is not supported.
+ * @hide
+ */
+ public static final int SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED = 0;
+
+ /**
+ * Satellite is disallowed because it has not been provisioned.
+ * @hide
+ */
+ public static final int SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED = 1;
+
+ /**
+ * Satellite is disallowed because it is currently outside an allowed region.
+ * @hide
+ */
+ public static final int SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION = 2;
+
+ /**
+ * Satellite is disallowed because an unsupported default message application is being used.
+ * @hide
+ */
+ public static final int SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP = 3;
+
+ /**
+ * Satellite is disallowed because location settings have been disabled.
+ * @hide
+ */
+ public static final int SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED = 4;
+
+ /** @hide */
+ @IntDef(prefix = "SATELLITE_DISALLOWED_REASON_", value = {
+ SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED,
+ SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED,
+ SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION,
+ SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP,
+ SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SatelliteDisallowedReason {}
+
+ /**
* Start receiving satellite transmission updates.
* This can be called by the pointing UI when the user starts pointing to the satellite.
* Modem should continue to report the pointing input as the device or satellite moves.
@@ -2270,6 +2341,68 @@ public final class SatelliteManager {
}
/**
+ * Request to get satellite access configuration for the current location.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback object to which the result will be delivered.
+ * If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+ * will return a {@code SatelliteAccessConfiguration} with value the regional
+ * satellite access configuration at the current location.
+ * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+ * will return a {@link SatelliteException} with the {@link SatelliteResult}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public void requestSatelliteAccessConfigurationForCurrentLocation(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<SatelliteAccessConfiguration, SatelliteException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ if (resultData.containsKey(KEY_SATELLITE_ACCESS_CONFIGURATION)) {
+ SatelliteAccessConfiguration satelliteAccessConfiguration =
+ resultData.getParcelable(KEY_SATELLITE_ACCESS_CONFIGURATION,
+ SatelliteAccessConfiguration.class);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(satelliteAccessConfiguration)));
+ } else {
+ loge("KEY_SATELLITE_ACCESS_CONFIGURATION does not exist.");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+ }
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
+ }
+ };
+ telephony.requestSatelliteAccessConfigurationForCurrentLocation(receiver);
+ } else {
+ loge("requestSatelliteAccessConfigurationForCurrentLocation() invalid telephony");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ } catch (RemoteException ex) {
+ loge("requestSatelliteAccessConfigurationForCurrentLocation() RemoteException: "
+ + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ }
+
+ /**
* Request to get the duration in seconds after which the satellite will be visible.
* This will be {@link Duration#ZERO} if the satellite is currently visible.
*
@@ -2374,7 +2507,7 @@ public final class SatelliteManager {
* <li>There is no satellite communication restriction, which is added by
* {@link #addAttachRestrictionForCarrier(int, int, Executor, Consumer)}</li>
* <li>The carrier config {@link
- * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to
+ * CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to
* {@code true}.</li>
* </ul>
*
@@ -2579,12 +2712,125 @@ public final class SatelliteManager {
}
/**
+ * Returns list of disallowed reasons of satellite.
+ *
+ * @return list of disallowed reasons of satellite.
+ *
+ * @throws SecurityException if caller doesn't have required permission.
+ * @throws IllegalStateException if Telephony process isn't available.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @SatelliteDisallowedReason
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull
+ public List<Integer> getSatelliteDisallowedReasons() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ int[] receivedArray = telephony.getSatelliteDisallowedReasons();
+ if (receivedArray.length == 0) {
+ logd("receivedArray is empty, create empty list");
+ return new ArrayList<>();
+ } else {
+ return Arrays.stream(receivedArray).boxed().collect(Collectors.toList());
+ }
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("getSatelliteDisallowedReasons() RemoteException: " + ex);
+ ex.rethrowAsRuntimeException();
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * Registers for disallowed reasons change event from satellite service.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle disallowed reasons changed event.
+ *
+ * @throws SecurityException if caller doesn't have required permission.
+ * @throws IllegalStateException if Telephony process is not available.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void registerForSatelliteDisallowedReasonsChanged(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull SatelliteDisallowedReasonsCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ISatelliteDisallowedReasonsCallback internalCallback =
+ new ISatelliteDisallowedReasonsCallback.Stub() {
+ @Override
+ public void onSatelliteDisallowedReasonsChanged(
+ int[] disallowedReasons) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onSatelliteDisallowedReasonsChanged(
+ disallowedReasons)));
+ }
+ };
+ telephony.registerForSatelliteDisallowedReasonsChanged(internalCallback);
+ sSatelliteDisallowedReasonsCallbackMap.put(callback, internalCallback);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("registerForSatelliteDisallowedReasonsChanged() RemoteException" + ex);
+ ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Unregisters for disallowed reasons change event from satellite service.
+ *
+ * @param callback The callback that was passed to
+ * {@link #registerForSatelliteDisallowedReasonsChanged(
+ * Executor, SatelliteDisallowedReasonsCallback)}
+ *
+ * @throws SecurityException if caller doesn't have required permission.
+ * @throws IllegalStateException if Telephony process is not available.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void unregisterForSatelliteDisallowedReasonsChanged(
+ @NonNull SatelliteDisallowedReasonsCallback callback) {
+ Objects.requireNonNull(callback);
+ ISatelliteDisallowedReasonsCallback internalCallback =
+ sSatelliteDisallowedReasonsCallbackMap.remove(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ if (internalCallback != null) {
+ telephony.unregisterForSatelliteDisallowedReasonsChanged(internalCallback);
+ } else {
+ loge("unregisterForSatelliteDisallowedReasonsChanged: No internal callback.");
+ throw new IllegalArgumentException("callback is not valid");
+ }
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("unregisterForSatelliteDisallowedReasonsChanged() RemoteException: " + ex);
+ ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Request to get the signal strength of the satellite connection.
*
* <p>
* Note: This API is specifically designed for OEM enabled satellite connectivity only.
* For satellite connectivity enabled using carrier roaming, please refer to
- * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+ * {@link TelephonyCallback.SignalStrengthsListener}, and
* {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
* </p>
*
@@ -2655,7 +2901,7 @@ public final class SatelliteManager {
* <p>
* Note: This API is specifically designed for OEM enabled satellite connectivity only.
* For satellite connectivity enabled using carrier roaming, please refer to
- * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+ * {@link TelephonyCallback.SignalStrengthsListener}, and
* {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
* </p>
*
@@ -2972,6 +3218,15 @@ public final class SatelliteManager {
() -> callback.onSatelliteCommunicationAllowedStateChanged(
isAllowed)));
}
+
+ @Override
+ public void onSatelliteAccessConfigurationChanged(
+ @Nullable SatelliteAccessConfiguration
+ satelliteAccessConfiguration) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onSatelliteAccessConfigurationChanged(
+ satelliteAccessConfiguration)));
+ }
};
sSatelliteCommunicationAllowedStateCallbackMap.put(callback, internalCallback);
return telephony.registerForCommunicationAllowedStateChanged(
@@ -3256,6 +3511,40 @@ public final class SatelliteManager {
}
}
+ /**
+ * Inform whether application supports NTN SMS in satellite mode.
+ *
+ * This method is used by default messaging application to inform framework whether it supports
+ * NTN SMS or not.
+ *
+ * Invoking this API will internally result in triggering
+ * {@link android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener
+ * #onCarrierRoamingNtnAvailableServicesChanged(List)} and
+ * {@link android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener
+ * #onCarrierRoamingNtnEligibleStateChanged(boolean)} callbacks.
+ *
+ * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @hide
+ */
+ @RequiresPermission(allOf = {Manifest.permission.SATELLITE_COMMUNICATION,
+ Manifest.permission.SEND_SMS})
+ public void setNtnSmsSupported(boolean ntnSmsSupported) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setNtnSmsSupported(ntnSmsSupported);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("setNtnSmsSupported() RemoteException:" + ex);
+ ex.rethrowAsRuntimeException();
+ }
+ }
+
@Nullable
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.aidl b/telephony/java/android/telephony/satellite/SatellitePosition.aidl
new file mode 100644
index 000000000000..a8028eb48ee7
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatellitePosition.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.telephony.satellite;
+
+ parcelable SatellitePosition; \ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.java b/telephony/java/android/telephony/satellite/SatellitePosition.java
new file mode 100644
index 000000000000..1e8c0180f456
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatellitePosition.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * The position of a satellite in Earth orbit.
+ *
+ * Longitude is the angular distance, measured in degrees, east or west of the prime longitude line
+ * ranging from -180 to 180 degrees
+ * Altitude is the distance from the center of the Earth to the satellite, measured in kilometers
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public class SatellitePosition implements Parcelable {
+
+ /**
+ * The longitude of the satellite in degrees, ranging from -180 to 180 degrees
+ */
+ private double mLongitudeDegree;
+
+ /**
+ * The distance from the center of the earth to the satellite, measured in kilometers
+ */
+ private double mAltitudeKm;
+
+ /**
+ * Constructor for {@link SatellitePosition} used to create an instance from a {@link Parcel}.
+ *
+ * @param in The {@link Parcel} to read the satellite position data from.
+ */
+ public SatellitePosition(Parcel in) {
+ mLongitudeDegree = in.readDouble();
+ mAltitudeKm = in.readDouble();
+ }
+
+ /**
+ * Constructor for {@link SatellitePosition}.
+ *
+ * @param longitudeDegree The longitude of the satellite in degrees.
+ * @param altitudeKm The altitude of the satellite in kilometers.
+ */
+ public SatellitePosition(double longitudeDegree, double altitudeKm) {
+ mLongitudeDegree = longitudeDegree;
+ mAltitudeKm = altitudeKm;
+ }
+
+ public static final Creator<SatellitePosition> CREATOR = new Creator<SatellitePosition>() {
+ @Override
+ public SatellitePosition createFromParcel(Parcel in) {
+ return new SatellitePosition(in);
+ }
+
+ @Override
+ public SatellitePosition[] newArray(int size) {
+ return new SatellitePosition[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mLongitudeDegree);
+ dest.writeDouble(mAltitudeKm);
+ }
+
+ /**
+ * Returns the longitude of the satellite in degrees, ranging from -180 to 180 degrees.
+ *
+ * @return The longitude of the satellite.
+ */
+ public double getLongitudeDegrees() {
+ return mLongitudeDegree;
+ }
+
+ /**
+ * Returns the altitude of the satellite in kilometers
+ *
+ * @return The altitude of the satellite.
+ */
+ public double getAltitudeKm() {
+ return mAltitudeKm;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 231c8f551389..a58427318b55 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -71,6 +71,7 @@ import android.telephony.satellite.INtnSignalStrengthCallback;
import android.telephony.satellite.ISatelliteCapabilitiesCallback;
import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.ISatelliteDisallowedReasonsCallback;
import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
import android.telephony.satellite.ISatelliteSupportedStateCallback;
@@ -885,7 +886,7 @@ interface ITelephony {
/**
* @return true if the ImsService to bind to for the slot id specified was set, false otherwise.
*/
- boolean setBoundImsServiceOverride(int slotIndex, boolean isCarrierService,
+ boolean setBoundImsServiceOverride(int slotIndex, int userId, boolean isCarrierService,
in int[] featureTypes, in String packageName);
/**
@@ -2955,6 +2956,37 @@ interface ITelephony {
in boolean needFullScreenPointingUI, IIntegerConsumer callback);
/**
+ * Returns integer array of disallowed reasons of satellite.
+ *
+ * @return Integer array of disallowed reasons of satellite.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ int[] getSatelliteDisallowedReasons();
+
+ /**
+ * Registers for disallowed reasons change event from satellite service.
+ *
+ * @param callback The callback to handle disallowed reasons changed event.
+ *
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void registerForSatelliteDisallowedReasonsChanged(
+ ISatelliteDisallowedReasonsCallback callback);
+
+ /**
+ * Unregisters for disallowed reasons change event from satellite service.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param callback The callback to handle disallowed reasons changed event.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void unregisterForSatelliteDisallowedReasonsChanged(
+ ISatelliteDisallowedReasonsCallback callback);
+
+ /**
* Request to get whether satellite communication is allowed for the current location.
*
* @param subId The subId of the subscription to get whether satellite communication is allowed
@@ -2967,6 +2999,16 @@ interface ITelephony {
void requestIsCommunicationAllowedForCurrentLocation(int subId, in ResultReceiver receiver);
/**
+ * Request to get satellite access configuration for the current location.
+ *
+ * @param receiver Result receiver to get the error code of the request
+ * and satellite access configuration for the current location.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void requestSatelliteAccessConfigurationForCurrentLocation(in ResultReceiver receiver);
+
+ /**
* Request to get the time after which the satellite will be visible.
*
* @param receiver Result receiver to get the error code of the request and the requested
@@ -3455,4 +3497,15 @@ interface ITelephony {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void deprovisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
+
+ /**
+ * Inform whether application supports NTN SMS in satellite mode.
+ *
+ * This method is used by default messaging application to inform framework whether it supports
+ * NTN SMS or not.
+ *
+ * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}.
+ * @hide
+ */
+ void setNtnSmsSupported(boolean ntnSmsSupported);
}
diff --git a/tests/AppJankTest/AndroidManifest.xml b/tests/AppJankTest/AndroidManifest.xml
index ae973393b90e..abed1798c47c 100644
--- a/tests/AppJankTest/AndroidManifest.xml
+++ b/tests/AppJankTest/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.app.jank.tests">
- <application>
+ <application android:appCategory="news">
<uses-library android:name="android.test.runner" />
<activity android:name=".EmptyActivity"
android:label="EmptyActivity"
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
new file mode 100644
index 000000000000..1bdf019d6c42
--- /dev/null
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.app.jank.Flags;
+import android.app.jank.JankTracker;
+import android.app.jank.StateTracker;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.Choreographer;
+import android.view.View;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.policy.DecorView;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+public class JankTrackerTest {
+ private Choreographer mChoreographer;
+ private JankTracker mJankTracker;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ /**
+ * Start an empty activity so decore view is not null when creating the JankTracker instance.
+ */
+ private static ActivityScenario<EmptyActivity> sEmptyActivityRule;
+
+ private static String sActivityName;
+
+ private static View sActivityDecorView;
+
+ @BeforeClass
+ public static void classSetup() {
+ sEmptyActivityRule = ActivityScenario.launch(EmptyActivity.class);
+ sEmptyActivityRule.onActivity(activity -> {
+ sActivityDecorView = activity.getWindow().getDecorView();
+ sActivityName = activity.toString();
+ });
+ }
+
+ @AfterClass
+ public static void classTearDown() {
+ sEmptyActivityRule.close();
+ }
+
+ @Before
+ @UiThreadTest
+ public void setup() {
+ mChoreographer = Choreographer.getInstance();
+ mJankTracker = new JankTracker(mChoreographer, sActivityDecorView);
+ mJankTracker.setActivityName(sActivityName);
+ }
+
+ /**
+ * When jank tracking is enabled the activity name should be added as a state to associate
+ * frames to it.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTracking_WhenEnabled_ActivityAdded() {
+ mJankTracker.enableAppJankTracking();
+
+ ArrayList<StateTracker.StateData> stateData = new ArrayList<>();
+ mJankTracker.getAllUiStates(stateData);
+
+ assertEquals(1, stateData.size());
+
+ StateTracker.StateData firstState = stateData.getFirst();
+
+ assertEquals(sActivityName, firstState.mWidgetId);
+ }
+
+ /**
+ * No states should be added when tracking is disabled.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTrackingDisabled_StatesShouldNot_BeAddedToTracker() {
+ mJankTracker.disableAppJankTracking();
+
+ mJankTracker.addUiState("FAKE_CATEGORY", "FAKE_ID",
+ "FAKE_STATE");
+
+ ArrayList<StateTracker.StateData> stateData = new ArrayList<>();
+ mJankTracker.getAllUiStates(stateData);
+
+ assertEquals(0, stateData.size());
+ }
+
+ /**
+ * The activity name as well as the test state should be added for frame association.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTrackingEnabled_StatesShould_BeAddedToTracker() {
+ mJankTracker.forceListenerRegistration();
+
+ mJankTracker.enableAppJankTracking();
+ mJankTracker.addUiState("FAKE_CATEGORY", "FAKE_ID",
+ "FAKE_STATE");
+
+ ArrayList<StateTracker.StateData> stateData = new ArrayList<>();
+ mJankTracker.getAllUiStates(stateData);
+
+ assertEquals(2, stateData.size());
+ }
+
+ /**
+ * Activity state should only be added once even if jank tracking is enabled multiple times.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTrackingEnabled_EnabledCalledTwice_ActivityStateOnlyAddedOnce() {
+ mJankTracker.enableAppJankTracking();
+
+ ArrayList<StateTracker.StateData> stateData = new ArrayList<>();
+ mJankTracker.getAllUiStates(stateData);
+
+ assertEquals(1, stateData.size());
+
+ stateData.clear();
+
+ mJankTracker.enableAppJankTracking();
+ mJankTracker.getAllUiStates(stateData);
+
+ assertEquals(1, stateData.size());
+ }
+
+ /**
+ * Test confirms a JankTracker object is retrieved from the activity.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_LOGGING_ENABLED)
+ public void jankTracker_NotNull_WhenRetrievedFromDecorView() {
+ DecorView decorView = (DecorView) sActivityDecorView;
+ JankTracker jankTracker = decorView.getJankTracker();
+
+ assertNotNull(jankTracker);
+ }
+}
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 30cc002b4144..bfce3d276a2d 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -168,32 +168,28 @@ public class BatteryUsageStatsPerfTest {
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(123)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
- .setConsumedPower(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 10300)
- .setUsageDurationMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+ .addConsumedPower(123)
+ .addConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ .addConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+ .addUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 10300)
+ .addUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
for (int i = 0; i < 1000; i++) {
final UidBatteryConsumer.Builder consumerBuilder =
builder.getOrCreateUidBatteryConsumerBuilder(i)
.setPackageWithHighestDrain("example.packagename" + i)
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
+ .setTimeInProcessStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
+ .setTimeInProcessStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
componentId++) {
- consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
+ consumerBuilder.addConsumedPower(componentId, componentId * 123.0,
BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
+ consumerBuilder.addUsageDurationMillis(componentId, componentId * 1000);
}
consumerBuilder
- .setConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
- .setUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+ .addConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+ .addUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
}
return builder.build();
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 1c6bd1114c1c..ea61ad9d4481 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -21,13 +21,14 @@ import android.graphics.Insets
import android.graphics.Rect
import android.graphics.Region
import android.os.SystemClock
-import android.platform.uiautomator_helpers.DeviceHelpers
+import android.platform.uiautomatorhelpers.DeviceHelpers
import android.tools.device.apphelpers.IStandardAppHelper
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.wm.WindowingMode
import android.view.WindowInsets
import android.view.WindowManager
+import android.window.DesktopModeFlags
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
@@ -35,7 +36,6 @@ import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod.TOUCH
-import com.android.window.flags.Flags
import java.time.Duration
/**
@@ -107,7 +107,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
// drag the window to move to desktop
if (motionEventHelper.inputMethod == TOUCH
- && Flags.enableHoldToDragAppHandle()) {
+ && DesktopModeFlags.ENABLE_HOLD_TO_DRAG_APP_HANDLE.isTrue) {
// Touch requires hold-to-drag.
motionEventHelper.holdToDrag(startX, startY, startX, endY, steps = 100)
} else {
@@ -132,6 +132,24 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
+ private fun getMinimizeButtonForTheApp(caption: UiObject2?): UiObject2 {
+ return caption
+ ?.children
+ ?.find { it.resourceName.endsWith(MINIMIZE_BUTTON_VIEW) }
+ ?: error("Unable to find resource $MINIMIZE_BUTTON_VIEW\n")
+ }
+
+ fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) {
+ val caption = getCaptionForTheApp(wmHelper, device)
+ val minimizeButton = getMinimizeButtonForTheApp(caption)
+ minimizeButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceDisappeared(innerHelper)
+ .waitForAndVerify()
+ }
+
/** Open maximize menu and click snap resize button on the app header for the given app. */
fun snapResizeDesktopApp(
wmHelper: WindowManagerStateHelper,
@@ -141,7 +159,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
) {
val caption = getCaptionForTheApp(wmHelper, device)
val maximizeButton = getMaximizeButtonForTheApp(caption)
- maximizeButton?.longClick()
+ maximizeButton.longClick()
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
val buttonResId = if (toLeft) SNAP_LEFT_BUTTON else SNAP_RIGHT_BUTTON
@@ -400,6 +418,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) :
const val DESKTOP_MODE_BUTTON: String = "desktop_button"
const val SNAP_LEFT_BUTTON: String = "maximize_menu_snap_left_button"
const val SNAP_RIGHT_BUTTON: String = "maximize_menu_snap_right_button"
+ const val MINIMIZE_BUTTON_VIEW: String = "minimize_window"
val caption: BySelector
get() = By.res(SYSTEMUI_PACKAGE, CAPTION)
}
diff --git a/tests/Input/res/xml/bookmarks.xml b/tests/Input/res/xml/bookmarks.xml
new file mode 100644
index 000000000000..ba3f1871cdec
--- /dev/null
+++ b/tests/Input/res/xml/bookmarks.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<bookmarks>
+ <!-- the key combinations for the following shortcuts must be in sync
+ with the key combinations sent by the test in KeyGestureControllerTests.java -->
+ <bookmark
+ role="android.app.role.BROWSER"
+ shortcut="b" />
+ <bookmark
+ category="android.intent.category.APP_CONTACTS"
+ shortcut="c" />
+ <bookmark
+ category="android.intent.category.APP_EMAIL"
+ shortcut="e" />
+ <bookmark
+ category="android.intent.category.APP_CALENDAR"
+ shortcut="k" />
+ <bookmark
+ category="android.intent.category.APP_MAPS"
+ shortcut="m" />
+ <bookmark
+ category="android.intent.category.APP_MUSIC"
+ shortcut="p" />
+ <bookmark
+ role="android.app.role.SMS"
+ shortcut="s" />
+ <bookmark
+ category="android.intent.category.APP_CALCULATOR"
+ shortcut="u" />
+
+ <bookmark
+ role="android.app.role.BROWSER"
+ shortcut="b"
+ shift="true" />
+
+ <bookmark
+ category="android.intent.category.APP_CONTACTS"
+ shortcut="c"
+ shift="true" />
+
+ <bookmark
+ package="com.test"
+ class="com.test.BookmarkTest"
+ shortcut="j"
+ shift="true" />
+</bookmarks> \ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
index 01c56b7148cd..e281a3fb1287 100644
--- a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt
@@ -21,6 +21,7 @@ import android.hardware.input.InputManager
import android.hardware.input.KeyGestureEvent
import android.platform.test.annotations.Presubmit
import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -42,7 +43,7 @@ class InputGestureManagerTests {
@Before
fun setup() {
- inputGestureManager = InputGestureManager()
+ inputGestureManager = InputGestureManager(ApplicationProvider.getApplicationContext())
}
@Test
@@ -60,13 +61,13 @@ class InputGestureManagerTests {
assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS, result)
assertEquals(
listOf(customGesture),
- inputGestureManager.getCustomInputGestures(USER_ID)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
)
inputGestureManager.removeCustomInputGesture(USER_ID, customGesture)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
)
}
@@ -85,7 +86,7 @@ class InputGestureManagerTests {
assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST, result)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
)
}
@@ -114,7 +115,7 @@ class InputGestureManagerTests {
assertEquals(InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS, result)
assertEquals(
listOf(customGesture),
- inputGestureManager.getCustomInputGestures(USER_ID)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
)
}
@@ -143,13 +144,67 @@ class InputGestureManagerTests {
assertEquals(
listOf(customGesture, customGesture2),
- inputGestureManager.getCustomInputGestures(USER_ID)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
)
- inputGestureManager.removeAllCustomInputGestures(USER_ID)
+ inputGestureManager.removeAllCustomInputGestures(USER_ID, /* filter = */null)
assertEquals(
listOf<InputGestureData>(),
- inputGestureManager.getCustomInputGestures(USER_ID)
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ )
+ }
+
+ @Test
+ fun filteringBasedOnTouchpadOrKeyGestures() {
+ val customKeyGesture = InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ KeyEvent.KEYCODE_H,
+ KeyEvent.META_META_ON
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ .build()
+ inputGestureManager.addCustomInputGesture(USER_ID, customKeyGesture)
+ val customTouchpadGesture = InputGestureData.Builder()
+ .setTrigger(
+ InputGestureData.createTouchpadTrigger(
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP
+ )
+ )
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)
+ .build()
+ inputGestureManager.addCustomInputGesture(USER_ID, customTouchpadGesture)
+
+ assertEquals(
+ listOf(customTouchpadGesture, customKeyGesture),
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ )
+ assertEquals(
+ listOf(customKeyGesture),
+ inputGestureManager.getCustomInputGestures(USER_ID, InputGestureData.Filter.KEY)
+ )
+ assertEquals(
+ listOf(customTouchpadGesture),
+ inputGestureManager.getCustomInputGestures(
+ USER_ID,
+ InputGestureData.Filter.TOUCHPAD
+ )
+ )
+
+ inputGestureManager.removeAllCustomInputGestures(USER_ID, InputGestureData.Filter.KEY)
+ assertEquals(
+ listOf(customTouchpadGesture),
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
+ )
+
+ inputGestureManager.removeAllCustomInputGestures(
+ USER_ID,
+ InputGestureData.Filter.TOUCHPAD
+ )
+ assertEquals(
+ listOf<InputGestureData>(),
+ inputGestureManager.getCustomInputGestures(USER_ID, /* filter = */null)
)
}
} \ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 927958eb62cc..6eb00457a1a6 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -95,8 +95,11 @@ class InputManagerServiceTests {
@get:Rule
val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
- .mockStatic(PermissionChecker::class.java).build()!!
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(LocalServices::class.java)
+ .mockStatic(PermissionChecker::class.java)
+ .mockStatic(KeyCharacterMap::class.java)
+ .build()!!
@get:Rule
val setFlagsRule = SetFlagsRule()
@@ -122,6 +125,9 @@ class InputManagerServiceTests {
@Mock
private lateinit var kbdController: InputManagerService.KeyboardBacklightControllerInterface
+ @Mock
+ private lateinit var kcm: KeyCharacterMap
+
private lateinit var service: InputManagerService
private lateinit var localService: InputManagerInternal
private lateinit var context: Context
@@ -171,6 +177,9 @@ class InputManagerServiceTests {
ExtendedMockito.doReturn(packageManagerInternal).`when` {
LocalServices.getService(eq(PackageManagerInternal::class.java))
}
+ ExtendedMockito.doReturn(kcm).`when` {
+ KeyCharacterMap.load(anyInt())
+ }
assertTrue("Local service must be registered", this::localService.isInitialized)
service.setWindowManagerCallbacks(wmCallbacks)
@@ -206,6 +215,7 @@ class InputManagerServiceTests {
verify(native).setTouchpadTapDraggingEnabled(anyBoolean())
verify(native).setShouldNotifyTouchpadHardwareState(anyBoolean())
verify(native).setTouchpadRightClickZoneEnabled(anyBoolean())
+ verify(native).setTouchpadThreeFingerTapShortcutEnabled(anyBoolean())
verify(native).setShowTouches(anyBoolean())
verify(native).setMotionClassifierEnabled(anyBoolean())
verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 0b147d63ed08..09a686ca2c3f 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -16,13 +16,16 @@
package com.android.server.input
+import android.app.role.RoleManager
import android.content.Context
import android.content.ContextWrapper
+import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Resources
-import android.hardware.input.IInputManager
+import android.content.res.XmlResourceParser
import android.hardware.input.AidlKeyGestureEvent
import android.hardware.input.AppLaunchData
+import android.hardware.input.IInputManager
import android.hardware.input.IKeyGestureEventListener
import android.hardware.input.IKeyGestureHandler
import android.hardware.input.InputGestureData
@@ -34,14 +37,15 @@ import android.os.Process
import android.os.SystemClock
import android.os.SystemProperties
import android.os.test.TestLooper
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.view.InputDevice
+import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE
import androidx.test.core.app.ApplicationProvider
+import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.R
import com.android.internal.annotations.Keep
import com.android.internal.util.FrameworkStatsLog
@@ -99,7 +103,9 @@ class KeyGestureControllerTests {
@Rule
val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
.mockStatic(FrameworkStatsLog::class.java)
- .mockStatic(SystemProperties::class.java).build()!!
+ .mockStatic(SystemProperties::class.java)
+ .mockStatic(KeyCharacterMap::class.java)
+ .build()!!
@JvmField
@Rule
@@ -116,6 +122,7 @@ class KeyGestureControllerTests {
private var currentPid = 0
private lateinit var context: Context
+ private lateinit var keyGestureController: KeyGestureController
private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private lateinit var testLooper: TestLooper
private var events = mutableListOf<KeyGestureEvent>()
@@ -123,8 +130,6 @@ class KeyGestureControllerTests {
@Before
fun setup() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- Mockito.`when`(context.resources).thenReturn(resources)
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
setupInputDevices()
setupBehaviors()
testLooper = TestLooper()
@@ -139,11 +144,13 @@ class KeyGestureControllerTests {
}
private fun setupBehaviors() {
- Mockito.`when`(
- resources.getBoolean(
- com.android.internal.R.bool.config_enableScreenshotChord
- )
- ).thenReturn(true)
+ Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1")
+ Mockito.`when`(resources.getBoolean(R.bool.config_enableScreenshotChord)).thenReturn(true)
+ val testBookmarks: XmlResourceParser = context.resources.getXml(
+ com.android.test.input.R.xml.bookmarks
+ )
+ Mockito.`when`(resources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks)
+ Mockito.`when`(context.resources).thenReturn(resources)
Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH))
.thenReturn(true)
Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
@@ -152,6 +159,10 @@ class KeyGestureControllerTests {
}
private fun setupInputDevices() {
+ val correctIm = context.getSystemService(InputManager::class.java)!!
+ val virtualDevice = correctIm.getInputDevice(KeyCharacterMap.VIRTUAL_KEYBOARD)!!
+ val kcm = virtualDevice.keyCharacterMap!!
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
val inputManager = InputManager(context)
Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
@@ -159,9 +170,17 @@ class KeyGestureControllerTests {
val keyboardDevice = InputDevice.Builder().setId(DEVICE_ID).build()
Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+ ExtendedMockito.`when`(KeyCharacterMap.load(Mockito.anyInt())).thenReturn(kcm)
+ }
+
+ private fun setupKeyGestureController() {
+ keyGestureController = KeyGestureController(context, testLooper.looper)
+ Mockito.`when`(iInputManager.getAppLaunchBookmarks())
+ .thenReturn(keyGestureController.appLaunchBookmarks)
+ keyGestureController.systemRunning()
}
- private fun notifyHomeGestureCompleted(keyGestureController: KeyGestureController) {
+ private fun notifyHomeGestureCompleted() {
keyGestureController.notifyKeyGestureCompleted(
DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H),
KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
@@ -171,12 +190,12 @@ class KeyGestureControllerTests {
@Test
fun testKeyGestureEvent_registerUnregisterListener() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
val listener = KeyGestureEventListener()
// Register key gesture event listener
keyGestureController.registerKeyGestureEventListener(listener, 0)
- notifyHomeGestureCompleted(keyGestureController)
+ notifyHomeGestureCompleted()
testLooper.dispatchAll()
assertEquals(
"Listener should get callbacks on key gesture event completed",
@@ -192,7 +211,7 @@ class KeyGestureControllerTests {
// Unregister listener
events.clear()
keyGestureController.unregisterKeyGestureEventListener(listener, 0)
- notifyHomeGestureCompleted(keyGestureController)
+ notifyHomeGestureCompleted()
testLooper.dispatchAll()
assertEquals(
"Listener should not get callback after being unregistered",
@@ -203,7 +222,7 @@ class KeyGestureControllerTests {
@Test
fun testKeyGestureEvent_multipleGestureHandlers() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
// Set up two callbacks.
var callbackCount1 = 0
@@ -267,7 +286,7 @@ class KeyGestureControllerTests {
}
@Keep
- private fun keyGestureEventHandlerTestArguments(): Array<TestData> {
+ private fun systemGesturesTestArguments(): Array<TestData> {
return arrayOf(
TestData(
"META + A -> Launch Assistant",
@@ -278,25 +297,6 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "RECENT_APPS -> Show Overview",
- intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
- intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
- 0,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
- "APP_SWITCH -> App Switch",
- intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
- KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
- intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
- 0,
- intArrayOf(
- KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
- ),
- TestData(
"META + H -> Go Home",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_H),
KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
@@ -465,6 +465,429 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
+ "META + ALT -> Toggle Caps Lock",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + META -> Toggle Caps Lock",
+ intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_META_LEFT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + TAB -> Open Overview",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_TAB),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ intArrayOf(KeyEvent.KEYCODE_TAB),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + TAB -> Toggle Recent Apps Switcher",
+ intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_TAB),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+ intArrayOf(KeyEvent.KEYCODE_TAB),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(
+ KeyGestureEvent.ACTION_GESTURE_START,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ )
+ ),
+ TestData(
+ "CTRL + SPACE -> Switch Language Forward",
+ intArrayOf(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_SPACE),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ intArrayOf(KeyEvent.KEYCODE_SPACE),
+ KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "CTRL + SHIFT + SPACE -> Switch Language Backward",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_SPACE
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ intArrayOf(KeyEvent.KEYCODE_SPACE),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "CTRL + ALT + Z -> Accessibility Shortcut",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_Z
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
+ intArrayOf(KeyEvent.KEYCODE_Z),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + B -> Launch Default Browser",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_B),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_B),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ ),
+ TestData(
+ "META + C -> Launch Default Contacts",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_C),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ ),
+ TestData(
+ "META + E -> Launch Default Email",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_E),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_E),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
+ ),
+ TestData(
+ "META + K -> Launch Default Calendar",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_K),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
+ ),
+ TestData(
+ "META + M -> Launch Default Maps",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_M),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_M),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
+ ),
+ TestData(
+ "META + P -> Launch Default Music",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_P),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
+ ),
+ TestData(
+ "META + S -> Launch Default SMS",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_S),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
+ ),
+ TestData(
+ "META + U -> Launch Default Calculator",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_U),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
+ ),
+ TestData(
+ "META + SHIFT + B -> Launch Default Browser",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_B
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_B),
+ KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
+ ),
+ TestData(
+ "META + SHIFT + C -> Launch Default Contacts",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_C
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_C),
+ KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
+ ),
+ TestData(
+ "META + SHIFT + J -> Launch Target Activity",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_SHIFT_LEFT,
+ KeyEvent.KEYCODE_J
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_J),
+ KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ ),
+ TestData(
+ "META + CTRL + DEL -> Trigger Bug Report",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_DEL
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+ intArrayOf(KeyEvent.KEYCODE_DEL),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "Meta + Alt + 3 -> Toggle Bounce Keys",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_3
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
+ intArrayOf(KeyEvent.KEYCODE_3),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "Meta + Alt + 4 -> Toggle Mouse Keys",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_4
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
+ intArrayOf(KeyEvent.KEYCODE_4),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "Meta + Alt + 5 -> Toggle Sticky Keys",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_5
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
+ intArrayOf(KeyEvent.KEYCODE_5),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "Meta + Alt + 6 -> Toggle Slow Keys",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_6
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
+ intArrayOf(KeyEvent.KEYCODE_6),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + D -> Move a task to next display",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_D
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
+ intArrayOf(KeyEvent.KEYCODE_D),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + [ -> Resizes a task to fit the left half of the screen",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_LEFT_BRACKET
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + ] -> Resizes a task to fit the right half of the screen",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_RIGHT_BRACKET
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + '=' -> Maximizes a task to fit the screen",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_EQUALS
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_EQUALS),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + '-' -> Restores a task size to its previous bounds",
+ intArrayOf(
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_MINUS
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+ intArrayOf(KeyEvent.KEYCODE_MINUS),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + '-' -> Magnifier Zoom Out",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_MINUS
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
+ intArrayOf(KeyEvent.KEYCODE_MINUS),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + '=' -> Magnifier Zoom In",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_EQUALS
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
+ intArrayOf(KeyEvent.KEYCODE_EQUALS),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + M -> Toggle Magnification",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_M
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
+ intArrayOf(KeyEvent.KEYCODE_M),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + S -> Activate Select to Speak",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_S
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
+ intArrayOf(KeyEvent.KEYCODE_S),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ )
+ }
+
+ @Test
+ @Parameters(method = "systemGesturesTestArguments")
+ @EnableFlags(
+ com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS,
+ com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES,
+ com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
+ com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ )
+ fun testKeyGestures(test: TestData) {
+ setupKeyGestureController()
+ testKeyGestureInternal(test)
+ }
+
+ @Test
+ @Parameters(method = "systemGesturesTestArguments")
+ @EnableFlags(
+ com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS,
+ com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES,
+ com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
+ com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
+ )
+ fun testCustomKeyGesturesNotAllowedForSystemGestures(test: TestData) {
+ setupKeyGestureController()
+ // Need to re-init so that bookmarks are correctly blocklisted
+ Mockito.`when`(iInputManager.getAppLaunchBookmarks())
+ .thenReturn(keyGestureController.appLaunchBookmarks)
+ keyGestureController.systemRunning()
+
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(
+ InputGestureData.createKeyTrigger(
+ test.expectedKeys[0],
+ test.expectedModifierState
+ )
+ )
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ assertEquals(
+ test.toString(),
+ InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE,
+ keyGestureController.addCustomInputGesture(0, builder.build().aidlData)
+ )
+ }
+
+ @Keep
+ private fun systemKeysTestArguments(): Array<TestData> {
+ return arrayOf(
+ TestData(
+ "RECENT_APPS -> Show Overview",
+ intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "APP_SWITCH -> App Switch",
+ intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+ intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
+ 0,
+ intArrayOf(
+ KeyGestureEvent.ACTION_GESTURE_START,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ )
+ ),
+ TestData(
"BRIGHTNESS_UP -> Brightness Up",
intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
@@ -553,85 +976,88 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "META + ALT -> Toggle Caps Lock",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ "SYSRQ -> Take screenshot",
+ intArrayOf(KeyEvent.KEYCODE_SYSRQ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ intArrayOf(KeyEvent.KEYCODE_SYSRQ),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "ALT + META -> Toggle Caps Lock",
- intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_META_LEFT),
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ "ESC -> Close All Dialogs",
+ intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+ KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
+ intArrayOf(KeyEvent.KEYCODE_ESCAPE),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "META + TAB -> Open Overview",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_TAB),
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
- intArrayOf(KeyEvent.KEYCODE_TAB),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ "EXPLORER -> Launch Default Browser",
+ intArrayOf(KeyEvent.KEYCODE_EXPLORER),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_EXPLORER),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "ALT + TAB -> Toggle Recent Apps Switcher",
- intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_TAB),
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
- intArrayOf(KeyEvent.KEYCODE_TAB),
- KeyEvent.META_ALT_ON,
- intArrayOf(
- KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
+ "ENVELOPE -> Launch Default Email",
+ intArrayOf(KeyEvent.KEYCODE_ENVELOPE),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_ENVELOPE),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
),
TestData(
- "CTRL + SPACE -> Switch Language Forward",
- intArrayOf(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_SPACE),
- KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
- intArrayOf(KeyEvent.KEYCODE_SPACE),
- KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ "CONTACTS -> Launch Default Contacts",
+ intArrayOf(KeyEvent.KEYCODE_CONTACTS),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_CONTACTS),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
),
TestData(
- "CTRL + SHIFT + SPACE -> Switch Language Backward",
- intArrayOf(
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_SPACE
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
- intArrayOf(KeyEvent.KEYCODE_SPACE),
- KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ "CALENDAR -> Launch Default Calendar",
+ intArrayOf(KeyEvent.KEYCODE_CALENDAR),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_CALENDAR),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
),
TestData(
- "CTRL + ALT + Z -> Accessibility Shortcut",
- intArrayOf(
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_Z
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
- intArrayOf(KeyEvent.KEYCODE_Z),
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ "MUSIC -> Launch Default Music",
+ intArrayOf(KeyEvent.KEYCODE_MUSIC),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_MUSIC),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
),
TestData(
- "SYSRQ -> Take screenshot",
- intArrayOf(KeyEvent.KEYCODE_SYSRQ),
- KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
- intArrayOf(KeyEvent.KEYCODE_SYSRQ),
+ "CALCULATOR -> Launch Default Calculator",
+ intArrayOf(KeyEvent.KEYCODE_CALCULATOR),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_CALCULATOR),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR)
+ ),
+ TestData(
+ "LOCK -> Lock Screen",
+ intArrayOf(KeyEvent.KEYCODE_LOCK),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+ intArrayOf(KeyEvent.KEYCODE_LOCK),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "ESC -> Close All Dialogs",
- intArrayOf(KeyEvent.KEYCODE_ESCAPE),
- KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
- intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+ "FULLSCREEN -> Maximizes a task to fit the screen",
+ intArrayOf(KeyEvent.KEYCODE_FULLSCREEN),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ intArrayOf(KeyEvent.KEYCODE_FULLSCREEN),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
@@ -639,15 +1065,16 @@ class KeyGestureControllerTests {
}
@Test
- @Parameters(method = "keyGestureEventHandlerTestArguments")
- fun testKeyGestures(test: TestData) {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(keyGestureController, test)
+ @Parameters(method = "systemKeysTestArguments")
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES)
+ fun testSystemKeys(test: TestData) {
+ setupKeyGestureController()
+ testKeyGestureInternal(test)
}
@Test
fun testKeycodesFullyConsumed_irrespectiveOfHandlers() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
val testKeys = intArrayOf(
KeyEvent.KEYCODE_RECENT_APPS,
KeyEvent.KEYCODE_APP_SWITCH,
@@ -669,13 +1096,16 @@ class KeyGestureControllerTests {
KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
+ KeyEvent.KEYCODE_DO_NOT_DISTURB,
+ KeyEvent.KEYCODE_LOCK,
+ KeyEvent.KEYCODE_FULLSCREEN
)
val handler = KeyGestureHandler { _, _ -> false }
keyGestureController.registerKeyGestureHandler(handler, 0)
for (key in testKeys) {
- sendKeys(keyGestureController, intArrayOf(key), assertNotSentToApps = true)
+ sendKeys(intArrayOf(key), assertNotSentToApps = true)
}
}
@@ -683,9 +1113,8 @@ class KeyGestureControllerTests {
fun testSearchKeyGestures_defaultSearch() {
Mockito.`when`(resources.getInteger(R.integer.config_searchKeyBehavior))
.thenReturn(SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH)
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
testKeyGestureNotProduced(
- keyGestureController,
"SEARCH -> Default Search",
intArrayOf(KeyEvent.KEYCODE_SEARCH),
)
@@ -695,9 +1124,8 @@ class KeyGestureControllerTests {
fun testSearchKeyGestures_searchActivity() {
Mockito.`when`(resources.getInteger(R.integer.config_searchKeyBehavior))
.thenReturn(SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY)
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
testKeyGestureInternal(
- keyGestureController,
TestData(
"SEARCH -> Launch Search Activity",
intArrayOf(KeyEvent.KEYCODE_SEARCH),
@@ -713,9 +1141,8 @@ class KeyGestureControllerTests {
fun testSettingKeyGestures_doNothing() {
Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior))
.thenReturn(SETTINGS_KEY_BEHAVIOR_NOTHING)
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
testKeyGestureNotProduced(
- keyGestureController,
"SETTINGS -> Do Nothing",
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
)
@@ -725,9 +1152,8 @@ class KeyGestureControllerTests {
fun testSettingKeyGestures_settingsActivity() {
Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior))
.thenReturn(SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY)
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
testKeyGestureInternal(
- keyGestureController,
TestData(
"SETTINGS -> Launch Settings Activity",
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
@@ -743,9 +1169,8 @@ class KeyGestureControllerTests {
fun testSettingKeyGestures_notificationPanel() {
Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior))
.thenReturn(SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL)
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
testKeyGestureInternal(
- keyGestureController,
TestData(
"SETTINGS -> Toggle Notification Panel",
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
@@ -758,221 +1183,12 @@ class KeyGestureControllerTests {
}
@Test
- @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
- fun testTriggerBugReport() {
- Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1")
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "META + CTRL + DEL -> Trigger Bug Report",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DEL
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
- intArrayOf(KeyEvent.KEYCODE_DEL),
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
- @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
- fun testTriggerBugReport_flagDisabled() {
- Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1")
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "META + CTRL + DEL -> Not Trigger Bug Report (Fallback to BACK)",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_DEL
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
- intArrayOf(KeyEvent.KEYCODE_DEL),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
- @EnableFlags(
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS
- )
- fun testKeyboardAccessibilityToggleShortcutPress() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "Meta + Alt + 3 -> Toggle Bounce Keys",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_3
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
- intArrayOf(KeyEvent.KEYCODE_3),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "Meta + Alt + 4 -> Toggle Mouse Keys",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_4
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
- intArrayOf(KeyEvent.KEYCODE_4),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "Meta + Alt + 5 -> Toggle Sticky Keys",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_5
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
- intArrayOf(KeyEvent.KEYCODE_5),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "Meta + Alt + 6 -> Toggle Slow Keys",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_6
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
- intArrayOf(KeyEvent.KEYCODE_6),
- KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)))
- }
-
- @Test
- @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
- fun testMoveToNextDisplay() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "META + CTRL + D -> Move a task to next display",
- intArrayOf(
- KeyEvent.KEYCODE_META_LEFT,
- KeyEvent.KEYCODE_CTRL_LEFT,
- KeyEvent.KEYCODE_D
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
- intArrayOf(KeyEvent.KEYCODE_D),
- KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
- @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
- fun testSnapLeftFreeformTask() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "ALT + [ -> Resizes a task to fit the left half of the screen",
- intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_LEFT_BRACKET
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
- intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET),
- KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
- @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
- fun testSnapRightFreeformTask() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "ALT + ] -> Resizes a task to fit the right half of the screen",
- intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_RIGHT_BRACKET
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
- intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET),
- KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
- @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
- fun testMaximizeFreeformTask() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "ALT + '=' -> Maximizes a task to fit the screen",
- intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_EQUALS
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
- intArrayOf(KeyEvent.KEYCODE_EQUALS),
- KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
- @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
- fun testRestoreFreeformTask() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(
- keyGestureController,
- TestData(
- "ALT + '-' -> Restores a task size to its previous bounds",
- intArrayOf(
- KeyEvent.KEYCODE_ALT_LEFT,
- KeyEvent.KEYCODE_MINUS
- ),
- KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
- intArrayOf(KeyEvent.KEYCODE_MINUS),
- KeyEvent.META_ALT_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- )
- )
- }
-
- @Test
fun testCapsLockPressNotified() {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
val listener = KeyGestureEventListener()
keyGestureController.registerKeyGestureEventListener(listener, 0)
- sendKeys(keyGestureController, intArrayOf(KeyEvent.KEYCODE_CAPS_LOCK))
+ sendKeys(intArrayOf(KeyEvent.KEYCODE_CAPS_LOCK))
testLooper.dispatchAll()
assertEquals(
"Listener should get callbacks on key gesture event completed",
@@ -987,7 +1203,7 @@ class KeyGestureControllerTests {
}
@Keep
- private fun keyGestureEventHandlerTestArguments_forKeyCombinations(): Array<TestData> {
+ private fun systemGesturesTestArguments_forKeyCombinations(): Array<TestData> {
return arrayOf(
TestData(
"VOLUME_DOWN + POWER -> Screenshot Chord",
@@ -1048,14 +1264,14 @@ class KeyGestureControllerTests {
}
@Test
- @Parameters(method = "keyGestureEventHandlerTestArguments_forKeyCombinations")
+ @Parameters(method = "systemGesturesTestArguments_forKeyCombinations")
@EnableFlags(
com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_PRESS_GESTURES
)
fun testKeyCombinationGestures(test: TestData) {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
- testKeyGestureInternal(keyGestureController, test)
+ setupKeyGestureController()
+ testKeyGestureInternal(test)
}
@Keep
@@ -1096,7 +1312,7 @@ class KeyGestureControllerTests {
@Test
@Parameters(method = "customInputGesturesTestArguments")
fun testCustomKeyGestures(test: TestData) {
- val keyGestureController = KeyGestureController(context, testLooper.looper)
+ setupKeyGestureController()
val builder = InputGestureData.Builder()
.setKeyGestureType(test.expectedKeyGestureType)
.setTrigger(
@@ -1104,34 +1320,112 @@ class KeyGestureControllerTests {
test.expectedKeys[0],
test.expectedModifierState
)
- );
+ )
if (test.expectedAppLaunchData != null) {
builder.setAppLaunchData(test.expectedAppLaunchData)
}
- val inputGestureData = builder.build();
+ val inputGestureData = builder.build()
keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData)
- testKeyGestureInternal(keyGestureController, test)
+ testKeyGestureInternal(test)
+ }
+
+ class TouchpadTestData(
+ val name: String,
+ val touchpadGestureType: Int,
+ val expectedKeyGestureType: Int,
+ val expectedAction: Int,
+ val expectedAppLaunchData: AppLaunchData? = null,
+ ) {
+ override fun toString(): String = name
}
- private fun testKeyGestureInternal(keyGestureController: KeyGestureController, test: TestData) {
- var handleEvents = mutableListOf<KeyGestureEvent>()
+ @Keep
+ private fun customTouchpadGesturesTestArguments(): Array<TouchpadTestData> {
+ return arrayOf(
+ TouchpadTestData(
+ "3 Finger Tap -> Go Home",
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ ),
+ TouchpadTestData(
+ "3 Finger Tap -> Launch app",
+ InputGestureData.TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+ AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+ ),
+ )
+ }
+
+ @Test
+ @Parameters(method = "customTouchpadGesturesTestArguments")
+ fun testCustomTouchpadGesture(test: TouchpadTestData) {
+ setupKeyGestureController()
+ val builder = InputGestureData.Builder()
+ .setKeyGestureType(test.expectedKeyGestureType)
+ .setTrigger(InputGestureData.createTouchpadTrigger(test.touchpadGestureType))
+ if (test.expectedAppLaunchData != null) {
+ builder.setAppLaunchData(test.expectedAppLaunchData)
+ }
+ val inputGestureData = builder.build()
+
+ keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData)
+
+ val handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
- handleEvents.add(KeyGestureEvent(event))
+ handledEvents.add(KeyGestureEvent(event))
true
}
keyGestureController.registerKeyGestureHandler(handler, 0)
- handleEvents.clear()
+ handledEvents.clear()
- sendKeys(keyGestureController, test.keys)
+ keyGestureController.handleTouchpadGesture(test.touchpadGestureType)
+
+ assertEquals(
+ "Test: $test doesn't produce correct number of key gesture events",
+ 1,
+ handledEvents.size
+ )
+ val event = handledEvents[0]
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture type",
+ test.expectedKeyGestureType,
+ event.keyGestureType
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture action",
+ test.expectedAction,
+ event.action
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct app launch data",
+ test.expectedAppLaunchData,
+ event.appLaunchData
+ )
+
+ keyGestureController.unregisterKeyGestureHandler(handler, 0)
+ }
+
+ private fun testKeyGestureInternal(test: TestData) {
+ val handledEvents = mutableListOf<KeyGestureEvent>()
+ val handler = KeyGestureHandler { event, _ ->
+ handledEvents.add(KeyGestureEvent(event))
+ true
+ }
+ keyGestureController.registerKeyGestureHandler(handler, 0)
+ handledEvents.clear()
+
+ sendKeys(test.keys)
assertEquals(
"Test: $test doesn't produce correct number of key gesture events",
test.expectedActions.size,
- handleEvents.size
+ handledEvents.size
)
- for (i in handleEvents.indices) {
- val event = handleEvents[i]
+ for (i in handledEvents.indices) {
+ val event = handledEvents[i]
assertArrayEquals(
"Test: $test doesn't produce correct key gesture keycodes",
test.expectedKeys,
@@ -1162,28 +1456,20 @@ class KeyGestureControllerTests {
keyGestureController.unregisterKeyGestureHandler(handler, 0)
}
- private fun testKeyGestureNotProduced(
- keyGestureController: KeyGestureController,
- testName: String,
- testKeys: IntArray
- ) {
- var handleEvents = mutableListOf<KeyGestureEvent>()
+ private fun testKeyGestureNotProduced(testName: String, testKeys: IntArray) {
+ var handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
- handleEvents.add(KeyGestureEvent(event))
+ handledEvents.add(KeyGestureEvent(event))
true
}
keyGestureController.registerKeyGestureHandler(handler, 0)
- handleEvents.clear()
+ handledEvents.clear()
- sendKeys(keyGestureController, testKeys)
- assertEquals("Test: $testName should not produce Key gesture", 0, handleEvents.size)
+ sendKeys(testKeys)
+ assertEquals("Test: $testName should not produce Key gesture", 0, handledEvents.size)
}
- private fun sendKeys(
- keyGestureController: KeyGestureController,
- testKeys: IntArray,
- assertNotSentToApps: Boolean = false
- ) {
+ private fun sendKeys(testKeys: IntArray, assertNotSentToApps: Boolean = false) {
var metaState = 0
val now = SystemClock.uptimeMillis()
for (key in testKeys) {
@@ -1192,7 +1478,7 @@ class KeyGestureControllerTests {
DEVICE_ID, 0 /*scancode*/, 0 /*flags*/,
InputDevice.SOURCE_KEYBOARD
)
- interceptKey(keyGestureController, downEvent, assertNotSentToApps)
+ interceptKey(downEvent, assertNotSentToApps)
metaState = metaState or MODIFIER.getOrDefault(key, 0)
downEvent.recycle()
@@ -1205,7 +1491,7 @@ class KeyGestureControllerTests {
DEVICE_ID, 0 /*scancode*/, 0 /*flags*/,
InputDevice.SOURCE_KEYBOARD
)
- interceptKey(keyGestureController, upEvent, assertNotSentToApps)
+ interceptKey(upEvent, assertNotSentToApps)
metaState = metaState and MODIFIER.getOrDefault(key, 0).inv()
upEvent.recycle()
@@ -1213,11 +1499,7 @@ class KeyGestureControllerTests {
}
}
- private fun interceptKey(
- keyGestureController: KeyGestureController,
- event: KeyEvent,
- assertNotSentToApps: Boolean
- ) {
+ private fun interceptKey(event: KeyEvent, assertNotSentToApps: Boolean) {
keyGestureController.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE)
testLooper.dispatchAll()
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
index 58fb4e1ed103..938e2f8a3611 100644
--- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -19,6 +19,7 @@ package com.android.server.input
import android.animation.ValueAnimator
import android.content.Context
import android.content.ContextWrapper
+import android.content.res.Resources
import android.graphics.Color
import android.hardware.input.IKeyboardBacklightListener
import android.hardware.input.IKeyboardBacklightState
@@ -28,11 +29,12 @@ import android.os.UEventObserver
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
+import android.util.TypedValue
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
+import com.android.internal.R
import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL
import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS
-import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
import com.android.test.input.MockInputManagerRule
import java.io.FileNotFoundException
import java.io.FileOutputStream
@@ -49,6 +51,7 @@ import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
import org.mockito.Mockito.spy
@@ -94,6 +97,7 @@ class KeyboardBacklightControllerTests {
const val LIGHT_ID = 2
const val SECOND_LIGHT_ID = 3
const val MAX_BRIGHTNESS = 255
+ const val USER_INACTIVITY_THRESHOLD_MILLIS = 30000
}
@get:Rule
@@ -105,6 +109,8 @@ class KeyboardBacklightControllerTests {
private lateinit var native: NativeInputManagerService
@Mock
private lateinit var uEventManager: UEventManager
+ @Mock
+ private lateinit var resources: Resources
private lateinit var keyboardBacklightController: KeyboardBacklightController
private lateinit var context: Context
private lateinit var dataStore: PersistentDataStore
@@ -117,6 +123,7 @@ class KeyboardBacklightControllerTests {
@Before
fun setup() {
context = spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ `when`(context.resources).thenReturn(resources)
dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
override fun openRead(): InputStream? {
throw FileNotFoundException()
@@ -129,6 +136,7 @@ class KeyboardBacklightControllerTests {
override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
})
testLooper = TestLooper()
+ setupConfig()
keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
testLooper.looper, FakeAnimatorFactory(), uEventManager)
val inputManager = InputManager(context)
@@ -147,7 +155,31 @@ class KeyboardBacklightControllerTests {
sysfsNodeChanges++
}
}
-
+ private fun setupConfig() {
+ val brightnessValues = intArrayOf(100, 200, 0)
+ val decreaseThresholds = intArrayOf(-1, 900, 1900)
+ val increaseThresholds = intArrayOf(1000, 2000, -1)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues))
+ .thenReturn(brightnessValues)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold))
+ .thenReturn(decreaseThresholds)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold))
+ .thenReturn(increaseThresholds)
+ `when`(resources.getInteger(R.integer.config_keyboardBacklightTimeoutMs))
+ .thenReturn(USER_INACTIVITY_THRESHOLD_MILLIS)
+ `when`(
+ resources.getValue(
+ eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
+ any(TypedValue::class.java),
+ anyBoolean()
+ )
+ ).then {
+ val args = it.arguments
+ val outValue = args[1] as TypedValue
+ outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
+ Unit
+ }
+ }
@Test
fun testKeyboardBacklightIncrementDecrement() {
KeyboardBacklightFlags(
@@ -365,7 +397,7 @@ class KeyboardBacklightControllerTests {
lightColorMap[LIGHT_ID]
)
- testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000)
+ testLooper.moveTimeForward((USER_INACTIVITY_THRESHOLD_MILLIS + 1000).toLong())
testLooper.dispatchNext()
assertEquals(
"Keyboard backlight level should be turned off after inactivity",
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 096555eb3056..91483eb41387 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -35,7 +35,13 @@ android_test {
"services.core",
"services.net",
"truth",
- ],
+ ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+ "true": [
+ "service-crashrecovery.impl",
+ "framework-crashrecovery.impl",
+ ],
+ default: [],
+ }),
libs: ["android.test.runner.stubs.system"],
jni_libs: [
// mockito-target-extended dependencies
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 8d143b69d124..05a0f8f34337 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -224,39 +224,39 @@ public class CrashRecoveryTest {
PackageWatchdog watchdog = createWatchdog();
RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(1);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
- verify(rescuePartyObserver).executeBootLoopMitigation(1);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(2);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(3);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(4);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(3);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(4);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(4);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(5);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(4);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(5);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(5);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(6);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(5);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(6);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(7);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(6);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(7);
}
@Test
@@ -265,14 +265,14 @@ public class CrashRecoveryTest {
RollbackPackageHealthObserver rollbackObserver =
setUpRollbackPackageHealthObserver(watchdog);
- verify(rollbackObserver, never()).executeBootLoopMitigation(1);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
- verify(rollbackObserver).executeBootLoopMitigation(1);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rollbackObserver).onExecuteBootLoopMitigation(1);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
// Update the list of available rollbacks after executing bootloop mitigation once
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH,
@@ -280,15 +280,15 @@ public class CrashRecoveryTest {
watchdog.noteBoot();
- verify(rollbackObserver).executeBootLoopMitigation(2);
- verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+ verify(rollbackObserver).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
// Update the list of available rollbacks after executing bootloop mitigation once
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL));
watchdog.noteBoot();
- verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
}
@Test
@@ -299,61 +299,61 @@ public class CrashRecoveryTest {
RollbackPackageHealthObserver rollbackObserver =
setUpRollbackPackageHealthObserver(watchdog);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(1);
- verify(rollbackObserver, never()).executeBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(1);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
- verify(rescuePartyObserver).executeBootLoopMitigation(1);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
- verify(rollbackObserver, never()).executeBootLoopMitigation(1);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(2);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
- verify(rollbackObserver).executeBootLoopMitigation(1);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
+ verify(rollbackObserver).onExecuteBootLoopMitigation(1);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
// Update the list of available rollbacks after executing bootloop mitigation once
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH,
ROLLBACK_INFO_MANUAL));
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(3);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(4);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(3);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(4);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(4);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(5);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(4);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(5);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(5);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(6);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(5);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
watchdog.noteBoot();
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(6);
- verify(rollbackObserver).executeBootLoopMitigation(2);
- verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(6);
+ verify(rollbackObserver).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
// Update the list of available rollbacks after executing bootloop mitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL));
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(6);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(7);
- verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(6);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(7);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
Mockito.reset(rescuePartyObserver);
@@ -361,8 +361,8 @@ public class CrashRecoveryTest {
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
- verify(rescuePartyObserver).executeBootLoopMitigation(1);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
}
@Test
@@ -373,37 +373,37 @@ public class CrashRecoveryTest {
RollbackPackageHealthObserver rollbackObserver =
setUpRollbackPackageHealthObserver(watchdog);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(1);
- verify(rollbackObserver, never()).executeBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(1);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
- verify(rescuePartyObserver).executeBootLoopMitigation(1);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
- verify(rollbackObserver, never()).executeBootLoopMitigation(1);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(1);
watchdog.noteBoot();
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
- verify(rollbackObserver).executeBootLoopMitigation(1);
- verify(rollbackObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver).onExecuteBootLoopMitigation(1);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(2);
// Update the list of available rollbacks after executing bootloop mitigation once
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH,
ROLLBACK_INFO_MANUAL));
watchdog.noteBoot();
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
- verify(rollbackObserver).executeBootLoopMitigation(2);
- verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver).onExecuteBootLoopMitigation(2);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
// Update the list of available rollbacks after executing bootloop mitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL));
watchdog.noteBoot();
- verify(rescuePartyObserver).executeBootLoopMitigation(2);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(3);
- verify(rollbackObserver, never()).executeBootLoopMitigation(3);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(2);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(3);
+ verify(rollbackObserver, never()).onExecuteBootLoopMitigation(3);
moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1);
Mockito.reset(rescuePartyObserver);
@@ -411,8 +411,8 @@ public class CrashRecoveryTest {
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
watchdog.noteBoot();
}
- verify(rescuePartyObserver).executeBootLoopMitigation(1);
- verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
+ verify(rescuePartyObserver).onExecuteBootLoopMitigation(1);
+ verify(rescuePartyObserver, never()).onExecuteBootLoopMitigation(2);
}
@Test
@@ -435,46 +435,46 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: SCOPED_DEVICE_CONFIG_RESET
- verify(rescuePartyObserver).execute(versionedPackageA,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rollbackObserver, never()).execute(versionedPackageA,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: ALL_DEVICE_CONFIG_RESET
- verify(rescuePartyObserver).execute(versionedPackageA,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
- verify(rollbackObserver, never()).execute(versionedPackageA,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: WARM_REBOOT
- verify(rescuePartyObserver).execute(versionedPackageA,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
- verify(rollbackObserver, never()).execute(versionedPackageA,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: Low impact rollback
- verify(rollbackObserver).execute(versionedPackageA,
+ verify(rollbackObserver).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
// update available rollbacks to mock rollbacks being applied after the call to
- // rollbackObserver.execute
+ // rollbackObserver.onExecuteHealthCheckMitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(
List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
@@ -482,9 +482,9 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
- verify(rollbackObserver, never()).execute(versionedPackageA,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
}
@@ -510,24 +510,24 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: WARM_REBOOT
- verify(rescuePartyObserver).execute(versionedPackageA,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rollbackObserver, never()).execute(versionedPackageA,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: Low impact rollback
- verify(rollbackObserver).execute(versionedPackageA,
+ verify(rollbackObserver).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
// update available rollbacks to mock rollbacks being applied after the call to
- // rollbackObserver.execute
+ // rollbackObserver.onExecuteHealthCheckMitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(
List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
@@ -535,9 +535,9 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
- verify(rescuePartyObserver, never()).execute(versionedPackageA,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rollbackObserver, never()).execute(versionedPackageA,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageA,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
}
@@ -567,48 +567,48 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: SCOPED_DEVICE_CONFIG_RESET
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: ALL_DEVICE_CONFIG_RESET
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: WARM_REBOOT
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: Low impact rollback
- verify(rollbackObserver).execute(versionedPackageUi,
+ verify(rollbackObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
// update available rollbacks to mock rollbacks being applied after the call to
- // rollbackObserver.execute
+ // rollbackObserver.onExecuteHealthCheckMitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(
List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
@@ -616,44 +616,44 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: RESET_SETTINGS_UNTRUSTED_DEFAULTS
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: RESET_SETTINGS_UNTRUSTED_CHANGES
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: RESET_SETTINGS_TRUSTED_DEFAULTS
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 8);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
}
@@ -685,26 +685,26 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: WARM_REBOOT
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
raiseFatalFailureAndDispatch(watchdog,
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: Low impact rollback
- verify(rollbackObserver).execute(versionedPackageUi,
+ verify(rollbackObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
// update available rollbacks to mock rollbacks being applied after the call to
- // rollbackObserver.execute
+ // rollbackObserver.onExecuteHealthCheckMitigation
when(mRollbackManager.getAvailableRollbacks()).thenReturn(
List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
@@ -712,17 +712,17 @@ public class CrashRecoveryTest {
Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
// Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
- verify(rescuePartyObserver).execute(versionedPackageUi,
+ verify(rescuePartyObserver).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
- verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+ verify(rescuePartyObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
- verify(rollbackObserver, never()).execute(versionedPackageUi,
+ verify(rollbackObserver, never()).onExecuteHealthCheckMitigation(versionedPackageUi,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
}
RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) {
RollbackPackageHealthObserver rollbackObserver =
- spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager));
+ spy(new RollbackPackageHealthObserver(mSpyContext));
when(mSpyContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
@@ -785,7 +785,7 @@ public class CrashRecoveryTest {
Handler handler = new Handler(mTestLooper.getLooper());
PackageWatchdog watchdog =
new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
- mConnectivityModuleConnector, mTestClock);
+ mTestClock);
mockCrashRecoveryProperties(watchdog);
// Verify controller is not automatically started
diff --git a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
index 2fbfeba47b13..cd2ab86d6444 100644
--- a/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/ExplicitHealthCheckServiceTest.java
@@ -35,6 +35,8 @@ public class ExplicitHealthCheckServiceTest {
private ExplicitHealthCheckService mExplicitHealthCheckService;
private static final String PACKAGE_NAME = "com.test.package";
+ private static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
+ "android.service.watchdog.extra.health_check_passed_package";
@Before
public void setup() throws Exception {
@@ -50,7 +52,7 @@ public class ExplicitHealthCheckServiceTest {
IBinder binder = mExplicitHealthCheckService.onBind(new Intent());
CountDownLatch countDownLatch = new CountDownLatch(1);
RemoteCallback callback = new RemoteCallback(result -> {
- assertThat(result.get(ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
+ assertThat(result.get(EXTRA_HEALTH_CHECK_PASSED_PACKAGE))
.isEqualTo(PACKAGE_NAME);
countDownLatch.countDown();
});
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 0364781ab064..a540a8d567a1 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1754,7 +1754,7 @@ public class PackageWatchdogTest {
Handler handler = new Handler(mTestLooper.getLooper());
PackageWatchdog watchdog =
new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
- mConnectivityModuleConnector, mTestClock);
+ mTestClock);
mockCrashRecoveryProperties(watchdog);
// Verify controller is not automatically started
@@ -1869,8 +1869,8 @@ public class PackageWatchdogTest {
return mImpact;
}
- public boolean execute(VersionedPackage versionedPackage, int failureReason,
- int mitigationCount) {
+ public boolean onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
+ int failureReason, int mitigationCount) {
mMitigatedPackages.add(versionedPackage.getPackageName());
mMitigationCounts.add(mitigationCount);
mLastFailureReason = failureReason;
@@ -1893,7 +1893,7 @@ public class PackageWatchdogTest {
return mImpact;
}
- public boolean executeBootLoopMitigation(int level) {
+ public boolean onExecuteBootLoopMitigation(int level) {
mMitigatedBootLoop = true;
mBootMitigationCounts.add(level);
return true;
diff --git a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
index e2099e652c49..635e5de935c7 100644
--- a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
+++ b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
@@ -18,19 +18,27 @@ package com.android.server.usblib;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
+import android.hardware.usb.flags.Flags;
import android.os.Binder;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -43,13 +51,36 @@ public class UsbManagerTestLib {
private UsbManager mUsbManagerSys;
private UsbManager mUsbManagerMock;
- @Mock private android.hardware.usb.IUsbManager mMockUsbService;
+ @Mock
+ private android.hardware.usb.IUsbManager mMockUsbService;
+ private TestParcelFileDescriptor mTestParcelFileDescriptor = new TestParcelFileDescriptor(
+ new ParcelFileDescriptor(new FileDescriptor()));
+ @Mock
+ private UsbAccessory mMockUsbAccessory;
/**
* Counter for tracking UsbOperation operations.
*/
private static final AtomicInteger sUsbOperationCount = new AtomicInteger();
+ private class TestParcelFileDescriptor extends ParcelFileDescriptor {
+
+ private final AtomicInteger mCloseCount = new AtomicInteger();
+
+ TestParcelFileDescriptor(ParcelFileDescriptor wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public void close() {
+ int unused = mCloseCount.incrementAndGet();
+ }
+
+ public void clearCloseCount() {
+ mCloseCount.set(0);
+ }
+ }
+
public UsbManagerTestLib(Context context) {
MockitoAnnotations.initMocks(this);
mContext = context;
@@ -74,6 +105,34 @@ public class UsbManagerTestLib {
mUsbManagerSys.setCurrentFunctions(functions);
}
+ private InputStream openAccessoryInputStream(UsbAccessory accessory) {
+ try {
+ when(mMockUsbService.openAccessory(accessory)).thenReturn(mTestParcelFileDescriptor);
+ } catch (RemoteException remEx) {
+ Log.w(TAG, "RemoteException");
+ }
+
+ if (Flags.enableAccessoryStreamApi()) {
+ return mUsbManagerMock.openAccessoryInputStream(accessory);
+ }
+
+ throw new UnsupportedOperationException("Stream APIs not available");
+ }
+
+ private OutputStream openAccessoryOutputStream(UsbAccessory accessory) {
+ try {
+ when(mMockUsbService.openAccessory(accessory)).thenReturn(mTestParcelFileDescriptor);
+ } catch (RemoteException remEx) {
+ Log.w(TAG, "RemoteException");
+ }
+
+ if (Flags.enableAccessoryStreamApi()) {
+ return mUsbManagerMock.openAccessoryOutputStream(accessory);
+ }
+
+ throw new UnsupportedOperationException("Stream APIs not available");
+ }
+
private void testSetGetCurrentFunctions_Matched(long functions) {
setCurrentFunctions(functions);
assertEquals("CurrentFunctions mismatched: ", functions, getCurrentFunctions());
@@ -94,7 +153,7 @@ public class UsbManagerTestLib {
try {
setCurrentFunctions(functions);
- verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId);
+ verify(mMockUsbService).setCurrentFunctions(eq(functions), eq(operationId));
} catch (RemoteException remEx) {
Log.w(TAG, "RemoteException");
}
@@ -118,7 +177,7 @@ public class UsbManagerTestLib {
int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
setCurrentFunctions(functions);
- verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId);
+ verify(mMockUsbService).setCurrentFunctions(eq(functions), eq(operationId));
}
public void testGetCurrentFunctions_shouldMatched() {
@@ -138,4 +197,47 @@ public class UsbManagerTestLib {
testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS);
testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NCM);
}
+
+ public void testParcelFileDescriptorClosedWhenAllOpenStreamsAreClosed() {
+ mTestParcelFileDescriptor.clearCloseCount();
+ try {
+ try (InputStream ignored = openAccessoryInputStream(mMockUsbAccessory)) {
+ //noinspection EmptyTryBlock
+ try (OutputStream ignored2 = openAccessoryOutputStream(mMockUsbAccessory)) {
+ // do nothing
+ }
+ }
+
+ // ParcelFileDescriptor is closed only once.
+ assertEquals(mTestParcelFileDescriptor.mCloseCount.get(), 1);
+ mTestParcelFileDescriptor.clearCloseCount();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+
+ public void testOnlyOneOpenInputStreamAllowed() {
+ try {
+ //noinspection EmptyTryBlock
+ try (InputStream ignored = openAccessoryInputStream(mMockUsbAccessory)) {
+ assertThrows(IllegalStateException.class,
+ () -> openAccessoryInputStream(mMockUsbAccessory));
+ }
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+
+ public void testOnlyOneOpenOutputStreamAllowed() {
+ try {
+ //noinspection EmptyTryBlock
+ try (OutputStream ignored = openAccessoryOutputStream(mMockUsbAccessory)) {
+ assertThrows(IllegalStateException.class,
+ () -> openAccessoryOutputStream(mMockUsbAccessory));
+ }
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+
}
diff --git a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java
index 8b21763b4a24..40fd0b431451 100644
--- a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java
+++ b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java
@@ -18,17 +18,21 @@ package com.android.server.usbtest;
import android.content.Context;
import android.hardware.usb.UsbManager;
+import android.hardware.usb.flags.Flags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Ignore;
+import com.android.server.usblib.UsbManagerTestLib;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import com.android.server.usblib.UsbManagerTestLib;
-
/**
* Unit tests for {@link android.hardware.usb.UsbManager}.
* Note: MUST claimed MANAGE_USB permission in Manifest
@@ -41,6 +45,9 @@ public class UsbManagerApiTest {
private final UsbManagerTestLib mUsbManagerTestLib =
new UsbManagerTestLib(mContext = InstrumentationRegistry.getContext());
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
/**
* Verify NO SecurityException
* Go through System Server
@@ -92,4 +99,23 @@ public class UsbManagerApiTest {
public void testUsbApi_SetCurrentFunctions_shouldMatched() {
mUsbManagerTestLib.testSetCurrentFunctions_shouldMatched();
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API)
+ public void testUsbApi_closesParcelFileDescriptorAfterAllStreamsClosed() {
+ mUsbManagerTestLib.testParcelFileDescriptorClosedWhenAllOpenStreamsAreClosed();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API)
+ public void testUsbApi_callingOpenAccessoryInputStreamTwiceThrowsException() {
+ mUsbManagerTestLib.testOnlyOneOpenInputStreamAllowed();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API)
+ public void testUsbApi_callingOpenAccessoryOutputStreamTwiceThrowsException() {
+ mUsbManagerTestLib.testOnlyOneOpenOutputStreamAllowed();
+ }
+
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index 07b733830bd3..0da4521fca71 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -143,6 +143,38 @@ public class VibratorManagerServicePermissionTest {
}
@Test
+ public void testStartVendorVibrationSessionWithoutVibratePermissionFails() throws Exception {
+ getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.VIBRATE_VENDOR_EFFECTS,
+ Manifest.permission.START_VIBRATION_SESSIONS);
+ expectSecurityException("VIBRATE");
+ mVibratorService.startVendorVibrationSession(Process.myUid(), DEVICE_ID, PACKAGE_NAME,
+ new int[] { 1 }, ATTRS, "testVibrate", null);
+ }
+
+ @Test
+ public void testStartVendorVibrationSessionWithoutVibrateVendorEffectsPermissionFails()
+ throws Exception {
+ getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.VIBRATE,
+ Manifest.permission.START_VIBRATION_SESSIONS);
+ expectSecurityException("VIBRATE");
+ mVibratorService.startVendorVibrationSession(Process.myUid(), DEVICE_ID, PACKAGE_NAME,
+ new int[] { 1 }, ATTRS, "testVibrate", null);
+ }
+
+ @Test
+ public void testStartVendorVibrationSessionWithoutStartSessionPermissionFails()
+ throws Exception {
+ getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.VIBRATE,
+ Manifest.permission.VIBRATE_VENDOR_EFFECTS);
+ expectSecurityException("VIBRATE");
+ mVibratorService.startVendorVibrationSession(Process.myUid(), DEVICE_ID, PACKAGE_NAME,
+ new int[] { 1 }, ATTRS, "testVibrate", null);
+ }
+
+ @Test
public void testCancelVibrateFails() throws RemoteException {
expectSecurityException("VIBRATE");
mVibratorService.cancelVibrate(/* usageFilter= */ -1, new Binder());
diff --git a/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt b/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt
index b27b8269575b..ded467993eef 100644
--- a/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt
+++ b/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt
@@ -17,7 +17,13 @@
package android.animation
import android.animation.AnimatorTestRuleToolkit.Companion.TAG
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
import android.util.Log
+import android.view.View
+import androidx.core.graphics.drawable.toBitmap
+import androidx.test.core.app.ActivityScenario
+import java.util.concurrent.TimeUnit
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,13 +42,45 @@ import platform.test.motion.golden.FrameId
import platform.test.motion.golden.TimeSeries
import platform.test.motion.golden.TimeSeriesCaptureScope
import platform.test.motion.golden.TimestampFrameId
+import platform.test.screenshot.captureToBitmapAsync
-class AnimatorTestRuleToolkit(val animatorTestRule: AnimatorTestRule, val testScope: TestScope) {
+class AnimatorTestRuleToolkit(
+ internal val animatorTestRule: AnimatorTestRule,
+ internal val testScope: TestScope,
+ internal val currentActivityScenario: () -> ActivityScenario<*>,
+) {
internal companion object {
const val TAG = "AnimatorRuleToolkit"
}
}
+/** Capture utility to extract a [Bitmap] from a [drawable]. */
+fun captureDrawable(drawable: Drawable): Bitmap {
+ val width = drawable.bounds.right - drawable.bounds.left
+ val height = drawable.bounds.bottom - drawable.bounds.top
+
+ // If either dimension is 0 this will fail, so we set it to 1 pixel instead.
+ return drawable.toBitmap(
+ width =
+ if (width > 0) {
+ width
+ } else {
+ 1
+ },
+ height =
+ if (height > 0) {
+ height
+ } else {
+ 1
+ },
+ )
+}
+
+/** Capture utility to extract a [Bitmap] from a [view]. */
+fun captureView(view: View): Bitmap {
+ return view.captureToBitmapAsync().get(10, TimeUnit.SECONDS)
+}
+
/**
* Controls the timing of the motion recording.
*
@@ -71,24 +109,39 @@ data class AnimatorRuleRecordingSpec<T>(
/** Time interval between frame captures, in milliseconds. */
val frameDurationMs: Long = 16L,
- /** Produces the time-series, invoked on each animation frame. */
+ /** Whether a sequence of screenshots should also be recorded. */
+ val visualCapture: ((captureRoot: T) -> Bitmap)? = null,
+
+ /** Produces the time-series, invoked on each animation frame. */
val timeSeriesCapture: TimeSeriesCaptureScope<T>.() -> Unit,
)
/** Records the time-series of the features specified in [recordingSpec]. */
fun <T> MotionTestRule<AnimatorTestRuleToolkit>.recordMotion(
- recordingSpec: AnimatorRuleRecordingSpec<T>,
+ recordingSpec: AnimatorRuleRecordingSpec<T>
): RecordedMotion {
with(toolkit.animatorTestRule) {
+ val activityScenario = toolkit.currentActivityScenario()
val frameIdCollector = mutableListOf<FrameId>()
val propertyCollector = mutableMapOf<String, MutableList<DataPoint<*>>>()
+ val screenshotCollector =
+ if (recordingSpec.visualCapture != null) {
+ mutableListOf<Bitmap>()
+ } else {
+ null
+ }
fun recordFrame(frameId: FrameId) {
Log.i(TAG, "recordFrame($frameId)")
frameIdCollector.add(frameId)
- recordingSpec.timeSeriesCapture.invoke(
- TimeSeriesCaptureScope(recordingSpec.captureRoot, propertyCollector)
- )
+ activityScenario.onActivity {
+ recordingSpec.timeSeriesCapture.invoke(
+ TimeSeriesCaptureScope(recordingSpec.captureRoot, propertyCollector)
+ )
+ }
+
+ val bitmap = recordingSpec.visualCapture?.invoke(recordingSpec.captureRoot)
+ if (bitmap != null) screenshotCollector!!.add(bitmap)
}
val motionControl =
@@ -101,10 +154,13 @@ fun <T> MotionTestRule<AnimatorTestRuleToolkit>.recordMotion(
Log.i(TAG, "recordMotion() begin recording")
- val startFrameTime = currentTime
+ var startFrameTime: Long? = null
+ toolkit.currentActivityScenario().onActivity { startFrameTime = currentTime }
while (!motionControl.recordingEnded) {
- recordFrame(TimestampFrameId(currentTime - startFrameTime))
- motionControl.nextFrame()
+ var time: Long? = null
+ toolkit.currentActivityScenario().onActivity { time = currentTime }
+ recordFrame(TimestampFrameId(time!! - startFrameTime!!))
+ toolkit.currentActivityScenario().onActivity { motionControl.nextFrame() }
}
Log.i(TAG, "recordMotion() end recording")
@@ -115,7 +171,7 @@ fun <T> MotionTestRule<AnimatorTestRuleToolkit>.recordMotion(
propertyCollector.entries.map { entry -> Feature(entry.key, entry.value) },
)
- return create(timeSeries, null)
+ return create(timeSeries, screenshotCollector)
}
}
diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp
index 71105646a3d3..f0cda535b3aa 100644
--- a/tests/testables/tests/Android.bp
+++ b/tests/testables/tests/Android.bp
@@ -37,10 +37,11 @@ android_test {
"androidx.core_core-ktx",
"androidx.test.ext.junit",
"androidx.test.rules",
- "androidx.test.ext.junit",
"hamcrest-library",
"kotlinx_coroutines_test",
"mockito-target-inline-minus-junit4",
+ "platform-screenshot-diff-core",
+ "platform-test-annotations",
"testables",
"truth",
],
@@ -55,6 +56,7 @@ android_test {
"android.test.mock.stubs.system",
],
certificate: "platform",
+ test_config: "AndroidTest.xml",
test_suites: [
"device-tests",
"automotive-tests",
diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml
index 2bfb04fdb765..6cba59872710 100644
--- a/tests/testables/tests/AndroidManifest.xml
+++ b/tests/testables/tests/AndroidManifest.xml
@@ -23,6 +23,10 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
+ <activity
+ android:name="platform.test.screenshot.ScreenshotActivity"
+ android:exported="true">
+ </activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/testables/tests/AndroidTest.xml b/tests/testables/tests/AndroidTest.xml
new file mode 100644
index 000000000000..85f6e6257770
--- /dev/null
+++ b/tests/testables/tests/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+-->
+<configuration description="Runs Tests for Testables.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="TestablesTests.apk" />
+ <option name="install-arg" value="-t" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="TestableTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.testables" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="test-filter-dir" value="/data/data/com.android.testables" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/user/0/com.android.testables/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/testables/tests/goldens/recordFilmstrip_withAnimator.png b/tests/testables/tests/goldens/recordFilmstrip_withAnimator.png
new file mode 100644
index 000000000000..9aed2e970239
--- /dev/null
+++ b/tests/testables/tests/goldens/recordFilmstrip_withAnimator.png
Binary files differ
diff --git a/tests/testables/tests/goldens/recordFilmstrip_withSpring.png b/tests/testables/tests/goldens/recordFilmstrip_withSpring.png
new file mode 100644
index 000000000000..1d0c0c3c3393
--- /dev/null
+++ b/tests/testables/tests/goldens/recordFilmstrip_withSpring.png
Binary files differ
diff --git a/tests/testables/tests/goldens/recordMotion_withAnimator.json b/tests/testables/tests/goldens/recordTimeSeries_withAnimator.json
index 87fece5395e7..73eb6c74fee6 100644
--- a/tests/testables/tests/goldens/recordMotion_withAnimator.json
+++ b/tests/testables/tests/goldens/recordTimeSeries_withAnimator.json
@@ -29,7 +29,7 @@
],
"features": [
{
- "name": "value",
+ "name": "alpha",
"type": "float",
"data_points": [
1,
diff --git a/tests/testables/tests/goldens/recordMotion_withSpring.json b/tests/testables/tests/goldens/recordTimeSeries_withSpring.json
index e9fb5b4af869..2b97bad08e00 100644
--- a/tests/testables/tests/goldens/recordMotion_withSpring.json
+++ b/tests/testables/tests/goldens/recordTimeSeries_withSpring.json
@@ -21,7 +21,7 @@
],
"features": [
{
- "name": "value",
+ "name": "alpha",
"type": "float",
"data_points": [
1,
diff --git a/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt b/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt
index fbef4899bca9..993c3fed9d59 100644
--- a/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt
+++ b/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt
@@ -16,10 +16,15 @@
package android.animation
-import android.util.FloatProperty
+import android.graphics.Color
+import android.platform.test.annotations.MotionTest
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
import com.android.internal.dynamicanimation.animation.SpringAnimation
import com.android.internal.dynamicanimation.animation.SpringForce
import kotlinx.coroutines.test.TestScope
@@ -28,102 +33,169 @@ import org.junit.Test
import org.junit.runner.RunWith
import platform.test.motion.MotionTestRule
import platform.test.motion.RecordedMotion
-import platform.test.motion.golden.FeatureCapture
-import platform.test.motion.golden.asDataPoint
import platform.test.motion.testing.createGoldenPathManager
+import platform.test.motion.view.ViewFeatureCaptures
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.DisplaySpec
+import platform.test.screenshot.ScreenshotActivity
+import platform.test.screenshot.ScreenshotTestRule
@SmallTest
+@MotionTest
@RunWith(AndroidJUnit4::class)
class AnimatorTestRuleToolkitTest {
companion object {
private val GOLDEN_PATH_MANAGER =
createGoldenPathManager("frameworks/base/tests/testables/tests/goldens")
- private val TEST_PROPERTY =
- object : FloatProperty<TestState>("value") {
- override fun get(state: TestState): Float {
- return state.animatedValue
- }
-
- override fun setValue(state: TestState, value: Float) {
- state.animatedValue = value
- }
- }
+ private val EMULATION_SPEC =
+ DeviceEmulationSpec(DisplaySpec("phone", width = 320, height = 690, densityDpi = 160))
}
- @get:Rule(order = 0) val animatorTestRule = AnimatorTestRule(this)
- @get:Rule(order = 1)
+ @get:Rule(order = 0) val deviceEmulationRule = DeviceEmulationRule(EMULATION_SPEC)
+ @get:Rule(order = 1) val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java)
+ @get:Rule(order = 2) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 3) val screenshotRule = ScreenshotTestRule(GOLDEN_PATH_MANAGER)
+ @get:Rule(order = 4)
val motionRule =
- MotionTestRule(AnimatorTestRuleToolkit(animatorTestRule, TestScope()), GOLDEN_PATH_MANAGER)
+ MotionTestRule(
+ AnimatorTestRuleToolkit(animatorTestRule, TestScope()) { activityRule.scenario },
+ GOLDEN_PATH_MANAGER,
+ bitmapDiffer = screenshotRule,
+ )
@Test
- fun recordMotion_withAnimator() {
- val state = TestState()
- AnimatorSet().apply {
- duration = 500
- play(
- ValueAnimator.ofFloat(state.animatedValue, 0f).apply {
- addUpdateListener { state.animatedValue = it.animatedValue as Float }
- }
+ fun recordFilmstrip_withAnimator() {
+ val animatedBox = createScene()
+ createAnimator(animatedBox).apply { getInstrumentation().runOnMainSync { start() } }
+
+ val recordedMotion =
+ record(
+ animatedBox,
+ MotionControl { awaitFrames(count = 26) },
+ sampleIntervalMs = 20L,
+ recordScreenshots = true,
+ )
+
+ motionRule.assertThat(recordedMotion).filmstripMatchesGolden("recordFilmstrip_withAnimator")
+ }
+
+ @Test
+ fun recordTimeSeries_withAnimator() {
+ val animatedBox = createScene()
+ createAnimator(animatedBox).apply { getInstrumentation().runOnMainSync { start() } }
+
+ val recordedMotion =
+ record(
+ animatedBox,
+ MotionControl { awaitFrames(count = 26) },
+ sampleIntervalMs = 20L,
+ recordScreenshots = false,
)
+
+ motionRule
+ .assertThat(recordedMotion)
+ .timeSeriesMatchesGolden("recordTimeSeries_withAnimator")
+ }
+
+ @Test
+ fun recordFilmstrip_withSpring() {
+ val animatedBox = createScene()
+ var isDone = false
+ createSpring(animatedBox).apply {
+ addEndListener { _, _, _, _ -> isDone = true }
getInstrumentation().runOnMainSync { start() }
}
val recordedMotion =
- record(state, MotionControl { awaitFrames(count = 26) }, sampleIntervalMs = 20L)
+ record(
+ animatedBox,
+ MotionControl { awaitCondition { isDone } },
+ sampleIntervalMs = 16L,
+ recordScreenshots = true,
+ )
- motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden("recordMotion_withAnimator")
+ motionRule.assertThat(recordedMotion).filmstripMatchesGolden("recordFilmstrip_withSpring")
}
@Test
- fun recordMotion_withSpring() {
- val state = TestState()
+ fun recordTimeSeries_withSpring() {
+ val animatedBox = createScene()
var isDone = false
- SpringAnimation(state, TEST_PROPERTY).apply {
+ createSpring(animatedBox).apply {
+ addEndListener { _, _, _, _ -> isDone = true }
+ getInstrumentation().runOnMainSync { start() }
+ }
+
+ val recordedMotion =
+ record(
+ animatedBox,
+ MotionControl { awaitCondition { isDone } },
+ sampleIntervalMs = 16L,
+ recordScreenshots = false,
+ )
+
+ motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden("recordTimeSeries_withSpring")
+ }
+
+ private fun createScene(): ViewGroup {
+ lateinit var sceneRoot: ViewGroup
+ activityRule.scenario.onActivity { activity ->
+ sceneRoot = FrameLayout(activity).apply { setBackgroundColor(Color.BLACK) }
+ activity.setContentView(sceneRoot)
+ }
+ getInstrumentation().waitForIdleSync()
+ return sceneRoot
+ }
+
+ private fun createAnimator(animatedBox: ViewGroup): AnimatorSet {
+ return AnimatorSet().apply {
+ duration = 500
+ play(
+ ValueAnimator.ofFloat(animatedBox.alpha, 0f).apply {
+ addUpdateListener { animatedBox.alpha = it.animatedValue as Float }
+ }
+ )
+ }
+ }
+
+ private fun createSpring(animatedBox: ViewGroup): SpringAnimation {
+ return SpringAnimation(animatedBox, DynamicAnimation.ALPHA).apply {
spring =
SpringForce(0f).apply {
stiffness = 500f
dampingRatio = 0.95f
}
- setStartValue(1f)
+ setStartValue(animatedBox.alpha)
setMinValue(0f)
setMaxValue(1f)
minimumVisibleChange = 0.01f
-
- addEndListener { _, _, _, _ -> isDone = true }
- getInstrumentation().runOnMainSync { start() }
}
-
- val recordedMotion =
- record(state, MotionControl { awaitCondition { isDone } }, sampleIntervalMs = 16L)
-
- motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden("recordMotion_withSpring")
}
private fun record(
- state: TestState,
+ container: ViewGroup,
motionControl: MotionControl,
sampleIntervalMs: Long,
+ recordScreenshots: Boolean,
): RecordedMotion {
- var recordedMotion: RecordedMotion? = null
- getInstrumentation().runOnMainSync {
- recordedMotion =
- motionRule.recordMotion(
- AnimatorRuleRecordingSpec(
- state,
- motionControl,
- sampleIntervalMs,
- ) {
- feature(
- FeatureCapture("value") { state -> state.animatedValue.asDataPoint() },
- "value",
- )
- }
- )
- }
- return recordedMotion!!
+ val visualCapture =
+ if (recordScreenshots) {
+ ::captureView
+ } else {
+ null
+ }
+ return motionRule.recordMotion(
+ AnimatorRuleRecordingSpec(
+ container,
+ motionControl,
+ sampleIntervalMs,
+ visualCapture,
+ ) {
+ feature(ViewFeatureCaptures.alpha, "alpha")
+ }
+ )
}
-
- data class TestState(var animatedValue: Float = 1f)
}
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index e6eabd804294..56b0a25ed2dd 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -90,20 +90,6 @@ public class TestLooper {
* and call {@link #dispatchAll()}.
*/
public TestLooper(Clock clock) {
- Field messageQueueUseConcurrentField = null;
- boolean previousUseConcurrentValue = false;
- try {
- messageQueueUseConcurrentField = MessageQueue.class.getDeclaredField("sUseConcurrent");
- messageQueueUseConcurrentField.setAccessible(true);
- previousUseConcurrentValue = messageQueueUseConcurrentField.getBoolean(null);
- // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing.
- messageQueueUseConcurrentField.set(null, false);
- } catch (NoSuchFieldException e) {
- // Ignore - maybe this is not CombinedMessageQueue?
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Reflection error constructing or accessing looper", e);
- }
-
try {
mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
@@ -115,14 +101,6 @@ public class TestLooper {
}
mClock = clock;
-
- if (messageQueueUseConcurrentField != null) {
- try {
- messageQueueUseConcurrentField.set(null, previousUseConcurrentValue);
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Reflection error constructing or accessing looper", e);
- }
- }
}
public Looper getLooper() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 49665f7a3304..613b92616707 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -360,12 +360,10 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
private void verifyGetSafeModeTimeoutMs(
boolean isInTestMode,
- boolean isConfigTimeoutSupported,
PersistableBundleWrapper carrierConfig,
long expectedTimeoutMs)
throws Exception {
doReturn(isInTestMode).when(mVcnContext).isInTestMode();
- doReturn(isConfigTimeoutSupported).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
doReturn(carrierConfig).when(snapshot).getCarrierConfigForSubGrp(TEST_SUB_GRP);
@@ -377,16 +375,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
}
@Test
- public void testGetSafeModeTimeoutMs_configTimeoutUnsupported() throws Exception {
- verifyGetSafeModeTimeoutMs(
- false /* isInTestMode */,
- false /* isConfigTimeoutSupported */,
- null /* carrierConfig */,
- TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
- }
-
- @Test
- public void testGetSafeModeTimeoutMs_configTimeoutSupported() throws Exception {
+ public void testGetSafeModeTimeoutMs() throws Exception {
final int carrierConfigTimeoutSeconds = 20;
final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class);
doReturn(carrierConfigTimeoutSeconds)
@@ -395,17 +384,14 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
verifyGetSafeModeTimeoutMs(
false /* isInTestMode */,
- true /* isConfigTimeoutSupported */,
carrierConfig,
TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds));
}
@Test
- public void testGetSafeModeTimeoutMs_configTimeoutSupported_carrierConfigNull()
- throws Exception {
+ public void testGetSafeModeTimeoutMs_carrierConfigNull() throws Exception {
verifyGetSafeModeTimeoutMs(
false /* isInTestMode */,
- true /* isConfigTimeoutSupported */,
null /* carrierConfig */,
TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
}
@@ -420,7 +406,6 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
verifyGetSafeModeTimeoutMs(
true /* isInTestMode */,
- true /* isConfigTimeoutSupported */,
carrierConfig,
TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds));
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 4c7b25aaa7c3..8374fd944568 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -222,7 +222,6 @@ public class VcnGatewayConnectionTestBase {
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
- doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
doReturn(mUnderlyingNetworkController)
.when(mDeps)
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 2527dcd26382..661df4d0fe33 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -21,10 +21,13 @@
#include <format/binary/ResChunkPullParser.h>
#include <algorithm>
+#include <array>
#include <map>
#include <memory>
#include <queue>
#include <set>
+#include <span>
+#include <utility>
#include <vector>
#include "ResourceTable.h"
@@ -684,6 +687,80 @@ class ChunkPrinter {
printer_->Print("\n");
}
+ void PrintQualifiers(uint32_t qualifiers) const {
+ if (qualifiers == 0) {
+ printer_->Print("0");
+ return;
+ }
+
+ printer_->Print(StringPrintf("0x%04x: ", qualifiers));
+ static constinit std::array kValues = {
+ std::pair{ResTable_config::CONFIG_MCC, "mcc"},
+ std::pair{ResTable_config::CONFIG_MNC, "mnc"},
+ std::pair{ResTable_config::CONFIG_LOCALE, "locale"},
+ std::pair{ResTable_config::CONFIG_TOUCHSCREEN, "touchscreen"},
+ std::pair{ResTable_config::CONFIG_KEYBOARD, "keyboard"},
+ std::pair{ResTable_config::CONFIG_KEYBOARD_HIDDEN, "keyboard_hidden"},
+ std::pair{ResTable_config::CONFIG_NAVIGATION, "navigation"},
+ std::pair{ResTable_config::CONFIG_ORIENTATION, "orientation"},
+ std::pair{ResTable_config::CONFIG_DENSITY, "screen_density"},
+ std::pair{ResTable_config::CONFIG_SCREEN_SIZE, "screen_size"},
+ std::pair{ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE, "screen_smallest_size"},
+ std::pair{ResTable_config::CONFIG_VERSION, "version"},
+ std::pair{ResTable_config::CONFIG_SCREEN_LAYOUT, "screen_layout"},
+ std::pair{ResTable_config::CONFIG_UI_MODE, "ui_mode"},
+ std::pair{ResTable_config::CONFIG_LAYOUTDIR, "layout_dir"},
+ std::pair{ResTable_config::CONFIG_SCREEN_ROUND, "screen_round"},
+ std::pair{ResTable_config::CONFIG_COLOR_MODE, "color_mode"},
+ std::pair{ResTable_config::CONFIG_GRAMMATICAL_GENDER, "grammatical_gender"}};
+ const char* delimiter = "";
+ for (auto&& pair : kValues) {
+ if (qualifiers & pair.first) {
+ printer_->Print(StringPrintf("%s%s", delimiter, pair.second));
+ delimiter = "|";
+ }
+ }
+ }
+
+ bool PrintTypeSpec(const ResTable_typeSpec* chunk) const {
+ printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
+ printer_->Print(StringPrintf(" types: %u", android::util::DeviceToHost16(chunk->typesCount)));
+ printer_->Print(
+ StringPrintf(" entry configs: %u\n", android::util::DeviceToHost32(chunk->entryCount)));
+ printer_->Print("Entry qualifier masks:\n");
+ printer_->Indent();
+ std::span<const uint32_t> masks(reinterpret_cast<const uint32_t*>(GetChunkData(&chunk->header)),
+ GetChunkDataLen(&chunk->header) / sizeof(uint32_t));
+ int i = 0;
+ int non_empty_count = 0;
+ for (auto dev_mask : masks) {
+ auto mask = android::util::DeviceToHost32(dev_mask);
+ if (mask == 0) {
+ i++;
+ continue;
+ }
+ ++non_empty_count;
+ printer_->Print(StringPrintf("#0x%02x = ", i++));
+ if (mask & ResTable_typeSpec::SPEC_PUBLIC) {
+ mask &= ~ResTable_typeSpec::SPEC_PUBLIC;
+ printer_->Print("(PUBLIC) ");
+ }
+ if (mask & ResTable_typeSpec::SPEC_STAGED_API) {
+ mask &= ~ResTable_typeSpec::SPEC_STAGED_API;
+ printer_->Print("(STAGED) ");
+ }
+ PrintQualifiers(mask);
+ printer_->Print("\n");
+ }
+ if (non_empty_count > 0) {
+ printer_->Print("\n");
+ } else {
+ printer_->Print("(all empty)\n");
+ }
+ printer_->Undent();
+ return true;
+ }
+
bool PrintTableType(const ResTable_type* chunk) {
printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
printer_->Print(StringPrintf(
@@ -864,6 +941,10 @@ class ChunkPrinter {
PrintTableType(reinterpret_cast<const ResTable_type*>(chunk));
break;
+ case RES_TABLE_TYPE_SPEC_TYPE:
+ PrintTypeSpec(reinterpret_cast<const ResTable_typeSpec*>(chunk));
+ break;
+
default:
printer_->Print("\n");
break;
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index 1abe77fd3ceb..f260e2733843 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -188,7 +188,7 @@ object SystemFeaturesGenerator {
?: throw IllegalArgumentException(
"Invalid feature version input for $name: ${featureArgs[1]}"
)
- FeatureInfo(name, featureArgs[1].toInt(), readonly = true)
+ FeatureInfo(name, featureVersion, readonly = true)
}
}
}
diff --git a/wifi/wifi.aconfig b/wifi/wifi.aconfig
index 90d13e628ad6..369b80994e1b 100644
--- a/wifi/wifi.aconfig
+++ b/wifi/wifi.aconfig
@@ -38,6 +38,15 @@ flag {
}
flag {
+ name: "usd"
+ is_exported: true
+ namespace: "wifi"
+ description: "Unsynchronized Service Discovery"
+ bug: "340878198"
+ is_fixed_read_only: true
+}
+
+flag {
name: "hotspot_network_connecting_state_for_details_page"
namespace: "wifi"
description: "Update getConnectedState in HotspotNetworkEntry so that details page displays correctly."